summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt44
-rw-r--r--tests/async_queries.c435
-rw-r--r--tests/auto_increment.res114
-rw-r--r--tests/auto_increment.tst27
-rwxr-xr-xtests/big_record.pl111
-rw-r--r--tests/bug25714.c78
-rw-r--r--tests/check_async_queries.pl73
-rw-r--r--tests/code_quality/cppcheck_ignorelist.txt251
-rw-r--r--tests/code_quality/flawfinder_ignorelist.json622
-rw-r--r--tests/connect_test.c66
-rwxr-xr-xtests/consistent_snapshot.pl107
-rw-r--r--tests/deadlock_test.c239
-rwxr-xr-xtests/drop_test.pl237
-rwxr-xr-xtests/export.pl180
-rwxr-xr-xtests/fork2_test.pl257
-rwxr-xr-xtests/fork_big.pl610
-rw-r--r--tests/fork_big2.pl754
-rw-r--r--tests/function.res258
-rw-r--r--tests/function.tst80
-rw-r--r--tests/grant.res616
-rwxr-xr-xtests/index_corrupt.pl229
-rwxr-xr-xtests/insert_and_repair.pl197
-rw-r--r--tests/insert_test.c60
-rw-r--r--tests/list_test.c71
-rwxr-xr-xtests/lock_test.pl110
-rw-r--r--tests/lock_test.res25
-rwxr-xr-xtests/mail_to_db.pl624
-rw-r--r--tests/myisam-big-rows.tst72
-rw-r--r--tests/mysql_client_fw.c1509
-rw-r--r--tests/mysql_client_test.c22495
-rw-r--r--tests/nonblock-wrappers.h479
-rwxr-xr-xtests/pmail.pl339
-rwxr-xr-xtests/rename_test.pl221
-rwxr-xr-xtests/restore-lock.smack63
-rw-r--r--tests/select_test.c74
-rw-r--r--tests/showdb_test.c67
-rw-r--r--tests/ssl_test.c83
-rwxr-xr-xtests/table_types.pl232
-rwxr-xr-xtests/test_delayed_insert.pl381
-rw-r--r--tests/thread_test.c248
-rwxr-xr-xtests/truncate.pl142
41 files changed, 32880 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 00000000..8d4d7759
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
+
+ADD_DEFINITIONS("-DMYSQL_CLIENT")
+
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_SOURCE_DIR}/client)
+INCLUDE_DIRECTORIES(BEFORE
+ ${CMAKE_BINARY_DIR}/libmariadb/include
+ ${CMAKE_SOURCE_DIR}/libmariadb/include)
+
+MYSQL_ADD_EXECUTABLE(mariadb-client-test mysql_client_test.c COMPONENT Test)
+SET(CLIENT_LIB mariadbclient mysys)
+
+TARGET_LINK_LIBRARIES(mariadb-client-test ${CLIENT_LIB})
+ADD_DEPENDENCIES(mariadb-client-test GenError ${CLIENT_LIB})
+
+IF(WITH_UNIT_TESTS)
+ ADD_EXECUTABLE(bug25714 bug25714.c)
+ TARGET_LINK_LIBRARIES(bug25714 ${CLIENT_LIB})
+ ADD_DEPENDENCIES(bug25714 GenError ${CLIENT_LIB})
+ENDIF()
+
+CHECK_INCLUDE_FILE(event.h HAVE_EVENT_H)
+FIND_LIBRARY(EVENT_LIBRARY event)
+MARK_AS_ADVANCED(EVENT_LIBRARY)
+IF(HAVE_EVENT_H AND EVENT_LIBRARY)
+ ADD_EXECUTABLE(async_queries async_queries.c)
+ TARGET_LINK_LIBRARIES(async_queries ${CLIENT_LIB} ${EVENT_LIBRARY})
+ ADD_DEPENDENCIES(async_queries GenError ${CLIENT_LIB})
+ENDIF()
diff --git a/tests/async_queries.c b/tests/async_queries.c
new file mode 100644
index 00000000..8509b414
--- /dev/null
+++ b/tests/async_queries.c
@@ -0,0 +1,435 @@
+/*
+ Copyright 2011 Kristian Nielsen and Monty Program Ab.
+
+ This file is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ Run a set of queries in parallel against a server using the non-blocking
+ API, and compare to running same queries with the normal blocking API.
+*/
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <mysql.h>
+#include <my_getopt.h>
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <event.h>
+
+
+#define SL(s) (s), sizeof(s)
+static const char *my_groups[]= { "client", NULL };
+
+/* Maintaining a list of queries to run. */
+struct query_entry {
+ struct query_entry *next;
+ char *query;
+ int index;
+};
+static struct query_entry *query_list;
+static struct query_entry **tail_ptr= &query_list;
+static int query_counter= 0;
+
+
+/* State kept for each connection. */
+struct state_data {
+ int ST; /* State machine current state */
+ struct event ev_mysql;
+ MYSQL mysql;
+ MYSQL_RES *result;
+ MYSQL *ret;
+ int err;
+ MYSQL_ROW row;
+ struct query_entry *query_element;
+ int index;
+};
+
+
+static const char *opt_db= NULL;
+static const char *opt_user= NULL;
+static const char *opt_password= NULL;
+static int tty_password= 0;
+static const char *opt_host= NULL;
+static const char *opt_socket= NULL;
+static unsigned int opt_port= 0;
+static unsigned int opt_connections= 5;
+static const char *opt_query_file= NULL;
+
+static struct my_option options[] =
+{
+ {"database", 'D', "Database to use", &opt_db, &opt_db,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
+ 0, 0, 0, 0, 0},
+ {"host", 'h', "Connect to host", &opt_host, &opt_host,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"password", 'p',
+ "Password to use when connecting to server. If password is not given it's asked from the tty.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"port", 'P', "Port number to use for connection.",
+ &opt_port, &opt_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"socket", 'S', "Socket file to use for connection",
+ &opt_socket, &opt_socket, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"user", 'u', "User for login if not current user", &opt_user,
+ &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"connections", 'n', "Number of simultaneous connections/queries.",
+ &opt_connections, &opt_connections, 0, GET_UINT, REQUIRED_ARG,
+ 5, 0, 0, 0, 0, 0},
+ {"queryfile", 'q', "Name of file containing extra queries to run",
+ &opt_query_file, &opt_query_file, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static void
+fatal(struct state_data *sd, const char *msg)
+{
+ fprintf(stderr, "%s: %s\n", msg, (sd ? mysql_error(&sd->mysql) : ""));
+ exit(1);
+}
+
+
+static void state_machine_handler(int fd, short event, void *arg);
+
+static void
+next_event(int new_st, int status, struct state_data *sd)
+{
+ short wait_event= 0;
+ struct timeval tv, *ptv;
+ int fd;
+
+ if (status & MYSQL_WAIT_READ)
+ wait_event|= EV_READ;
+ if (status & MYSQL_WAIT_WRITE)
+ wait_event|= EV_WRITE;
+ if (wait_event)
+ fd= mysql_get_socket(&sd->mysql);
+ else
+ fd= -1;
+ if (status & MYSQL_WAIT_TIMEOUT)
+ {
+ tv.tv_sec= mysql_get_timeout_value(&sd->mysql);
+ tv.tv_usec= 0;
+ ptv= &tv;
+ }
+ else
+ ptv= NULL;
+ event_set(&sd->ev_mysql, fd, wait_event, state_machine_handler, sd);
+ event_add(&sd->ev_mysql, ptv);
+ sd->ST= new_st;
+}
+
+static int
+mysql_status(short event)
+{
+ int status= 0;
+ if (event & EV_READ)
+ status|= MYSQL_WAIT_READ;
+ if (event & EV_WRITE)
+ status|= MYSQL_WAIT_WRITE;
+ if (event & EV_TIMEOUT)
+ status|= MYSQL_WAIT_TIMEOUT;
+ return status;
+}
+
+
+static int num_active_connections;
+
+/* Shortcut for going to new state immediately without waiting. */
+#define NEXT_IMMEDIATE(sd_, new_st) do { sd_->ST= new_st; goto again; } while (0)
+
+static void
+state_machine_handler(int fd __attribute__((unused)), short event, void *arg)
+{
+ struct state_data *sd= arg;
+ int status;
+
+again:
+ switch(sd->ST)
+ {
+ case 0:
+ /* Initial state, start making the connection. */
+ status= mysql_real_connect_start(&sd->ret, &sd->mysql, opt_host, opt_user, opt_password, opt_db, opt_port, opt_socket, 0);
+ if (status)
+ /* Wait for connect to complete. */
+ next_event(1, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 9);
+ break;
+
+ case 1:
+ status= mysql_real_connect_cont(&sd->ret, &sd->mysql, mysql_status(event));
+ if (status)
+ next_event(1, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 9);
+ break;
+
+ case 9:
+ if (!sd->ret)
+ fatal(sd, "Failed to mysql_real_connect()");
+ NEXT_IMMEDIATE(sd, 10);
+ break;
+
+ case 10:
+ /* Now run the next query. */
+ sd->query_element= query_list;
+ if (!sd->query_element)
+ {
+ /* No more queries, end the connection. */
+ NEXT_IMMEDIATE(sd, 40);
+ }
+ query_list= query_list->next;
+
+ sd->index= sd->query_element->index;
+ printf("%d ! %s\n", sd->index, sd->query_element->query);
+ status= mysql_real_query_start(&sd->err, &sd->mysql, sd->query_element->query,
+ strlen(sd->query_element->query));
+ if (status)
+ next_event(11, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 20);
+ break;
+
+ case 11:
+ status= mysql_real_query_cont(&sd->err, &sd->mysql, mysql_status(event));
+ if (status)
+ next_event(11, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 20);
+ break;
+
+ case 20:
+ my_free(sd->query_element->query);
+ my_free(sd->query_element);
+ if (sd->err)
+ {
+ printf("%d | Error: %s\n", sd->index, mysql_error(&sd->mysql));
+ NEXT_IMMEDIATE(sd, 10);
+ }
+ else
+ {
+ sd->result= mysql_use_result(&sd->mysql);
+ if (!sd->result)
+ fatal(sd, "mysql_use_result() returns error");
+ NEXT_IMMEDIATE(sd, 30);
+ }
+ break;
+
+ case 30:
+ status= mysql_fetch_row_start(&sd->row, sd->result);
+ if (status)
+ next_event(31, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 39);
+ break;
+
+ case 31:
+ status= mysql_fetch_row_cont(&sd->row, sd->result, mysql_status(event));
+ if (status)
+ next_event(31, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 39);
+ break;
+
+ case 39:
+ if (sd->row)
+ {
+ /* Got a row. */
+ unsigned int i;
+ printf("%d - ", sd->index);
+ for (i= 0; i < mysql_num_fields(sd->result); i++)
+ printf("%s%s", (i ? "\t" : ""), (sd->row[i] ? sd->row[i] : "(null)"));
+ printf ("\n");
+ NEXT_IMMEDIATE(sd, 30);
+ }
+ else
+ {
+ if (mysql_errno(&sd->mysql))
+ {
+ /* An error occurred. */
+ printf("%d | Error: %s\n", sd->index, mysql_error(&sd->mysql));
+ }
+ else
+ {
+ /* EOF. */
+ printf("%d | EOF\n", sd->index);
+ }
+ mysql_free_result(sd->result);
+ NEXT_IMMEDIATE(sd, 10);
+ }
+ break;
+
+ case 40:
+ status= mysql_close_start(&sd->mysql);
+ if (status)
+ next_event(41, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 50);
+ break;
+
+ case 41:
+ status= mysql_close_cont(&sd->mysql, mysql_status(event));
+ if (status)
+ next_event(41, status, sd);
+ else
+ NEXT_IMMEDIATE(sd, 50);
+ break;
+
+ case 50:
+ /* We are done! */
+ num_active_connections--;
+ if (num_active_connections == 0)
+ event_loopbreak();
+ break;
+
+ default:
+ abort();
+ }
+}
+
+
+void
+add_query(const char *q)
+{
+ struct query_entry *e;
+ char *q2;
+ size_t len;
+
+ e= my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*e), MYF(0));
+ q2= my_strdup(PSI_NOT_INSTRUMENTED, q, MYF(0));
+ if (!e || !q2)
+ fatal(NULL, "Out of memory");
+
+ /* Remove any trailing newline. */
+ len= strlen(q2);
+ if (q2[len] == '\n')
+ q2[len--]= '\0';
+ if (q2[len] == '\r')
+ q2[len--]= '\0';
+
+ e->next= NULL;
+ e->query= q2;
+ e->index= query_counter++;
+ *tail_ptr= e;
+ tail_ptr= &e->next;
+}
+
+
+static my_bool
+handle_option(const struct my_option *opt, const char *arg,
+ const char *filename __attribute__((unused)))
+{
+ switch (opt->id)
+ {
+ case '?':
+ printf("Usage: async_queries [OPTIONS] query ...\n");
+ my_print_help(options);
+ my_print_variables(options);
+ exit(0);
+ break;
+
+ case 'p':
+ if (arg)
+ opt_password= arg;
+ else
+ tty_password= 1;
+ break;
+ }
+
+ return 0;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ struct state_data *sds;
+ unsigned int i;
+ int err;
+ struct event_base *libevent_base;
+
+ err= handle_options(&argc, &argv, options, handle_option);
+ if (err)
+ exit(err);
+ if (tty_password)
+ opt_password= my_get_tty_password(NullS);
+
+ if (opt_query_file)
+ {
+ FILE *f= fopen(opt_query_file, "r");
+ char buf[65536];
+ if (!f)
+ fatal(NULL, "Cannot open query file");
+ while (!feof(f))
+ {
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+ add_query(buf);
+ }
+ fclose(f);
+ }
+ /* Add extra queries directly on command line. */
+ while (argc > 0)
+ {
+ --argc;
+ add_query(*argv++);
+ }
+
+ sds= my_malloc(PSI_NOT_INSTRUMENTED, opt_connections * sizeof(*sds), MYF(0));
+ if (!sds)
+ fatal(NULL, "Out of memory");
+
+ libevent_base= event_init();
+
+ err= mysql_library_init(argc, argv, (char **)my_groups);
+ if (err)
+ {
+ fprintf(stderr, "Fatal: mysql_library_init() returns error: %d\n", err);
+ exit(1);
+ }
+
+ num_active_connections= 0;
+ for (i= 0; i < opt_connections; i++)
+ {
+ mysql_init(&sds[i].mysql);
+ mysql_options(&sds[i].mysql, MYSQL_OPT_NONBLOCK, 0);
+ mysql_options(&sds[i].mysql, MYSQL_READ_DEFAULT_GROUP, "async_queries");
+
+ /*
+ We put the initial connect call in the first state 0 of the state machine
+ and run that manually, just to have everything in one place.
+ */
+ sds[i].ST= 0;
+ num_active_connections++;
+ state_machine_handler(-1, -1, &sds[i]);
+ }
+
+ event_dispatch();
+
+ my_free(sds);
+
+ mysql_library_end();
+
+ event_base_free(libevent_base);
+
+ return 0;
+}
diff --git a/tests/auto_increment.res b/tests/auto_increment.res
new file mode 100644
index 00000000..fa7b5428
--- /dev/null
+++ b/tests/auto_increment.res
@@ -0,0 +1,114 @@
+--------------
+drop table if exists auto_incr_test,auto_incr_test2
+--------------
+
+Query OK, 0 rows affected
+
+--------------
+create table auto_incr_test (id int not null auto_increment, name char(40), timestamp timestamp, primary key (id))
+--------------
+
+Query OK, 0 rows affected
+
+--------------
+insert into auto_incr_test (name) values ("first record")
+--------------
+
+Query OK, 1 row affected
+
+--------------
+insert into auto_incr_test values (last_insert_id()+1,"second record",null)
+--------------
+
+Query OK, 1 row affected
+
+--------------
+insert into auto_incr_test (id,name) values (10,"tenth record")
+--------------
+
+Query OK, 1 row affected
+
+--------------
+insert into auto_incr_test values (0,"eleventh record",null)
+--------------
+
+Query OK, 1 row affected
+
+--------------
+insert into auto_incr_test values (last_insert_id()+1,"12","1997-01-01")
+--------------
+
+Query OK, 1 row affected
+
+--------------
+insert into auto_incr_test values (12,"this will not work",NULL)
+--------------
+
+ERROR 1062 at line 15: Duplicate entry '12' for key 1
+--------------
+replace into auto_incr_test values (12,"twelfth record",NULL)
+--------------
+
+Query OK, 2 rows affected
+
+--------------
+select * from auto_incr_test
+--------------
+
+id name timestamp
+1 first record 19980817042654
+2 second record 19980817042655
+10 tenth record 19980817042655
+11 eleventh record 19980817042655
+12 twelfth record 19980817042655
+5 rows in set
+
+--------------
+create table auto_incr_test2 (id int not null auto_increment, name char(40), primary key (id))
+--------------
+
+Query OK, 0 rows affected
+
+--------------
+insert into auto_incr_test2 select NULL,name from auto_incr_test
+--------------
+
+Query OK, 5 rows affected
+Records: 5 Duplicates: 0 Warnings: 0
+
+--------------
+insert into auto_incr_test2 select id,name from auto_incr_test
+--------------
+
+Query OK, 3 rows affected
+Records: 5 Duplicates: 2 Warnings: 0
+
+--------------
+replace into auto_incr_test2 select id,name from auto_incr_test
+--------------
+
+Query OK, 5 rows affected
+Records: 5 Duplicates: 5 Warnings: 0
+
+--------------
+select * from auto_incr_test2
+--------------
+
+id name
+1 first record
+2 second record
+3 tenth record
+4 eleventh record
+5 twelfth record
+10 tenth record
+11 eleventh record
+12 twelfth record
+8 rows in set
+
+--------------
+drop table auto_incr_test,auto_incr_test2
+--------------
+
+Query OK, 0 rows affected
+
+Bye
diff --git a/tests/auto_increment.tst b/tests/auto_increment.tst
new file mode 100644
index 00000000..a11a05c8
--- /dev/null
+++ b/tests/auto_increment.tst
@@ -0,0 +1,27 @@
+#
+# Test of auto_increment
+#
+# run this program with mysql -vvf test < this file
+
+drop table if exists auto_incr_test,auto_incr_test2 ;
+
+create table auto_incr_test (id int not null auto_increment, name char(40), timestamp timestamp, primary key (id)) ;
+
+insert into auto_incr_test (name) values ("first record");
+insert into auto_incr_test values (last_insert_id()+1,"second record",null);
+insert into auto_incr_test (id,name) values (10,"tenth record");
+insert into auto_incr_test values (0,"eleventh record",null);
+insert into auto_incr_test values (last_insert_id()+1,"12","1997-01-01");
+insert into auto_incr_test values (12,"this will not work",NULL);
+replace into auto_incr_test values (12,"twelfth record",NULL);
+
+select * from auto_incr_test ;
+
+create table auto_incr_test2 (id int not null auto_increment, name char(40), primary key (id)) ;
+insert into auto_incr_test2 select NULL,name from auto_incr_test;
+insert into auto_incr_test2 select id,name from auto_incr_test;
+replace into auto_incr_test2 select id,name from auto_incr_test;
+
+select * from auto_incr_test2 ;
+
+drop table auto_incr_test,auto_incr_test2;
diff --git a/tests/big_record.pl b/tests/big_record.pl
new file mode 100755
index 00000000..639e305b
--- /dev/null
+++ b/tests/big_record.pl
@@ -0,0 +1,111 @@
+#!/usr/bin/env perl
+
+# Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
+
+# This is a test with stores big records in a blob.
+# Note that for the default test the mysql server should have been
+# started with at least 'mysqld --max_allowed_packet=30M' and you should have
+# at least 256M memory in your computer.
+
+use DBI;
+use Getopt::Long;
+
+$opt_host="";
+$opt_user=$opt_password="";
+$opt_db="test";
+$opt_rows=20; # Test of blobs up to ($rows-1)*100000+1 bytes
+$opt_compress=0;
+$opt_table="test_big_record";
+$opt_loop_count=100000; # Change this to make test harder/easier
+
+GetOptions("host=s","db=s","user=s", "password=s", "table=s", "rows=i",
+ "compress", "loop-count=i") || die "Aborted";
+
+print "Connection to database $test_db\n";
+
+$extra_options="";
+$extra_options.=":mariadb_compression=1" if ($opt_compress);
+
+$dbh = DBI->connect("DBI:MariaDB:$opt_db:$host$extra_options",$opt_user,$opt_password) || die "Can't connect: $DBI::errstr\n";
+
+$dbh->do("drop table if exists $opt_table");
+
+print "Creating table $opt_table\n";
+
+($dbh->do("\
+CREATE TABLE $opt_table (
+ auto int(5) unsigned NOT NULL DEFAULT '0' auto_increment,
+ test longblob,
+ PRIMARY KEY (auto))")) or die $DBI::errstr;
+
+print "Inserting $opt_rows records\n";
+
+$|=1; # Flush output to stdout to be able to monitor process
+
+for ($i=0 ; $i < $opt_rows ; $i++)
+{
+ $tmp= chr(65+($i % 16)) x ($i*100000+1);
+ $tmp= $dbh->quote($tmp);
+ $dbh->do("insert into $opt_table (test) values ($tmp)") or die $DBI::errstr;
+ print ".";
+}
+
+print "\nReading records\n";
+
+$sth=$dbh->prepare("select * from $opt_table", { "mariadb_use_result" => 1}) or die $dbh->errstr;
+
+$sth->execute() or die $sth->errstr;
+
+$i=0;
+while (($row = $sth->fetchrow_arrayref))
+{
+ die "Record $i had wrong data in blob" if ($row->[1] ne (chr(65+($i % 16)) x ($i*100000+1)));
+ $i++;
+}
+
+die "Didn't get all rows from server" if ($i != $opt_rows);
+
+#
+# Test by insert/updating/deleting random rows for a while
+#
+
+print "Testing insert/update/delete\n";
+
+$max_row_id= $rows;
+for ($i= 0 ; $i < $opt_loop_count ; $i++)
+{
+ $length= int(rand 65535);
+ $tmp= chr(65+($i % 16)) x $length;
+ $tmp= $dbh->quote($tmp);
+ $dbh->do("insert into $opt_table (test) values ($tmp)") or die $DBI::errstr;
+ $max_row_id++;
+ $length=int(rand 65535);
+ $tmp= chr(65+($i % 16)) x $length;
+ $tmp= $dbh->quote($tmp);
+ $id= int(rand $max_row_id);
+ $dbh->do("update $opt_table set test= $tmp where auto= $id") or die $DBI::errstr;
+ if (($i % 2) == 1)
+ {
+ $id= int(rand $max_row_id);
+ $dbh->do("delete from $opt_table where auto= $id") or die $DBI::errstr;
+ }
+ print "." if ($i % ($opt_loop_count/100) == 1);
+}
+
+# $dbh->do("drop table $opt_table") or die $DBI::errstr;
+
+print "\nTest ok\n";
+exit 0;
diff --git a/tests/bug25714.c b/tests/bug25714.c
new file mode 100644
index 00000000..35a13518
--- /dev/null
+++ b/tests/bug25714.c
@@ -0,0 +1,78 @@
+/* Copyright (c) 2007 MySQL AB, 2009 Sun Microsystems, Inc.
+ Use is subject to license terms.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <mysql.h>
+#include <m_string.h>
+#include <assert.h>
+
+int main (int argc, char **argv)
+{
+ MYSQL conn;
+ int OK __attribute__((unused));
+
+ const char* query4= "INSERT INTO federated.t1 SET Value=54";
+ const char* query5= "INSERT INTO federated.t1 SET Value=55";
+
+ MY_INIT(argv[0]);
+
+ if (argc != 2 || !strcmp(argv[1], "--help"))
+ {
+ fprintf(stderr, "This program is a part of the MySQL test suite. "
+ "It is not intended to be executed directly by a user.\n");
+ return -1;
+ }
+
+ mysql_init(&conn);
+ if (!mysql_real_connect(
+ &conn,
+ "127.0.0.1",
+ "root",
+ "",
+ "test",
+ atoi(argv[1]),
+ NULL,
+ CLIENT_FOUND_ROWS))
+ {
+ fprintf(stderr, "Failed to connect to database: Error: %s\n",
+ mysql_error(&conn));
+ return 1;
+ } else {
+ printf("%s\n", mysql_error(&conn));
+ }
+
+ OK = mysql_real_query (&conn, query4, (uint) strlen(query4));
+
+ assert(0 == OK);
+
+ printf("%ld inserted\n",
+ (long) mysql_insert_id(&conn));
+
+ OK = mysql_real_query (&conn, query5, (uint) strlen(query5));
+
+ assert(0 == OK);
+
+ printf("%ld inserted\n",
+ (long) mysql_insert_id(&conn));
+
+ mysql_close(&conn);
+ mysql_server_end();
+ my_end(0);
+
+ return 0;
+}
+
diff --git a/tests/check_async_queries.pl b/tests/check_async_queries.pl
new file mode 100644
index 00000000..0039dd90
--- /dev/null
+++ b/tests/check_async_queries.pl
@@ -0,0 +1,73 @@
+#! /usr/bin/perl
+
+# Read the output of async_queries.c. Run the queries again serially, using
+# the normal (not asynchronous) API. Compare the two results for correctness.
+
+use strict;
+use warnings;
+
+use DBI;
+
+my $D= [];
+
+die "Usage: $0 <host> <user> <password> <database>\n"
+ unless @ARGV == 4;
+
+my $dbh= DBI->connect("DBI:MariaDB:database=$ARGV[3];host=$ARGV[0]",
+ $ARGV[1], $ARGV[2],
+ { RaiseError => 1, PrintError => 0 });
+
+while (<STDIN>) {
+ chomp;
+ if (/^([0-9]+) ! (.*);$/) {
+ my ($index, $query)= ($1, $2);
+ $D->[$index]= { QUERY => $query, OUTPUT => [] };
+ } elsif (/^([0-9]+) - (.*)$/) {
+ my ($index, $data)= ($1, $2);
+ push @{$D->[$index]{OUTPUT}}, $data;
+ } elsif (/^([0-9]+) \| Error: (.*)$/) {
+ my ($index, $errmsg)= ($1, $2);
+ my $rows;
+ my $res= eval {
+ my $stm= $dbh->prepare($D->[$index]{QUERY});
+ $stm->execute();
+ $rows= $stm->fetchall_arrayref();
+ 1;
+ };
+ if ($res) {
+ die "Query $index succeeded, but should have failed with error.\nquery=$D->[$index]{QUERY}\nerror=$errmsg\n";
+ }
+ my $errmsg2= $@;
+ if ($errmsg2 =~ /^DBD::.*failed: (.*) at .*$/s) {
+ $errmsg2= $1;
+ } else {
+ die "Unexpected DBD error message format: '$errmsg2'\n";
+ }
+ if ($errmsg2 ne $errmsg) {
+ die "Query $index failed with different error message\nquery=$D->[$index]{QUERY}\nerror1=$errmsg\nerror2=$errmsg2\n";
+ }
+ print "OK $index\n";
+ delete $D->[$index];
+ } elsif (/^([0-9]+) \| EOF$/) {
+ my $index= $1;
+ my $rows;
+ my $res= eval {
+ my $stm= $dbh->prepare($D->[$index]{QUERY});
+ $stm->execute();
+ $rows= $stm->fetchall_arrayref();
+ 1;
+ };
+ if (!$res) {
+ die "Query $index failed, but should have succeeded.\nquery=$D->[$index]{QUERY}\nerror=$@\n";
+ }
+ my $result_string= join("\n", sort @{$D->[$index]{OUTPUT}});
+ my $result_string2= join("\n", sort(map(join("\t", map((defined($_) ? $_ : "(null)"), @$_)), @$rows)));
+ if ($result_string ne $result_string2) {
+ die "Query $index result difference.\nquery=$D->[$index]{QUERY}\noutput1=\n$$result_string\noutput2=\n$result_string2\n";
+ }
+ delete $D->[$index];
+ } else {
+ die "Unexpected line: '$_'\n";
+ }
+}
+$dbh->disconnect();
diff --git a/tests/code_quality/cppcheck_ignorelist.txt b/tests/code_quality/cppcheck_ignorelist.txt
new file mode 100644
index 00000000..268bf810
--- /dev/null
+++ b/tests/code_quality/cppcheck_ignorelist.txt
@@ -0,0 +1,251 @@
+client/mysql.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+client/mysql_upgrade.c: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+client/mysqladmin.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+client/mysqlbinlog.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+client/mysqlcheck.c: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+client/mysqlimport.c: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+client/mysqlshow.c: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+client/mysqltest.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+dbug/tests.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+lexyy.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+mysql-test/lib/My/SafeProcess/safe_process_win.cc: error: Uninitialized variable: message_text
+mysys/mf_keycache.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+mysys/my_delete.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+mysys/my_fopen.c: error: Return value of allocation function 'freopen' is not stored.
+mysys/my_getsystime.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+mysys/my_pread.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+mysys/my_rename.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+mysys/my_winfile.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+mysys/my_write.c: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+mysys/thr_lock.c: error: There is an unknown macro here somewhere. Configuration is required. If MYSQL_TABLE_WAIT_VARIABLES is a macro then please configure it.
+mysys/tree.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+pcre/pcrecpp.cc: warning: Uninitialized variable: kmat
+pcre/pcrecpp.h: error: syntax error
+pcre/pcregrep.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/audit_null/audit_null.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_ed25519/server_ed25519.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_examples/auth_0x0100.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_examples/dialog_examples.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_examples/qa_auth_interface.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_examples/qa_auth_server.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_examples/test_plugin.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_gssapi/server_plugin.cc: error: syntax error
+plugin/auth_gssapi/sspi.h: error: #include nested too deeply
+plugin/auth_pam/auth_pam.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_pam/auth_pam_v1.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_pipe/auth_pipe.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/auth_socket/auth_socket.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/aws_key_management/aws_key_management_plugin.cc: error: syntax error
+plugin/cracklib_password_check/cracklib_password_check.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/daemon_example/daemon_example.cc: error: syntax error
+plugin/debug_key_management/debug_key_management_plugin.cc: error: syntax error
+plugin/disks/information_schema_disks.cc: error: syntax error
+plugin/example_key_management/example_key_management_plugin.cc: error: syntax error
+plugin/feedback/feedback.cc: error: syntax error
+plugin/file_key_management/file_key_management_plugin.cc: error: syntax error
+plugin/fulltext/plugin_example.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/handler_socket/handlersocket/handlersocket.cpp: error: syntax error
+plugin/locale_info/locale_info.cc: error: syntax error
+plugin/metadata_lock_info/metadata_lock_info.cc: error: syntax error
+plugin/metadata_lock_info/metadata_lock_info.cc: error: syntax error
+plugin/qc_info/qc_info.cc: error: syntax error
+plugin/query_response_time/plugin.cc: error: syntax error
+plugin/query_response_time/query_response_time.cc: error: Array 'm_count[41]' accessed at index 43, which is out of bounds.
+plugin/query_response_time/query_response_time.cc: error: Array 'm_total[41]' accessed at index 43, which is out of bounds.
+plugin/server_audit/server_audit.c: error: Uninitialized variable: &tm_time
+plugin/server_audit/server_audit.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/server_audit/server_audit.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/server_audit/server_audit.c: error: Uninitialized variable: &tm_time
+plugin/simple_password_check/simple_password_check.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/sql_errlog/sql_errlog.c: error: Found a exit path from function with non-void return type that has missing return statement
+plugin/sql_errlog/sql_errlog.c: error: Uninitialized variable: &t
+plugin/user_variables/user_variables.cc: error: syntax error
+plugin/userstat/userstat.cc: error: syntax error
+plugin/versioning/versioning.cc: error: syntax error
+plugin/wsrep_info/plugin.cc: error: syntax error
+sql-common/client.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+sql-common/client_plugin.c: error: va_list 'unused' used before va_start() was called.
+sql-common/client_plugin.c: error: va_list 'unused' used before va_start() was called.
+sql-common/client_plugin.c: error: va_list 'unused' used before va_start() was called.
+sql-common/client_plugin.c: error: va_list 'unused' used before va_start() was called.
+sql/debug_sync.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE is a macro then please configure it.
+sql/gcalc_slicescan.cc: warning: Possible null pointer dereference: first_bottom_point
+sql/gen_lex_hash.cc: error: Common realloc mistake: 'hash_map' nulled but not freed upon failure
+sql/handler.h: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+sql/log.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+sql/log_event.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+sql/log_event_old.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+sql/net_serv.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+sql/protocol.h: error: syntax error
+sql/rpl_utility.h: error: There is an unknown macro here somewhere. Configuration is required. If CPP_UNNAMED_NS_START is a macro then please configure it.
+sql/semisync_slave.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+sql/sql_select.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+sql/sql_string.cc: warning: Iterators to containers from different expressions 'to' and 'from' are used together.
+sql/table.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+sql/winservice.c: error: Resource leak: mysql_upgrade_info
+sql/wsrep_thd.h: error: failed to expand 'wsrep_create_appliers', Wrong number of parameters for macro 'wsrep_create_appliers'.
+storage/archive/azio.c: error: Syntax Error: AST broken, 'if' doesn't have two operands.
+storage/archive/ha_archive.cc: error: syntax error
+storage/blackhole/ha_blackhole.cc: error: syntax error
+storage/cassandra/gen-cpp/Cassandra_server.skeleton.cpp: error: Found a exit path from function with non-void return type that has missing return statement
+storage/cassandra/ha_cassandra.cc: error: syntax error
+storage/connect/connect.cc: error: Uninitialized variable: lg
+storage/connect/domdoc.cpp: error: syntax error
+storage/connect/ha_connect.cc: error: syntax error
+storage/connect/myconn.cpp: error: Unmatched '{'. Configuration: 'ALPHA;MYSQL_PREPARED_STATEMENTS'.
+storage/connect/myconn.cpp: error: Unmatched '{'. Configuration: 'MYSQL_PREPARED_STATEMENTS'.
+storage/connect/odbconn.cpp: warning: Uninitialized variable: b
+storage/connect/odbconn.cpp: warning: Uninitialized variable: b
+storage/connect/odbconn.cpp: warning: Uninitialized variable: b
+storage/connect/plugutil.cpp: error: Width 255 given in format string (no. 2) is larger than destination buffer 'stmsg[200]', use %199[^\"] to prevent overflowing it.
+storage/connect/plugutil.cpp: error: Width 255 given in format string (no. 1) is larger than destination buffer 'stmsg[200]', use %199[^\"] to prevent overflowing it.
+storage/connect/tabjson.cpp: warning: Possible null pointer dereference: Val
+storage/connect/tabmul.cpp: error: Uninitialized variable: buf
+storage/connect/tabmul.cpp: error: Uninitialized variable: buf
+storage/connect/tabmul.cpp: error: Uninitialized variable: buf
+storage/connect/taboccur.cpp: warning: Uninitialized variable: *pcrp
+storage/connect/unzip.c: warning: Uninitialized variable: *pzlib_filefunc64_32_def.zopen32_file
+storage/connect/value.cpp: error: Signed integer overflow for expression 'n*126230400'.
+storage/connect/zip.c: warning: Uninitialized variable: *pzlib_filefunc64_32_def.zopen32_file
+storage/csv/ha_tina.cc: error: syntax error
+storage/example/ha_example.cc: error: syntax error
+storage/federated/ha_federated.cc: error: syntax error
+storage/heap/ha_heap.cc: error: syntax error
+storage/innobase/btr/btr0btr.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/btr/btr0cur.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/btr/btr0defragment.cc: error: There is an unknown macro here somewhere. Configuration is required. If DECLARE_THREAD is a macro then please configure it.
+storage/innobase/btr/btr0sea.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/buf/buf0buf.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/buf/buf0dump.cc: error: There is an unknown macro here somewhere. Configuration is required. If DECLARE_THREAD is a macro then please configure it.
+storage/innobase/buf/buf0flu.cc: error: There is an unknown macro here somewhere. Configuration is required. If DECLARE_THREAD is a macro then please configure it.
+storage/innobase/buf/buf0lru.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/dict/dict0crea.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/dict/dict0dict.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/dict/dict0load.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/dict/dict0stats.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/dict/dict0stats_bg.cc: error: There is an unknown macro here somewhere. Configuration is required. If DECLARE_THREAD is a macro then please configure it.
+storage/innobase/fil/fil0crypt.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/fil/fil0fil.cc: error: syntax error
+storage/innobase/fsp/fsp0file.cc: error: Resource leak: file
+storage/innobase/fsp/fsp0fsp.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/fts/fts0fts.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/fts/fts0opt.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/fts/fts0que.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/gis/gis0rtree.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/gis/gis0sea.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/handler/ha_innodb.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/handler/handler0alter.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/handler/i_s.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/ibuf/ibuf0ibuf.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/ibuf/ibuf0ibuf.cc: error: failed to expand 'ibuf_bitmap_page_get_bits', Wrong number of parameters for macro 'ibuf_bitmap_page_get_bits'.
+storage/innobase/lock/lock0lock.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/lock/lock0wait.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/lock/lock0wait.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/log/log0log.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/log/log0recv.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/os/os0file.cc: error: syntax error
+storage/innobase/page/page0page.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/page/page0zip.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/pars/pars0pars.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/row/row0ftsort.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/row/row0import.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/row/row0ins.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/row/row0log.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/row/row0merge.cc: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/innobase/row/row0mysql.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/row/row0quiesce.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/row/row0sel.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/row/row0umod.cc: error: There is an unknown macro here somewhere. Configuration is required. If ut_d is a macro then please configure it.
+storage/innobase/row/row0upd.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/row/row0vers.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/srv/srv0conc.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ALIGNED is a macro then please configure it.
+storage/innobase/srv/srv0srv.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/srv/srv0start.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ATTRIBUTE is a macro then please configure it.
+storage/innobase/trx/trx0i_s.cc: error: Array 'table_cache->chunks[39]' accessed at index 39, which is out of bounds.
+storage/innobase/trx/trx0i_s.cc: error: Array 'table_cache->chunks[39]' accessed at index 39, which is out of bounds.
+storage/innobase/trx/trx0purge.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/trx/trx0rec.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/trx/trx0roll.cc: error: There is an unknown macro here somewhere. Configuration is required. If DECLARE_THREAD is a macro then please configure it.
+storage/innobase/trx/trx0trx.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/innobase/trx/trx0undo.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ha_maria.cc: error: syntax error
+storage/maria/ma_bitmap.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_blockrec.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_check.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_checkpoint.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_delete.c: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/maria/ma_delete.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_ft_parser.c: error: Address of local auto-variable assigned to a function parameter.
+storage/maria/ma_key.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_loghandler.c: warning: Uninitialized variable: data->current_offset
+storage/maria/ma_open.c: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/maria/ma_pagecache.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_pagecache.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_range.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_recovery_util.c: error: va_start() or va_copy() called subsequently on 'args' without va_end() in between.
+storage/maria/ma_rkey.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_rt_index.c: error: failed to expand 'rt_PAGE_END', Wrong number of parameters for macro 'rt_PAGE_END'.
+storage/maria/ma_search.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_sp_key.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_update.c: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/maria/ma_update.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/ma_write.c: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/maria/ma_write.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/maria/maria_pack.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/ft_parser.c: error: Address of local auto-variable assigned to a function parameter.
+storage/myisam/ha_myisam.cc: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_check.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_close.c: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/myisam/mi_delete.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_key.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_locking.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_open.c: error: There is an unknown macro here somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please configure it.
+storage/myisam/mi_range.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_rkey.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_search.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_update.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/mi_write.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisam/myisampack.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+storage/myisammrg/ha_myisammrg.cc: error: syntax error
+storage/oqgraph/ha_oqgraph.cc: error: syntax error
+storage/perfschema/ha_perfschema.cc: error: syntax error
+storage/perfschema/pfs_instr.h: error: Uninitialized variable: m_has_io_stats
+storage/perfschema/pfs_instr.h: error: Uninitialized variable: m_has_lock_stats
+storage/perfschema/pfs_instr_class.cc: error: There is an unknown macro here somewhere. Configuration is required. If MY_ALIGNED is a macro then please configure it.
+storage/perfschema/table_accounts.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_esgs_by_account_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_esgs_by_host_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_esgs_by_user_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_esms_by_account_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_esms_by_host_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_esms_by_user_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_events_waits.cc: error: Uninitialized struct member: wait.m_wait_class
+storage/perfschema/table_events_waits.cc: error: Uninitialized variable: wait
+storage/perfschema/table_events_waits.cc: error: Uninitialized struct member: wait.m_wait_class
+storage/perfschema/table_ews_by_account_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_ews_by_host_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_ews_by_user_by_event_name.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_hosts.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_setup_actors.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_threads.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/perfschema/table_users.cc: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+storage/sequence/sequence.cc: error: syntax error
+storage/test_sql_discovery/test_sql_discovery.cc: error: syntax error
+strings/decimal.c: warning: Possible null pointer dereference: to
+strings/dump_map.c: error: Array 'fromstat[256]' accessed at index 256, which is out of bounds.
+tests/mysql_client_fw.c: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+tests/thread_test.c: error: There is an unknown macro here somewhere. Configuration is required. If STRINGIFY_ARG is a macro then please configure it.
+unittest/mysys/dynstring-t.c: error: syntax error
+unittest/mysys/queues-t.c: error: Uninitialized variable: i
+unittest/mysys/waiting_threads-t.c: error: Uninitialized variable: m
+unittest/mytap/tap.c: error: va_list 'ap' used before va_start() was called.
+unittest/mytap/tap.c: error: va_list 'ap' used before va_start() was called.
+unittest/mytap/tap.c: error: va_list 'ap' used before va_start() was called.
+unittest/mytap/tap.c: error: va_list 'ap' used before va_start() was called.
+vio/viosocket.c: error: There is an unknown macro here somewhere. Configuration is required. If MYSQL_SOCKET_WAIT_VARIABLES is a macro then please configure it.
+vio/viosocket.c: error: There is an unknown macro here somewhere. Configuration is required. If MYSQL_SOCKET_WAIT_VARIABLES is a macro then please configure it.
+vio/viosslfactories.c: error: There is an unknown macro here somewhere. Configuration is required. If ; is a macro then please configure it.
+vio/viotest-sslconnect.cc: error: Memory pointed to by 'vio' is freed twice.
+vio/viotest-sslconnect.cc: error: Memory pointed to by 'ssl_connector' is freed twice.
+wsrep-lib/src/server_state.cpp: error: syntax error: keyword 'try' is not allowed in global scope
+wsrep-lib/src/thread_service_v1.cpp: error: Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow. If there is no current exception this calls std::terminate(). More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object
diff --git a/tests/code_quality/flawfinder_ignorelist.json b/tests/code_quality/flawfinder_ignorelist.json
new file mode 100644
index 00000000..7b598689
--- /dev/null
+++ b/tests/code_quality/flawfinder_ignorelist.json
@@ -0,0 +1,622 @@
+{
+ "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json",
+ "version": "2.1.0",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "Flawfinder",
+ "version": "2.0.19",
+ "informationUri": "https://dwheeler.com/flawfinder/",
+ "supportedTaxonomies": [
+ {
+ "name": "CWE",
+ "guid": "FFC64C90-42B6-44CE-8BEB-F6B7DAE649E5"
+ }
+ ]
+ }
+ },
+ "columnKind": "utf16CodeUnits",
+ "results": [
+ {
+ "ruleId": "FF1010",
+ "level": "error",
+ "message": {
+ "text": "buffer/strncat:Easily used incorrectly (e.g., incorrectly computing the correct maximum size to add) [MS-banned] (CWE-120)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/portability/file.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 5,
+ "endColumn": 39,
+ "snippet": {
+ "text": " strncat(buf, path, TOKU_PATH_MAX);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "02af921b7054342955d8e30b196aa5ffdc3b1ac019e26c92823a7ab171d2b1fa"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/ft/logger/logformat.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 5,
+ "endColumn": 40,
+ "snippet": {
+ "text": " chmod(headerpath, S_IRUSR|S_IWUSR);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "085f579f942967e5c81fff75af832721b7b9bc59e54a7a9ebc086065cf56be13"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1035",
+ "level": "error",
+ "message": {
+ "text": "race/readlink:This accepts filename arguments; if an attacker can move those files or change the link content, a race condition results. Also, it does not terminate with ASCII NUL. (CWE-362, CWE-20)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/portability/file.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 25,
+ "endColumn": 63,
+ "snippet": {
+ "text": " ssize_t s = readlink(fdname, lname, sizeof lname);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "0dba1d2cdc995ccf30ad8fe5ce3ccf8795bd4f5a207f65c627affa2ef388496c"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1035",
+ "level": "error",
+ "message": {
+ "text": "race/readlink:This accepts filename arguments; if an attacker can move those files or change the link content, a race condition results. Also, it does not terminate with ASCII NUL. (CWE-362, CWE-20)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./extra/mariabackup/xtrabackup.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 17,
+ "endColumn": 57,
+ "snippet": {
+ "text": " ssize_t ret = readlink(\"/proc/self/exe\", buf, size-1);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "11523490c7f8cba115bce125bbce94de5cd5e7f66d4dd07a391aac70fbbdd353"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./client/mysqltest.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 13,
+ "endColumn": 38,
+ "snippet": {
+ "text": " err_code= chmod(ds_file.str, mode);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "12a7fa6bbd4c81be975838bae2b7b26fe841acaf9804e6d0299188683e230908"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/ft/logger/logformat.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 5,
+ "endColumn": 38,
+ "snippet": {
+ "text": " chmod(codepath, S_IRUSR|S_IWUSR);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "2827dedcdf10af2bf4105f3d48e30575238fa2552603cdcb09d536b288808f0e"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1014",
+ "level": "error",
+ "message": {
+ "text": "buffer/gets:Does not check for buffer overflows (CWE-120, CWE-20)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./extra/readline/tilde.c",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 12,
+ "endColumn": 24,
+ "snippet": {
+ "text": " if (!gets (line))"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "34a940ccc6e0248a2cf725e8a0c3f808d1f36d47fc814bd9daadb17f5563d357"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./sql/sql_class.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 10,
+ "endColumn": 28,
+ "snippet": {
+ "text": " (void) chmod(path, 0644);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "3f97fd0452062ab69db87a04222a17c37c216c4e28e2ae3622730da8dd070d2e"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./mysys/my_chmod.c",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 7,
+ "endColumn": 25,
+ "snippet": {
+ "text": " if (chmod(name, mode))"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "46805eec1d288b072d4edb3214822220d394307195be79a33ec3bce455d14750"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1010",
+ "level": "error",
+ "message": {
+ "text": "buffer/strncat:Easily used incorrectly (e.g., incorrectly computing the correct maximum size to add) [MS-banned] (CWE-120)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/ft/tests/recovery-datadir-is-file.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 9,
+ "endColumn": 47,
+ "snippet": {
+ "text": " strncat(buf, testfile, TOKU_PATH_MAX);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "4ca2dff1e35445f7997a9979cdd006d89befcc89922cf5d4a60bc9c07126a78d"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1035",
+ "level": "error",
+ "message": {
+ "text": "race/readlink:This accepts filename arguments; if an attacker can move those files or change the link content, a race condition results. Also, it does not terminate with ASCII NUL. (CWE-362, CWE-20)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./mysys/my_symlink.c",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 15,
+ "endColumn": 56,
+ "snippet": {
+ "text": " if ((length=readlink(filename, to, FN_REFLEN-1)) < 0)"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "7da5207ac0f5baba73c026472a2d3805eed92931852575db64f513702977dd70"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1031",
+ "level": "error",
+ "message": {
+ "text": "race/chown:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./mysys/my_redel.c",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 7,
+ "endColumn": 49,
+ "snippet": {
+ "text": " if (chown(to, statbuf.st_uid, statbuf.st_gid))"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "97d2cfe4cb9428e812b796eb39c27f28dc8b198ab9655c2aff8c442de39bdcfe"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/ft/logger/logformat.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 9,
+ "endColumn": 50,
+ "snippet": {
+ "text": " chmod(codepath, S_IRUSR|S_IRGRP|S_IROTH);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "a62b28fca5c6218ee4731e78bb3eacb93604fae20c91c69cccad3834973e70d5"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1035",
+ "level": "error",
+ "message": {
+ "text": "race/readlink:This accepts filename arguments; if an attacker can move those files or change the link content, a race condition results. Also, it does not terminate with ASCII NUL. (CWE-362, CWE-20)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/rocksdb/rocksdb/port/stack_trace.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 15,
+ "endColumn": 54,
+ "snippet": {
+ "text": " auto read = readlink(link, name, sizeof(name) - 1);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "acb399f2a4a15ef8da36c47631bc4ee4bcc1bb0577dfbda141d2eb5d7723af40"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1035",
+ "level": "error",
+ "message": {
+ "text": "race/readlink:This accepts filename arguments; if an attacker can move those files or change the link content, a race condition results. Also, it does not terminate with ASCII NUL. (CWE-362, CWE-20)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./sql/signal_handler.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 13,
+ "endColumn": 66,
+ "snippet": {
+ "text": " if ((len= readlink(\"/proc/self/cwd\", buff, sizeof(buff))) >= 0)"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "b55a5f3db29b1ce25e12f94e4ea344ed7fb0e63a230cf6b6deb42c28de924457"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./mysys/my_copy.c",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 9,
+ "endColumn": 46,
+ "snippet": {
+ "text": " if (chmod(to, stat_buff.st_mode & 07777))"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "bddb795a7efbd73a4387bbd33fd4f9e505b4f759d784e5d51f60cc43011ee610"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1031",
+ "level": "error",
+ "message": {
+ "text": "race/chown:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./mysys/my_copy.c",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 9,
+ "endColumn": 55,
+ "snippet": {
+ "text": " if (chown(to, stat_buff.st_uid, stat_buff.st_gid))"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "c63a81105d753de4762cbcab48d9700f7069da3cd9d57bf4329a6d20fad288aa"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/ft/logger/logformat.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 9,
+ "endColumn": 52,
+ "snippet": {
+ "text": " chmod(headerpath, S_IRUSR|S_IRGRP|S_IROTH);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "cc51b21d9b803a08b6c619b63abf77f4ca9ce247db0ef1b81f4bd83dfb95f3d8"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./sql/mysqld.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 12,
+ "endColumn": 71,
+ "snippet": {
+ "text": " (void) chmod(mysqld_unix_port,S_IFSOCK);\t/* Fix solaris 2.6 bug */"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "d0c4f1302290e2367e246ef7c8d3ea69589cbc4bc148e0efdd4c283fa03cbe01"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1033",
+ "level": "error",
+ "message": {
+ "text": "race/chmod:This accepts filename arguments; if an attacker can move those files, a race condition results. (CWE-362)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./mysys/my_redel.c",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 7,
+ "endColumn": 42,
+ "snippet": {
+ "text": " if (chmod(to, statbuf.st_mode & 07777))"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "e11b8df9cbb9e459e4d67a0af5e627b6b1285c78fe23f5a1c823285da96495a8"
+ },
+ "rank": 1.0
+ },
+ {
+ "ruleId": "FF1035",
+ "level": "error",
+ "message": {
+ "text": "race/readlink:This accepts filename arguments; if an attacker can move those files or change the link content, a race condition results. Also, it does not terminate with ASCII NUL. (CWE-362, CWE-20)."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "./storage/tokudb/PerconaFT/portability/file.cc",
+ "uriBaseId": "SRCROOT"
+ },
+ "region": {
+ "startColumn": 29,
+ "endColumn": 67,
+ "snippet": {
+ "text": " ssize_t n = readlink(fname, symname, MY_MAX_PATH);"
+ }
+ }
+ }
+ }
+ ],
+ "fingerprints": {
+ "contextHash/v1": "e307b1923cc852324e3050b3e4423be7ac4d1d64af274b70b897a85b1cde815f"
+ },
+ "rank": 1.0
+ }
+ ],
+ "externalPropertyFileReferences": {
+ "taxonomies": [
+ {
+ "location": {
+ "uri": "https://raw.githubusercontent.com/sarif-standard/taxonomies/main/CWE_v4.4.sarif"
+ },
+ "guid": "FFC64C90-42B6-44CE-8BEB-F6B7DAE649E5"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/connect_test.c b/tests/connect_test.c
new file mode 100644
index 00000000..121ff2aa
--- /dev/null
+++ b/tests/connect_test.c
@@ -0,0 +1,66 @@
+/* Copyright (c) 2000, 2002-2004 MySQL AB
+ Use is subject to license terms
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql.h"
+
+static void change_user(MYSQL *sock,const char *user, const char *password,
+ const char *db,my_bool warning)
+{
+ if (mysql_change_user(sock,user,password,db) != warning)
+ {
+ fprintf(stderr,"Couldn't change user to: user: '%s', password: '%s', db: '%s': Error: %s\n",
+ user, password ? password : "", db ? db : "",
+ mysql_error(sock));
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ MYSQL *sock;
+
+ if (!(sock=mysql_init(0)))
+ {
+ fprintf(stderr,"Couldn't initialize mysql struct\n");
+ exit(1);
+ }
+ mysql_options(sock,MYSQL_READ_DEFAULT_GROUP,"connect");
+ if (!mysql_real_connect(sock,NULL,NULL,NULL,NULL,0,NULL,0))
+ {
+ fprintf(stderr,"Couldn't connect to engine!\n%s\n",mysql_error(sock));
+ perror("");
+ exit(1);
+ }
+ sock->reconnect= 1;
+
+ if (mysql_select_db(sock,"test"))
+ {
+ fprintf(stderr,"Couldn't select database test: Error: %s\n",
+ mysql_error(sock));
+ }
+
+ change_user(sock,"test_user","test_user","test",0);
+ change_user(sock,"test",NULL,"test",0);
+ change_user(sock,"test_user",NULL,"test",1);
+ change_user(sock,"test_user",NULL,NULL,1);
+ change_user(sock,"test_user","test_user","mysql",1);
+
+ mysql_close(sock);
+ exit(0);
+ return 0;
+}
diff --git a/tests/consistent_snapshot.pl b/tests/consistent_snapshot.pl
new file mode 100755
index 00000000..5c006b00
--- /dev/null
+++ b/tests/consistent_snapshot.pl
@@ -0,0 +1,107 @@
+#! /usr/bin/perl
+
+# Test START TRANSACTION WITH CONSISTENT SNAPSHOT.
+# With MWL#116, this is implemented so it is actually consistent.
+
+use strict;
+use warnings;
+
+use DBI;
+
+my $UPDATERS= 10;
+my $READERS= 5;
+
+my $ROWS= 50;
+my $DURATION= 20;
+
+my $stop_time= time() + $DURATION;
+
+sub my_connect {
+ my $dbh= DBI->connect("DBI:MariaDB:mariadb_socket=/tmp/mysql.sock;database=test",
+ "root", undef, { RaiseError=>1, PrintError=>0, AutoCommit=>0});
+ $dbh->do("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ");
+ $dbh->do("SET SESSION autocommit = 0");
+ return $dbh;
+}
+
+sub my_setup {
+ my $dbh= my_connect();
+
+ $dbh->do("DROP TABLE IF EXISTS test_consistent_snapshot1, test_consistent_snapshot2");
+ $dbh->do(<<TABLE);
+CREATE TABLE test_consistent_snapshot1 (
+ a INT PRIMARY KEY,
+ b INT NOT NULL
+) ENGINE=InnoDB
+TABLE
+ $dbh->do(<<TABLE);
+CREATE TABLE test_consistent_snapshot2(
+ a INT PRIMARY KEY,
+ b INT NOT NULL
+) ENGINE=PBXT
+TABLE
+
+ for (my $i= 0; $i < $ROWS; $i++) {
+ my $value= int(rand()*1000);
+ $dbh->do("INSERT INTO test_consistent_snapshot1 VALUES (?, ?)", undef,
+ $i, $value);
+ $dbh->do("INSERT INTO test_consistent_snapshot2 VALUES (?, ?)", undef,
+ $i, -$value);
+ }
+ $dbh->commit();
+ $dbh->disconnect();
+}
+
+sub my_updater {
+ my $dbh= my_connect();
+
+ while (time() < $stop_time) {
+ my $i1= int(rand()*$ROWS);
+ my $i2= int(rand()*$ROWS);
+ my $v= int(rand()*99)-49;
+ $dbh->do("UPDATE test_consistent_snapshot1 SET b = b + ? WHERE a = ?",
+ undef, $v, $i1);
+ $dbh->do("UPDATE test_consistent_snapshot2 SET b = b - ? WHERE a = ?",
+ undef, $v, $i2);
+ $dbh->commit();
+ }
+
+ $dbh->disconnect();
+ exit(0);
+}
+
+sub my_reader {
+ my $dbh= my_connect();
+
+ my $iteration= 0;
+ while (time() < $stop_time) {
+ $dbh->do("START TRANSACTION WITH CONSISTENT SNAPSHOT");
+ my $s1= $dbh->selectrow_arrayref("SELECT SUM(b) FROM test_consistent_snapshot1");
+ $s1= $s1->[0];
+ my $s2= $dbh->selectrow_arrayref("SELECT SUM(b) FROM test_consistent_snapshot2");
+ $s2= $s2->[0];
+ $dbh->commit();
+ if ($s1 + $s2 != 0) {
+ print STDERR "Found inconsistency, s1=$s1 s2=$s2 iteration=$iteration\n";
+ last;
+ }
+ ++$iteration;
+ }
+
+ $dbh->disconnect();
+ exit(0);
+}
+
+my_setup();
+
+for (1 .. $UPDATERS) {
+ fork() || my_updater();
+}
+
+for (1 .. $READERS) {
+ fork() || my_reader();
+}
+
+waitpid(-1, 0) for (1 .. ($UPDATERS + $READERS));
+
+print "All checks done\n";
diff --git a/tests/deadlock_test.c b/tests/deadlock_test.c
new file mode 100644
index 00000000..b4deef84
--- /dev/null
+++ b/tests/deadlock_test.c
@@ -0,0 +1,239 @@
+/* Copyright (C) 2000-2001, 2003-2004, 2006 MySQL AB
+ Use is subject to license terms
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include <mysql.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef unsigned char uchar;
+static void die(char* fmt, ...);
+static void safe_query(MYSQL* mysql, char* query, int read_ok);
+static void run_query_batch(int* order, int num_queries);
+static void permute(int *order, int num_queries);
+static void permute_aux(int *order, int num_queries, int* fixed);
+static void dump_result(MYSQL* mysql, char* query);
+
+int count = 0;
+
+
+struct query
+{
+ MYSQL* mysql;
+ char* query;
+ int read_ok;
+ int pri;
+ int dump_result;
+};
+
+MYSQL lock, sel, del_ins;
+
+struct query queries[] =
+{
+ {&del_ins, "insert delayed into foo values(1)", 1, 0, 0},
+ {&del_ins, "insert delayed into foo values(1)", 1, 0, 0},
+ {&lock, "lock tables foo write", 1, 1, 0},
+ {&lock, "unlock tables", 1,2, 0},
+ {&sel, "select * from foo", 0,0, 0},
+ {&del_ins, "insert into foo values(4)", 0,3, 0},
+ {0,0,0}
+};
+
+static void die(char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(1);
+}
+
+static void permute(int *order, int num_queries)
+{
+ int *fixed;
+ if(num_queries < 2) return;
+ if(!(fixed = (int*)malloc(num_queries * sizeof(int))))
+ die("malloc() failed");
+
+ memset(fixed, 0, num_queries * sizeof(int));
+ permute_aux(order, num_queries, fixed);
+
+ free(fixed);
+}
+
+static order_ok(int *order, int num_queries)
+{
+ int i,j, pri_i, pri_j;
+ for(i = 0; i < num_queries; i++)
+ {
+ if((pri_i = queries[order[i]].pri))
+ for(j = i + 1; j < num_queries; j++)
+ {
+ pri_j = queries[order[j]].pri;
+ if(pri_j && pri_i > pri_j)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void permute_aux(int *order, int num_queries, int* fixed)
+{
+ int *p,*p1,j,i,tmp, num_free = 0;
+ p = fixed;
+ for(i = 0; i < num_queries; i++, p++)
+ {
+ if(!*p)
+ {
+ num_free++;
+ *p = 1;
+ for(j = 0, p1 = fixed ;
+ j < num_queries; j++,p1++)
+ {
+ if(!*p1)
+ {
+ tmp = order[i];
+ order[i] = order[j];
+ order[j] = tmp;
+ *p1 = 1;
+ permute_aux(order, num_queries, fixed);
+ tmp = order[i];
+ order[i] = order[j];
+ order[j] = tmp;
+ *p1 = 0;
+ }
+ }
+ *p = 0;
+ }
+ }
+
+ /*printf("num_free = %d\n", num_free); */
+
+ if(num_free <= 1)
+ {
+ count++;
+ if(order_ok(order, num_queries))
+ run_query_batch(order, num_queries);
+ }
+}
+
+static void run_query_batch(int* order, int num_queries)
+{
+ int i;
+ struct query* q;
+ int *save_order;
+ safe_query(&lock, "delete from foo", 1);
+ save_order = order;
+ for(i = 0; i < num_queries; i++,order++)
+ {
+ q = queries + *order;
+ printf("query='%s'\n", q->query);
+ safe_query(q->mysql, q->query, q->read_ok);
+ }
+ order = save_order;
+ for(i = 0; i < num_queries; i++,order++)
+ {
+ q = queries + *order;
+ if(q->dump_result)
+ dump_result(q->mysql, q->query);
+ }
+ printf("\n");
+
+}
+
+static void safe_net_read(NET* net, char* query)
+{
+ int len;
+ len = my_net_read(net);
+ if(len == packet_error || !len)
+ die("Error running query '%s'", query);
+ if(net->read_pos[0] == 255)
+ die("Error running query '%s'", query);
+}
+
+
+static void safe_query(MYSQL* mysql, char* query, int read_ok)
+{
+ int len;
+ NET* net = &mysql->net;
+ net_clear(net);
+ if(net_write_command(net,(uchar)COM_QUERY, query,strlen(query)))
+ die("Error running query '%s': %s", query, mysql_error(mysql));
+ if(read_ok)
+ {
+ safe_net_read(net, query);
+ }
+}
+
+static void dump_result(MYSQL* mysql, char* query)
+{
+ MYSQL_RES* res;
+ safe_net_read(&mysql->net, query);
+ res = mysql_store_result(mysql);
+ if(res)
+ mysql_free_result(res);
+}
+
+static int* init_order(int* num_queries)
+{
+ struct query* q;
+ int *order, *order_end, *p;
+ int n,i;
+
+ for(q = queries; q->mysql; q++)
+ ;
+
+ n = q - queries;
+ if(!(order = (int*) malloc(n * sizeof(int))))
+ die("malloc() failed");
+ order_end = order + n;
+ for(p = order,i = 0; p < order_end; p++,i++)
+ *p = i;
+ *num_queries = n;
+ return order;
+}
+
+int main()
+{
+ char* user = "root", *pass = "", *host = "localhost", *db = "test";
+ int *order, num_queries;
+ order = init_order(&num_queries);
+ if(!mysql_init(&lock) || !mysql_init(&sel) || !mysql_init(&del_ins))
+ die("error in mysql_init()");
+
+ mysql_options(&lock, MYSQL_READ_DEFAULT_GROUP, "mysql");
+ mysql_options(&sel, MYSQL_READ_DEFAULT_GROUP, "mysql");
+ mysql_options(&del_ins, MYSQL_READ_DEFAULT_GROUP, "mysql");
+
+ if(!mysql_real_connect(&lock, host, user, pass, db, 0,0,0 ) ||
+ !mysql_real_connect(&sel, host, user, pass, db, 0,0,0 ) ||
+ !mysql_real_connect(&del_ins, host, user, pass, db, 0,0,0 ))
+ die("Error in mysql_real_connect(): %s", mysql_error(&lock));
+ lock.reconnect= sel.reconnect= del_ins.reconnect= 1;
+
+ permute(order, num_queries);
+ printf("count = %d\n", count);
+
+ mysql_close(&lock);
+ mysql_close(&sel);
+ mysql_close(&del_ins);
+ free(order);
+}
diff --git a/tests/drop_test.pl b/tests/drop_test.pl
new file mode 100755
index 00000000..e9b796bd
--- /dev/null
+++ b/tests/drop_test.pl
@@ -0,0 +1,237 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# This is a test with uses processes to insert, select and drop tables.
+#
+
+$opt_loop_count=100000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+ $opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_host=""; $opt_db="test";
+
+GetOptions("host=s","db=s","loop-count=i","skip-create","skip-in","skip-delete",
+"verbose","fast-insert","lock-tables","debug","fast","force") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+print "Testing 5 multiple connections to a server with 1 insert, 2 drop/rename\n";
+print "1 select and 1 flush thread\n";
+
+$firsttable = "bench_f1";
+
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+if (!$opt_skip_create)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table if exists $firsttable, ${firsttable}_1, ${firsttable}_2");
+
+ print "Creating table $firsttable in database $opt_db\n";
+ $dbh->do("create table $firsttable (id int(6) not null, info varchar(32), marker char(1), primary key(id))") || die $DBI::errstr;
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+test_insert() if (($pid=fork()) == 0); $work{$pid}="insert";
+test_drop(1) if (($pid=fork()) == 0); $work{$pid}="drop 1";
+test_drop(2) if (($pid=fork()) == 0); $work{$pid}="drop 2";
+test_select() if (($pid=fork()) == 0); $work{$pid}="select";
+test_flush() if (($pid=fork()) == 0); $work{$pid}="flush";
+
+$errors=0;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ $errors++ if ($ret != 0);
+}
+
+if (!$opt_skip_delete && !$errors)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table $firsttable");
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+print ($errors ? "Test failed\n" :"Test ok\n");
+
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+#
+# Insert records in the table
+#
+
+sub test_insert
+{
+ my ($dbh,$i);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ if (!$dbh->do("insert into $firsttable values ($i,'This is entry $i','')"))
+ {
+ print "Warning; Got error on insert: " . $dbh->errstr . "\n" if (! ($dbh->errstr =~ /doesn't exist/));
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_insert: Inserted $i rows\n";
+ exit(0);
+}
+
+
+sub test_drop
+{
+ my ($id) = @_;
+ my ($dbh,$i,$sth,$error_counter,$sleep_time);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $error_counter=0;
+ $sleep_time=2;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ sleep($sleep_time);
+ # Check if insert thread is ready
+ $sth=$dbh->prepare("select count(*) from $firsttable") || die "Got error on select from $firsttable: $dbh->errstr\n";
+ if (!$sth->execute || !(@row = $sth->fetchrow_array()) ||
+ !$row[0])
+ {
+ $sth->finish;
+ $sleep_time=1;
+ last if ($error_counter++ == 5);
+ next;
+ }
+ $sleep_time=2;
+ $sth->finish;
+
+ # Change to use a new table
+ $dbh->do("create table ${firsttable}_$id (id int(6) not null, info varchar(32), marker char(1), primary key(id))") || die $DBI::errstr;
+ $dbh->do("drop table if exists $firsttable") || die "Got error on drop table: $dbh->errstr\n";
+ if (!$dbh->do("alter table ${firsttable}_$id rename to $firsttable"))
+ {
+ print "Warning; Got error from alter table: " . $dbh->errstr . "\n" if (! ($dbh->errstr =~ /already exist/));
+ $dbh->do("drop table if exists ${firsttable}_$id") || die "Got error on drop table: $dbh->errstr\n";
+ }
+ }
+ $dbh->do("drop table if exists $firsttable,${firsttable}_$id") || die "Got error on drop table: $dbh->errstr\n";
+ $dbh->disconnect; $dbh=0;
+ print "Test_drop: Did a drop $i times\n";
+ exit(0);
+}
+
+
+#
+# select records
+#
+
+sub test_select
+{
+ my ($dbh,$i,$sth,@row,$error_counter,$sleep_time);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $error_counter=0;
+ $sleep_time=3;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ sleep($sleep_time);
+ $sth=$dbh->prepare("select sum(t.id) from $firsttable as t,$firsttable as t2") || die "Got error on select: $dbh->errstr;\n";
+ if ($sth->execute)
+ {
+ @row = $sth->fetchrow_array();
+ $sth->finish;
+ $sleep_time=3;
+ }
+ else
+ {
+ print "Warning; Got error from select: " . $dbh->errstr . "\n" if (! ($dbh->errstr =~ /doesn't exist/));
+ $sth->finish;
+ last if ($error_counter++ == 5);
+ $sleep_time=1;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: ok\n";
+ exit(0);
+}
+
+#
+# flush records
+#
+
+sub test_flush
+{
+ my ($dbh,$i,$sth,@row,$error_counter,$sleep_time);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $error_counter=0;
+ $sleep_time=5;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ sleep($sleep_time);
+ $sth=$dbh->prepare("select count(*) from $firsttable") || die "Got error on prepar: $dbh->errstr;\n";
+ if ($sth->execute)
+ {
+ @row = $sth->fetchrow_array();
+ $sth->finish;
+ $sleep_time=5;
+ $dbh->do("flush tables $firsttable") || die "Got error on flush table: " . $dbh->errstr . "\n";
+ }
+ else
+ {
+ print "Warning; Got error from select: " . $dbh->errstr . "\n" if (! ($dbh->errstr =~ /doesn't exist/));
+ $sth->finish;
+ last if ($error_counter++ == 5);
+ $sleep_time=1;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: ok\n";
+ exit(0);
+}
diff --git a/tests/export.pl b/tests/export.pl
new file mode 100755
index 00000000..dace79fe
--- /dev/null
+++ b/tests/export.pl
@@ -0,0 +1,180 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000, 2001 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+# This is a test with uses two processes to a database.
+# The other inserts records in two tables, the other does a lot of joins
+# on these.
+#
+# Warning, the output from this test will differ in 'found' from time to time,
+# but there should never be any errors
+#
+
+$host= shift || "";
+$test_db="test";
+
+use Mysql;
+$|= 1; # Autoflush
+
+$org_file="/tmp/export-org.$$";
+$tmp_file="/tmp/export-old.$$";
+$tmp_file2="/tmp/export-new.$$";
+
+print "Connection to database $test_db\n";
+
+$dbh = Mysql->Connect($host) || die "Can't connect: $Mysql::db_errstr\n";
+$dbh->SelectDB($test_db) || die "Can't use database $test_db: $Mysql::db_errstr\n";
+
+$dbh->Query("drop table if exists export"); # Ignore this error
+
+print "Creating table\n";
+
+($dbh->Query("\
+CREATE TABLE export (
+ auto int(5) unsigned NOT NULL DEFAULT '0' auto_increment,
+ string char(11) NOT NULL,
+ tiny tinyint(4) NOT NULL DEFAULT '0',
+ short smallint(6) NOT NULL DEFAULT '0',
+ medium mediumint(8) NOT NULL DEFAULT '0',
+ longint int(11) NOT NULL DEFAULT '0',
+ longlong bigint(20) NOT NULL DEFAULT '0',
+ real_float float(13,1) NOT NULL DEFAULT '0.0',
+ real_double double(13,1) NOT NULL,
+ utiny tinyint(3) unsigned NOT NULL DEFAULT '0',
+ ushort smallint(5) unsigned zerofill NOT NULL DEFAULT '00000',
+ umedium mediumint(8) unsigned NOT NULL DEFAULT '0',
+ ulong int(11) unsigned NOT NULL DEFAULT '0',
+ ulonglong bigint(20) unsigned NOT NULL DEFAULT '0',
+ time_stamp timestamp,
+ blob_col blob,
+ tinyblob_col tinyblob,
+ mediumblob_col tinyblob not null,
+ longblob_col longblob not null,
+ PRIMARY KEY (auto),
+ KEY (string(5)),
+ KEY unsigned_tinykey (utiny),
+ KEY (tiny),
+ KEY (short),
+ FOREIGN KEY (medium) references export,
+ KEY (longlong),
+ KEY (real_float),
+ KEY (real_double),
+ KEY (ushort),
+ KEY (umedium),
+ KEY (ulong),
+ KEY (ulonglong),
+ KEY (ulonglong,ulong))")) or die $Mysql::db_errstr;
+
+print "Inserting data\n";
+
+@A=("insert into export values (10, 1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1)",
+ "insert into export values (NULL,2,2,2,2,2,2,2,2,2,2,2,2,2,NULL,NULL,NULL,2,2)",
+ "insert into export values (0,1/3,3,3,3,3,3,3,3,3,3,3,3,3,3,'','','','3')",
+ "insert into export values (0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,'-1')",
+ "insert into export values (0,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,-4294967295,'-4294967295')",
+ "insert into export values (0,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,'4294967295')",
+ "insert into export (string,tinyblob_col) values ('special','''\\0\\t\t\n''')",
+ "insert into export (string) values (',,!!\\\\##')",
+ "insert into export (tinyblob_col) values (',,!!!\\\\\\##')"
+ );
+
+foreach $A (@A)
+{
+ $dbh->Query($A) or die "query: $A returned: " . $Mysql::db_errstr;
+}
+
+
+print "Doing dump, load, check on different formats\n";
+
+@A=(# Ordinary format
+ "",
+ # Field terminated by something
+ "fields optionally enclosed by '+' escaped by '' terminated by ',,,' lines terminated by ',,,,'",
+ "fields enclosed by '' terminated by ',' lines terminated by ''",
+ "fields enclosed by '' terminated by ',' lines terminated by '!!'",
+ #Fields enclosed by
+ #"fields enclosed by '+' terminated by ''",
+ #"fields enclosed by '+' terminated by '' lines terminated by ''",
+ "fields enclosed by '+' terminated by ',,' lines terminated by '!!!'",
+ "fields enclosed by '+' terminated by ',,' lines terminated by '##'",
+ "fields enclosed by '+' escaped by '' terminated by ',,' lines terminated by '###'",
+ "fields enclosed by '+' escaped by '' terminated by '!' lines terminated by ''",
+ "fields enclosed by '+' terminated by ',' lines terminated by ''",
+ #Fields optionally enclosed by
+ "fields optionally enclosed by '+' terminated by ','",
+ "fields optionally enclosed by '+' terminated by ',' lines terminated by ''",
+ "fields optionally enclosed by '''' terminated by ',' lines starting by 'INSERT INTO a VALUES(' terminated by ');\n'",
+ );
+
+$dbh->Query("select * into outfile '$org_file' from export") or die $Mysql::db_errstr;
+
+
+foreach $A (@A)
+{
+ unlink($tmp_file);
+ unlink($tmp_file2);
+ $dbh->Query("select * into outfile '$tmp_file' $A from export") or die $Mysql::db_errstr;
+ $dbh->Query("delete from export") or die $Mysql::db_errstr;
+ $dbh->Query("load data infile '$tmp_file' into table export $A") or die $Mysql::db_errstr . " with format: $A\n";
+ $dbh->Query("select * into outfile '$tmp_file2' from export") or die $Mysql::db_errstr;
+ if (`cmp $tmp_file2 $org_file`)
+ {
+ print "Using format $A\n";
+ print "$tmp_file2 and $org_file differ. Plese check files\n";
+ exit 1;
+ }
+}
+
+
+@A=(#Fixed size fields
+ "fields enclosed by '' escaped by '' terminated by ''",
+ "fields enclosed by '' escaped by '' terminated by '' lines terminated by '\\r\\n'",
+ "fields enclosed by '' terminated by '' lines terminated by ''"
+ );
+
+unlink($org_file);
+
+$field_list="auto,ifnull(string,''),tiny,short,medium,longint,longlong,real_float,ifnull(real_double,''),utiny,ushort,umedium,ulong,ulonglong,time_stamp";
+
+$dbh->Query("select $field_list into outfile '$org_file' from export") or die $Mysql::db_errstr;
+
+$field_list="auto,string,tiny,short,medium,longint,longlong,real_float,real_double,utiny,ushort,umedium,ulong,ulonglong,time_stamp";
+
+foreach $A (@A)
+{
+ unlink($tmp_file);
+ unlink($tmp_file2);
+ $dbh->Query("select $field_list into outfile '$tmp_file' $A from export") or die $Mysql::db_errstr;
+ $dbh->Query("delete from export") or die $Mysql::db_errstr;
+ $dbh->Query("load data infile '$tmp_file' into table export $A ($field_list)") or die $Mysql::db_errstr;
+ $dbh->Query("select $field_list into outfile '$tmp_file2' from export") or die $Mysql::db_errstr;
+ if (`cmp $tmp_file2 $org_file`)
+ {
+ print "Using format $A\n";
+ print "$tmp_file2 and $org_file differ. Plese check files\n";
+ exit 1;
+ }
+}
+
+unlink($tmp_file);
+unlink($tmp_file2);
+unlink($org_file);
+
+$dbh->Query("drop table export") or die $Mysql::db_errstr;
+
+print "Test ok\n";
+exit 0;
diff --git a/tests/fork2_test.pl b/tests/fork2_test.pl
new file mode 100755
index 00000000..55e494dc
--- /dev/null
+++ b/tests/fork2_test.pl
@@ -0,0 +1,257 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000, 2001 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+# This is a test with uses 5 processes to insert, update and select from
+# two tables.
+# One inserts records in the tables, one updates some record in it and
+# the last 3 does different selects on the tables.
+# Er, hmmm..., something like that :^)
+# Modified to do crazy-join, à la Nasdaq.
+#
+# This test uses the old obsolete mysql interface. For a test that uses
+# DBI, please take a look at fork_big.pl
+
+$opt_loop_count=10000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use Mysql;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+ $opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_host=""; $opt_db="test";
+
+GetOptions("host=s","db=s","loop-count=i","skip-create","skip-in",
+ "skip-delete", "verbose","fast-insert","lock-tables","debug","fast",
+ "force") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$Mysql::db_errstr=$opt_force=undef; # Ignore warnings from these
+
+print "Testing 10 multiple connections to a server with 1 insert/update\n";
+print "and 8 select connections and one ALTER TABLE.\n";
+
+
+@testtables = qw(bench_f21 bench_f22 bench_f23 bench_f24 bench_f25);
+$numtables = $#testtables; # make emacs happier
+$dtable = "directory";
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+if (!$opt_skip_create)
+{
+ $dbh = Mysql->Connect($opt_host, $opt_db) || die $Mysql::db_errstr;
+ $Mysql::QUIET = 1;
+ foreach $table (@testtables) {
+ $dbh->Query("drop table $table");
+ }
+ $dbh->Query("drop table $dtable");
+ $Mysql::QUIET = 0;
+
+ foreach $table (@testtables) {
+ print "Creating table $table in database $opt_db\n";
+ $dbh->Query("create table $table".
+ " (id int(6) not null,".
+ " info varchar(32),".
+ " marker timestamp,".
+ " primary key(id))")
+ or die $Mysql::db_errstr;
+ }
+ print "Creating directory table $dtable in $opt_db\n";
+ $dbh->Query("create table $dtable (id int(6), last int(6))")
+ or die $Mysql::db_errstr;
+ # Populate directory table
+ for $i ( 0 .. $numtables ) {
+ $dbh->Query("insert into $dtable values($i, 0)");
+ }
+ $dbh=0; # Close handler
+}
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+#$test_index = 0;
+
+test_1() if (($pid=fork()) == 0); $work{$pid}="insert";
+test_2() if (($pid=fork()) == 0); $work{$pid}="simple1";
+test_3() if (($pid=fork()) == 0); $work{$pid}="funny1";
+test_2() if (($pid=fork()) == 0); $work{$pid}="simple2";
+test_3() if (($pid=fork()) == 0); $work{$pid}="funny2";
+test_2() if (($pid=fork()) == 0); $work{$pid}="simple3";
+test_3() if (($pid=fork()) == 0); $work{$pid}="funny3";
+test_2() if (($pid=fork()) == 0); $work{$pid}="simple4";
+test_3() if (($pid=fork()) == 0); $work{$pid}="funny4";
+alter_test() if (($pid=fork()) == 0); $work{$pid}="alter";
+
+$errors=0;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ $errors++ if ($ret != 0);
+}
+
+if (!$opt_skip_delete && !$errors)
+{
+ $dbh = Mysql->Connect($opt_host, $opt_db) || die $Mysql::db_errstr;
+ foreach $table (@testtables) {
+ $dbh->Query("drop table $table");
+ }
+}
+print ($errors ? "Test failed\n" :"Test ok\n");
+
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+#
+# Insert records in the ?? tables the Nasdaq way
+#
+
+sub test_1
+{
+ my ($dbh,$table,$tmpvar,$rows,$found,$i);
+
+ $dbh = Mysql->Connect($opt_host, $opt_db) || die $Mysql::db_errstr;
+ $tmpvar=1;
+ $rows=$found=0;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % $numtables);
+ # Nasdaq step 1:
+ $sth=$dbh->Query("select id,last from $dtable where id='$tmpvar'")
+ or die "Select directory row: $Mysql::db_errstr\n";
+ # Nasdaq step 2:
+ my ($did,$dlast) = $sth->FetchRow
+ or die "Fetch directory row: $Mysql::db_errstr\n";
+ $dlast++;
+ $sth=$dbh->Query("INSERT into $testtables[$did]".
+ " VALUES($dlast,'This is entry $dlast',NULL)")
+ || die "Got error on insert table $testtable[$did]:".
+ " $Mysql::db_errstr\n";
+ # Nasdaq step 3 - where my application hangs
+ $sth=$dbh->Query("update $dtable set last='$dlast' where id='$tmpvar'")
+ or die "Updating directory for table $testtable[$did]:".
+ " Mysql::db_errstr\n";
+ $rows++;
+ }
+ $dbh=0;
+ print "Test_1: Inserted $rows rows\n";
+ exit(0);
+}
+
+#
+# Nasdaq simple select
+#
+
+sub test_2
+{
+ my ($dbh,$id,$tmpvar,$rows,$found,$i);
+
+ $dbh = Mysql->Connect($opt_host, $opt_db) || die $Mysql::db_errstr;
+ $rows=$found=0;
+ $tmpvar=1;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % $numtables);
+ $sth=$dbh->Query("select a.id,a.info from $testtables[$tmpvar] as a,".
+ "$dtable as d".
+ " where a.id=d.last and $i >= 0")
+ || die "Got error select max: $Mysql::db_errstr\n";
+ if ((@row = $sth->FetchRow()) && defined($row[0]))
+ {
+ $found++;
+ }
+ }
+ $dbh=0;
+ print "Test_2: Found $found rows\n";
+ exit(0);
+}
+
+
+#
+# Nasdaq not-so-simple select
+#
+
+sub test_3
+{
+ my ($dbh,$id,$tmpvar,$rows,$i);
+ $dbh = Mysql->Connect($opt_host, $opt_db) || die $Mysql::db_errstr;
+ $rows=0;
+ $tmpvar ||= $numtables;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % $numtables);
+ $id1 = ($tmpvar+1) % $numtables;
+ $id2 = ($id1+1) % $numtables;
+ $id3 = ($id2+1) % $numtables;
+ $sth = $dbh->Query("SELECT greatest(a.id, b.id, c.id), a.info".
+ " FROM $testtables[$id1] as a,".
+ " $testtables[$id2] as b,".
+ " $testtables[$id3] as c,".
+ " $dtable as d1, $dtable as d2, $dtable as d3".
+ " WHERE ".
+ " d1.last=a.id AND d2.last=b.id AND d3.last=c.id".
+ " AND d1.id='$id1' AND d2.id='$id2'".
+ " AND d3.id='$id3'")
+ or die "Funny select: $Mysql::db_errstr\n";
+ $rows+=$sth->numrows;
+ }
+ $dbh=0;
+ print "Test_3: Found $rows rows\n";
+ exit(0);
+}
+
+#
+# Do an ALTER TABLE every 20 seconds
+#
+
+sub alter_test
+{
+ my ($dbh,$count,$old_row_count,$row_count,$id,@row,$sth);
+
+ $dbh = Mysql->Connect($opt_host, $opt_db) || die $Mysql::db_errstr;
+ $id=$count=$row_count=0; $old_row_count= -1;
+
+ # Execute the test as long as we get more data into the table
+ while ($row_count != $old_row_count)
+ {
+ sleep(10);
+ $sth=$dbh->Query("ALTER TABLE $testtables[$id] modify info varchar(32)") or die "Couldn't execute ALTER TABLE\n";
+ $sth=0;
+ $id=($id+1) % $numtables;
+
+ # Test if insert test has ended
+ $sth=$dbh->query("select count(*) from $testtables[0]") or die "Couldn't execute count(*)\n";
+ @row = $sth->FetchRow();
+ $old_row_count= $row_count;
+ $row_count=$row[0];
+ $count++;
+ }
+ $dbh=0;
+ print "alter: Executed $count ALTER TABLE commands\n";
+ exit(0);
+}
diff --git a/tests/fork_big.pl b/tests/fork_big.pl
new file mode 100755
index 00000000..157ffc35
--- /dev/null
+++ b/tests/fork_big.pl
@@ -0,0 +1,610 @@
+#!/usr/bin/env perl
+use strict;
+
+# Copyright (c) 2001, 2006 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# This is a test with uses many processes to test a MySQL server.
+#
+# Tested a lot with: --threads=30
+
+my $opt_loop_count=500000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+our ($opt_skip_create,$opt_skip_in,$opt_verbose,$opt_fast_insert);
+our ($opt_lock_tables,$opt_debug,$opt_skip_delete,$opt_fast,$opt_force);
+our ($opt_threads);
+our ($opt_host,$opt_user,$opt_password,$opt_db);
+my (@testtables, $abort_table, $numtables, $start_time, $end_time);
+my ($dbh);
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+$opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_threads=5;
+$opt_host=$opt_user=$opt_password=""; $opt_db="test";
+
+GetOptions("host=s","db=s","user=s","password=s","loop-count=i","skip-create","skip-in","skip-delete","verbose","fast-insert","lock-tables","debug","fast","force","threads=i") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+print "Test of multiple connections that test the following things:\n";
+print "insert, select, delete, update, alter, check, repair and flush\n";
+
+@testtables = ( ["bench_f31", ""],
+ ["bench_f32", "row_format=fixed"],
+ ["bench_f33", "delay_key_write=1"],
+ ["bench_f34", "checksum=1"],
+ ["bench_f35", "delay_key_write=1"]);
+$abort_table="bench_f39";
+
+$numtables = $#testtables+1;
+srand 100; # Make random numbers repeatable
+
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+$dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+if (!$opt_skip_create)
+{
+ my $table_def;
+ foreach $table_def (@testtables)
+ {
+ my ($table,$extra)= ($table_def->[0], $table_def->[1]);
+ print "Creating table $table in database $opt_db\n";
+ $dbh->do("drop table if exists $table");
+ $dbh->do("create table $table".
+ " (id int(6) not null auto_increment,".
+ " info varchar(32)," .
+ " marker timestamp," .
+ " flag int not null," .
+ " primary key(id)) $extra")
+
+ or die $DBI::errstr;
+ # One row in the table will make future tests easier
+ $dbh->do("insert into $table (id) values (null)")
+ or die $DBI::errstr;
+ }
+ # Create the table we use to signal that we should end the test
+ $dbh->do("drop table if exists $abort_table");
+ $dbh->do("create table $abort_table (id int(6) not null) ENGINE=heap") ||
+ die $DBI::errstr;
+}
+
+$dbh->do("delete from $abort_table");
+$dbh->disconnect; $dbh=0; # Close handler
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+my ($i, $pid, %work);
+
+for ($i=0 ; $i < $opt_threads ; $i ++)
+{
+ test_insert() if (($pid=fork()) == 0); $work{$pid}="insert";
+}
+for ($i=0 ; $i < $numtables ; $i ++)
+{
+ test_insert($i,$i) if (($pid=fork()) == 0); $work{$pid}="insert_one";
+}
+for ($i=0 ; $i < $opt_threads ; $i ++)
+{
+ test_select() if (($pid=fork()) == 0); $work{$pid}="select_key";
+}
+test_join() if (($pid=fork()) == 0); $work{$pid}="test_join";
+test_select_count() if (($pid=fork()) == 0); $work{$pid}="select_count";
+test_delete() if (($pid=fork()) == 0); $work{$pid}="delete";
+test_update() if (($pid=fork()) == 0); $work{$pid}="update";
+test_flush() if (($pid=fork()) == 0); $work{$pid}= "flush";
+test_check() if (($pid=fork()) == 0); $work{$pid}="check";
+test_repair() if (($pid=fork()) == 0); $work{$pid}="repair";
+test_alter() if (($pid=fork()) == 0); $work{$pid}="alter";
+#test_database("test2") if (($pid=fork()) == 0); $work{$pid}="check_database";
+
+print "Started " . ($opt_threads*2+4) . " threads\n";
+
+my ($errors, $running_insert_threads);
+
+$errors=0;
+$running_insert_threads=$opt_threads+$numtables;
+while (($pid=wait()) != -1)
+{
+ my ($ret);
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ if ($work{$pid} =~ /^insert/)
+ {
+ if (!--$running_insert_threads)
+ {
+ # Time to stop other threads
+ signal_abort();
+ }
+ }
+ $errors++ if ($ret != 0);
+}
+
+#
+# Cleanup
+#
+
+if (!$opt_skip_delete && !$errors)
+{
+ my $table_def;
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $dbh->do("drop table $abort_table");
+ foreach $table_def (@testtables)
+ {
+ $dbh->do("drop table " . $table_def->[0]);
+ }
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+
+print ($errors ? "Test failed\n" :"Test ok\n");
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+
+#
+# Insert records in the table
+#
+
+sub test_insert
+{
+ my ($from_table,$to_table)= @_;
+ my ($dbh,$i,$j,$count,$table_def,$table);
+
+ if (!defined($from_table))
+ {
+ $from_table=0; $to_table=$numtables-1;
+ }
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($i=$count=0 ; $i < $opt_loop_count; $i++)
+ {
+ for ($j= $from_table ; $j <= $to_table ; $j++)
+ {
+ my ($table)= ($testtables[$j]->[0]);
+ $dbh->do("insert into $table values (NULL,'This is entry $i','',0)") || die "Got error on insert: $DBI::errstr\n";
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_insert: Inserted $count rows\n";
+ exit(0);
+}
+
+
+#
+# select records
+# Do continously select over all tables as long as there is changed
+# rows in the table
+#
+
+sub test_select
+{
+ my ($dbh, $i, $j, $count, $loop, $count_query, $row_counts);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query($numtables);
+ $count=0;
+ $loop=9999;
+
+ $i=0;
+ while (($i++ % 100) || !test_if_abort($dbh))
+ {
+ if ($loop++ >= 100)
+ {
+ $loop=0;
+ $row_counts=simple_query($dbh, $count_query);
+ }
+ for ($j=0 ; $j < $numtables ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($table)= $testtables[$j]->[0];
+ simple_query($dbh, "select id,info from $table where id=$id");
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: Executed $count selects\n";
+ exit(0);
+}
+
+#
+# Do big select count(distinct..) over the table
+#
+
+sub test_select_count
+{
+ my ($dbh, $i, $j, $count, $loop);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count=0;
+ $i=0;
+ while (!test_if_abort($dbh))
+ {
+ for ($j=0 ; $j < $numtables ; $j++)
+ {
+ my ($table)= $testtables[$j]->[0];
+ simple_query($dbh, "select count(distinct marker),count(distinct id),count(distinct info) from $table");
+ $count++;
+ }
+ sleep(20); # This query is quite slow
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: Executed $count select count(distinct) queries\n";
+ exit(0);
+}
+
+#
+# select records
+# Do continously joins between the first and second table
+#
+
+sub test_join
+{
+ my ($dbh, $i, $j, $count, $loop, $count_query, $row_counts);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query($numtables);
+ $count=0;
+ $loop=9999;
+
+ $i=0;
+ while (($i++ % 100) || !test_if_abort($dbh))
+ {
+ if ($loop++ >= 100)
+ {
+ $loop=0;
+ $row_counts=simple_query($dbh, $count_query);
+ }
+ for ($j=0 ; $j < $numtables-1 ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($t1,$t2)= ($testtables[$j]->[0],$testtables[$j+1]->[0]);
+ simple_query($dbh, "select $t1.id,$t2.info from $t1, $t2 where $t1.id=$t2.id and $t1.id=$id");
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_join: Executed $count joins\n";
+ exit(0);
+}
+
+#
+# Delete 1-5 rows from the first 2 tables.
+# Test ends when the number of rows for table 3 didn't change during
+# one loop
+#
+
+sub test_delete
+{
+ my ($dbh, $i,$j, $row_counts, $count_query, $table_count, $count);
+
+ $table_count=2;
+ $count=0;
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query($table_count+1);
+
+ sleep(5); # Give time to insert some rows
+ $i=0;
+ while (($i++ % 10) || !test_if_abort($dbh))
+ {
+ sleep(1);
+ $row_counts=simple_query($dbh, $count_query);
+
+ for ($j=0 ; $j < $table_count ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($table)= $testtables[$j]->[0];
+ $dbh->do("delete from $table where id >= $id-2 and id <= $id +2") || die "Got error on delete from $table: $DBI::errstr\n";
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_delete: Executed $count deletes\n";
+ exit(0);
+}
+
+#
+# Update the flag for table 2 and 3
+# Will abort after a while when table1 doesn't change max value
+#
+
+sub test_update
+{
+ my ($dbh, $i, $j, $row_counts, $count_query, $count, $loop);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query(3);
+ $loop=9999;
+ $count=0;
+
+ sleep(5); # Give time to insert some rows
+ $i=0;
+ while (($i++ % 100) || !test_if_abort($dbh))
+ {
+ if ($loop++ >= 100)
+ {
+ $loop=0;
+ $row_counts=simple_query($dbh, $count_query);
+ }
+
+ for ($j=1 ; $j <= 2 ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($table)= $testtables[$j]->[0];
+ # Fix to not change the same rows as the above delete
+ $id= ($id + $count) % $row_counts->[$j];
+
+ $dbh->do("update $table set flag=flag+1 where id >= $id-2 and id <= $id +2") || die "Got error on update of $table: $DBI::errstr\n";
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_update: Executed $count updates\n";
+ exit(0);
+}
+
+
+#
+# Run a check on all tables except the last one
+# (The last one is not checked to put pressure on the key cache)
+#
+
+sub test_check
+{
+ my ($dbh, $sth, $row, $i, $j, $type, $table);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $type= "check";
+ for ($i=$j=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(200);
+ $table=$testtables[$j]->[0];
+ $sth=$dbh->prepare("$type table $table") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+
+ while (($row=$sth->fetchrow_arrayref))
+ {
+ if ($row->[3] ne "OK")
+ {
+ print "Got error " . $row->[3] . " when doing $type on $table\n";
+ exit(1);
+ }
+ }
+ if (++$j == $numtables-1)
+ {
+ $j=0;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_check: Executed $i checks\n";
+ exit(0);
+}
+
+#
+# Do a repair on the first table once in a while
+#
+
+sub test_repair
+{
+ my ($dbh, $sth, $row, $i, $type, $table);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $type= "repair";
+ for ($i=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(100);
+ $table=$testtables[0]->[0];
+ $sth=$dbh->prepare("$type table $table") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+
+ while (($row=$sth->fetchrow_arrayref))
+ {
+ if ($row->[3] ne "OK")
+ {
+ print "Got error " . $row->[3] . " when doing $type on $table\n";
+ exit(1);
+ }
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_repair: Executed $i repairs\n";
+ exit(0);
+}
+
+#
+# Do a flush tables on table 3 and 4 once in a while
+#
+
+sub test_flush
+{
+ my ($dbh,$count,$tables);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $tables=$testtables[2]->[0] . "," . $testtables[3]->[0];
+
+ $count=0;
+ while (!test_if_abort($dbh))
+ {
+ sleep(300);
+ $dbh->do("flush tables $tables") ||
+ die "Got error on flush $DBI::errstr\n";
+ $count++;
+ }
+ $dbh->disconnect; $dbh=0;
+ print "flush: Executed $count flushs\n";
+ exit(0);
+}
+
+
+#
+# Test all tables in a database
+#
+
+sub test_database
+{
+ my ($database) = @_;
+ my ($dbh, $sth, $row, $i, $type, $tables);
+ $dbh = DBI->connect("DBI:MariaDB:$database:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $tables= join(',',$dbh->func('_ListTables'));
+ $type= "check";
+ for ($i=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(120);
+ $sth=$dbh->prepare("$type table $tables") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+
+ while (($row=$sth->fetchrow_arrayref))
+ {
+ if ($row->[3] ne "OK")
+ {
+ print "Got error " . $row->[2] . " " . $row->[3] . " when doing $type on " . $row->[0] . "\n";
+ exit(1);
+ }
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_check: Executed $i checks\n";
+ exit(0);
+}
+
+#
+# Test ALTER TABLE on the second table
+#
+
+sub test_alter
+{
+ my ($dbh, $sth, $row, $i, $type, $table);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($i=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(100);
+ $table=$testtables[1]->[0];
+ $sth=$dbh->prepare("ALTER table $table modify info char(32)") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_alter: Executed $i ALTER TABLE\n";
+ exit(0);
+}
+
+
+#
+# Help functions
+#
+
+sub signal_abort
+{
+ my ($dbh);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $dbh->do("insert into $abort_table values(1)") || die $DBI::errstr;
+ $dbh->disconnect; $dbh=0;
+ exit(0);
+}
+
+
+sub test_if_abort()
+{
+ my ($dbh)=@_;
+ my ($row);
+ $row=simple_query($dbh,"select * from $opt_db.$abort_table");
+ return (defined($row) && defined($row->[0]) != 0) ? 1 : 0;
+}
+
+
+sub make_count_query
+{
+ my ($table_count)= @_;
+ my ($tables, $count_query, $i, $table_def);
+ $tables="";
+ $count_query="select high_priority ";
+ $table_count--;
+ for ($i=0 ; $i < $table_count ; $i++)
+ {
+ my ($table_def)= $testtables[$i];
+ $tables.=$table_def->[0] . ",";
+ $count_query.= "max(" . $table_def->[0] . ".id),";
+ }
+ $table_def=$testtables[$table_count];
+ $tables.=$table_def->[0];
+ $count_query.= "max(" . $table_def->[0] . ".id) from $tables";
+ return $count_query;
+}
+
+sub simple_query()
+{
+ my ($dbh, $query)= @_;
+ my ($sth,$row);
+
+ $sth=$dbh->prepare($query) || die "Got error on '$query': " . $dbh->errstr . "\n";
+ $sth->execute || die "Got error on '$query': " . $dbh->errstr . "\n";
+ $row= $sth->fetchrow_arrayref();
+ $sth=0;
+ return $row;
+}
diff --git a/tests/fork_big2.pl b/tests/fork_big2.pl
new file mode 100644
index 00000000..bb98b8c8
--- /dev/null
+++ b/tests/fork_big2.pl
@@ -0,0 +1,754 @@
+#!/usr/bin/env perl
+
+# Copyright (c) 2002, 2003, 2005, 2006 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# This is a test with uses many processes to test a MySQL server.
+#
+# Tested a lot with: --threads=30
+
+$opt_loop_count=500000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+$opt_lock_tables=$opt_debug=$opt_skip_drop=$opt_fast=$opt_force=0;
+$opt_thread_factor=1;
+$opt_insert=1;
+$opt_select=6;$opt_join=4;
+$opt_select_count=$opt_join_count=0;
+$opt_update=1;$opt_delete=0;
+$opt_flush=$opt_check=$opt_repair=$opt_alter=0;
+$opt_join_range=100;
+$opt_resize_interval=0;
+$opt_time=0;
+$opt_host=$opt_user=$opt_password=""; $opt_db="test";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+GetOptions("host=s","db=s","user=s","password=s","loop-count=i","skip-create","skip-in","skip-drop",
+ "verbose","fast-insert","lock-tables","debug","fast","force","thread-factor=i",
+ "insert=i", "select=i", "join=i", "select-count=i", "join-count=i", "update=i", "delete=i",
+ "flush=i", "check=i", "repair=i", "alter=i", "resize-interval=i", "max-join_range=i", "time=i") || die "Aborted";
+
+print "Test of multiple connections that test the following things:\n";
+print "insert, select, delete, update, alter, check, repair and flush\n";
+
+@testtables = ( ["bench_f31", ""],
+ ["bench_f32", "row_format=fixed"],
+ ["bench_f33", "delay_key_write=1"],
+ ["bench_f34", "checksum=1"],
+ ["bench_f35", "delay_key_write=1"]);
+$abort_table="bench_f39";
+
+$numtables = $#testtables+1;
+srand 100; # Make random numbers repeatable
+
+####
+#### Start timeing and start test
+####
+
+$opt_insert*=$opt_thread_factor;
+$opt_select*=$opt_thread_factor;
+$opt_join*=$opt_thread_factor;
+$opt_select_count*=$opt_thread_factor;
+$opt_join_count*=$opt_thread_factor;
+$opt_update*=$opt_thread_factor;
+$opt_delete*=$opt_thread_factor;
+
+if ($opt_time == 0 && $opt_insert == 0)
+{
+ $opt_insert=1;
+}
+
+$start_time=new Benchmark;
+$dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+if (!$opt_skip_create)
+{
+ my $table_def;
+ foreach $table_def (@testtables)
+ {
+ my ($table,$extra)= ($table_def->[0], $table_def->[1]);
+ print "Creating table $table in database $opt_db\n";
+ $dbh->do("drop table if exists $table");
+ $dbh->do("create table $table".
+ " (id int(6) not null auto_increment,".
+ " info varchar(32)," .
+ " marker timestamp," .
+ " flag int not null," .
+ " primary key(id)) $extra")
+
+ or die $DBI::errstr;
+ # One row in the table will make future tests easier
+ $dbh->do("insert into $table (id) values (null)")
+ or die $DBI::errstr;
+ }
+ # Create the table we use to signal that we should end the test
+ $dbh->do("drop table if exists $abort_table");
+ $dbh->do("create table $abort_table (id int(6) not null) ENGINE=heap") ||
+ die $DBI::errstr;
+}
+
+$dbh->do("delete from $abort_table");
+$dbh->disconnect; $dbh=0; # Close handler
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+if ($opt_time != 0)
+{
+ test_abort() if (($pid=fork()) == 0); $work{$pid}="abort";
+}
+for ($i=0 ; $i < $opt_insert ; $i ++)
+{
+ test_insert() if (($pid=fork()) == 0); $work{$pid}="insert";
+}
+$threads=$i;
+for ($i=0 ; $i < $opt_select ; $i ++)
+{
+ test_select() if (($pid=fork()) == 0); $work{$pid}="select";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_join ; $i ++)
+{
+ test_join() if (($pid=fork()) == 0); $work{$pid}="join";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_select_count ; $i ++)
+{
+ test_select_count() if (($pid=fork()) == 0); $work{$pid}="select_count";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_join_count ; $i ++)
+{
+ test_join_count() if (($pid=fork()) == 0); $work{$pid}="join_count";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_update ; $i ++)
+{
+ test_update() if (($pid=fork()) == 0); $work{$pid}="update";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_delete ; $i ++)
+{
+ test_delete() if (($pid=fork()) == 0); $work{$pid}="delete";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_flush ; $i ++)
+{
+ test_flush() if (($pid=fork()) == 0); $work{$pid}="flush";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_check ; $i ++)
+{
+ test_check() if (($pid=fork()) == 0); $work{$pid}="check";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_repair ; $i ++)
+{
+ test_repair() if (($pid=fork()) == 0); $work{$pid}="repair";
+}
+$threads+=$i;
+for ($i=0 ; $i < $opt_alter ; $i ++)
+{
+ test_alter() if (($pid=fork()) == 0); $work{$pid}="alter";
+}
+$threads+=$i;
+if ($opt_resize_interval != 0)
+{
+ test_resize() if (($pid=fork()) == 0); $work{$pid}="resize";
+ $threads+=1;
+}
+
+print "Started $threads threads\n";
+
+$errors=0;
+$running_insert_threads=$opt_insert;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ if ($opt_time == 0)
+ {
+ if ($work{$pid} =~ /^insert/)
+ {
+ if (!--$running_insert_threads)
+ {
+
+ # Time to stop other threads
+ signal_abort();
+ }
+ }
+ }
+ $errors++ if ($ret != 0);
+}
+
+#
+# Cleanup
+#
+
+if (!$opt_skip_drop && !$errors)
+{
+ my $table_def;
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $dbh->do("drop table $abort_table");
+ foreach $table_def (@testtables)
+ {
+ $dbh->do("drop table " . $table_def->[0]);
+ }
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+
+print ($errors ? "Test failed\n" :"Test ok\n");
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+#
+# Sleep and then abort other threads
+#
+
+sub test_abort
+{
+ sleep($opt_time);
+ signal_abort();
+ exit(0);
+}
+
+
+#
+# Insert records in the table
+#
+
+sub test_insert
+{
+ my ($from_table,$to_table)= @_;
+ my ($dbh,$i,$j,$count,$table_def,$table);
+
+ if (!defined($from_table))
+ {
+ $from_table=0; $to_table=$numtables-1;
+ }
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($i=$count=0 ; $i < $opt_loop_count; $i++)
+ {
+ for ($j= $from_table ; $j <= $to_table ; $j++)
+ {
+ my ($table)= ($testtables[$j]->[0]);
+ $dbh->do("insert into $table values (NULL,'This is entry $i','',0)") || die "Got error on insert: $DBI::errstr\n";
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_insert: Inserted $count rows\n";
+ exit(0);
+}
+
+
+#
+# select records
+# Do continously select over all tables as long as there is changed
+# rows in the table
+#
+
+sub test_select
+{
+ my ($dbh, $i, $j, $count, $loop);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query($numtables);
+ $count=0;
+ $loop=9999;
+
+ $i=0;
+ while (($i++ % 100) || !test_if_abort($dbh))
+ {
+ if ($loop++ >= 100)
+ {
+ $loop=0;
+ $row_counts=simple_query($dbh, $count_query);
+ }
+ for ($j=0 ; $j < $numtables ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($table)= $testtables[$j]->[0];
+ simple_query($dbh, "select id,info from $table where id=$id");
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: Executed $count selects\n";
+ exit(0);
+}
+
+#
+# Do big select count(distinct..) over the table
+#
+
+sub test_select_count
+{
+ my ($dbh, $i, $j, $count, $loop);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count=0;
+ $i=0;
+ while (!test_if_abort($dbh))
+ {
+ for ($j=0 ; $j < $numtables ; $j++)
+ {
+ my ($table)= $testtables[$j]->[0];
+ simple_query($dbh, "select count(distinct marker),count(distinct id),count(distinct info) from $table");
+ $count++;
+ }
+ sleep(20); # This query is quite slow
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: Executed $count select count(distinct) queries\n";
+ exit(0);
+}
+
+#
+# select records
+# Do continously joins between the first and second table
+#
+
+sub test_join
+{
+ my ($dbh, $i, $j, $count, $loop);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query($numtables);
+ $count=0;
+ $loop=9999;
+
+ $i=0;
+ while (($i++ % 100) || !test_if_abort($dbh))
+ {
+ if ($loop++ >= 100)
+ {
+ $loop=0;
+ $row_counts=simple_query($dbh, $count_query);
+ }
+ for ($j=0 ; $j < $numtables-1 ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($t1,$t2)= ($testtables[$j]->[0],$testtables[$j+1]->[0]);
+ simple_query($dbh, "select $t1.id,$t2.info from $t1, $t2 where $t1.id=$t2.id and $t1.id=$id");
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_join: Executed $count joins\n";
+ exit(0);
+}
+
+#
+# select records
+# Do continously joins between the first and second for range and count selected rows
+#
+
+sub test_join_count
+{
+ my ($dbh, $i, $j, $count, $loop);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query($numtables);
+ $count=0;
+ $loop=9999;
+ $sum=0;
+
+ srand();
+
+ $i=0;
+ while (($i++ % 10) || !test_if_abort($dbh))
+ {
+ if ($loop++ >= 10)
+ {
+ $loop=0;
+ $row_counts=simple_query($dbh, $count_query);
+ }
+ for ($j=0 ; $j < $numtables-1 ; $j++)
+ {
+ my ($id1)= int rand $row_counts->[$j];
+ my ($id2)= int rand $row_counts->[$j];
+ if ($id1 > $id2)
+ {
+ my $id0=$id1; $id1=$id2; $id2=$id0;
+ if ($id2-$id1 > $opt_join_range)
+ {
+ $id2=$id1+$opt_join_range;
+ }
+ }
+ my ($t1,$t2)= ($testtables[$j]->[0],$testtables[$j+1]->[0]);
+ $row=simple_query($dbh, "select count(*) from $t1, $t2 where $t1.id=$t2.id and $t1.id between $id1 and $id2");
+ $sum+=$row->[0];
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_join_count: Executed $count joins: total $sum rows\n";
+ exit(0);
+}
+
+
+#
+# Delete 1-5 rows from the first 2 tables.
+# Test ends when the number of rows for table 3 didn't change during
+# one loop
+#
+
+sub test_delete
+{
+ my ($dbh, $i,$j, $row_counts, $count_query, $table_count, $count);
+
+ $table_count=2;
+ $count=0;
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query($table_count+1);
+
+ sleep(5); # Give time to insert some rows
+ $i=0;
+ while (($i++ % 10) || !test_if_abort($dbh))
+ {
+ sleep(1);
+ $row_counts=simple_query($dbh, $count_query);
+
+ for ($j=0 ; $j < $table_count ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($table)= $testtables[$j]->[0];
+ $dbh->do("delete from $table where id >= $id-2 and id <= $id +2") || die "Got error on delete from $table: $DBI::errstr\n";
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_delete: Executed $count deletes\n";
+ exit(0);
+}
+
+#
+# Update the flag for table 2 and 3
+# Will abort after a while when table1 doesn't change max value
+#
+
+sub test_update
+{
+ my ($dbh, $i, $j, $row_counts, $count_query, $count, $loop);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count_query=make_count_query(3);
+ $loop=9999;
+ $count=0;
+
+ sleep(5); # Give time to insert some rows
+ $i=0;
+ while (($i++ % 100) || !test_if_abort($dbh))
+ {
+ if ($loop++ >= 100)
+ {
+ $loop=0;
+ $row_counts=simple_query($dbh, $count_query);
+ }
+
+ for ($j=1 ; $j <= 2 ; $j++)
+ {
+ my ($id)= int rand $row_counts->[$j];
+ my ($table)= $testtables[$j]->[0];
+ # Fix to not change the same rows as the above delete
+ $id= ($id + $count) % $row_counts->[$j];
+
+ $dbh->do("update $table set flag=flag+1 where id >= $id-2 and id <= $id +2") || die "Got error on update of $table: $DBI::errstr\n";
+ $count++;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_update: Executed $count updates\n";
+ exit(0);
+}
+
+
+#
+# Run a check on all tables except the last one
+# (The last one is not checked to put pressure on the key cache)
+#
+
+sub test_check
+{
+ my ($dbh, $row, $i, $j, $type, $table);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $type= "check";
+ for ($i=$j=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(1000);
+ $table=$testtables[$j]->[0];
+ $sth=$dbh->prepare("$type table $table") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+
+ while (($row=$sth->fetchrow_arrayref))
+ {
+ if ($row->[3] ne "OK")
+ {
+ print "Got error " . $row->[3] . " when doing $type on $table\n";
+ exit(1);
+ }
+ }
+ if (++$j == $numtables-1)
+ {
+ $j=0;
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_check: Executed $i checks\n";
+ exit(0);
+}
+
+#
+# Do a repair on the first table once in a while
+#
+
+sub test_repair
+{
+ my ($dbh, $row, $i, $type, $table);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $type= "repair";
+ for ($i=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(2000);
+ $table=$testtables[0]->[0];
+ $sth=$dbh->prepare("$type table $table") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+
+ while (($row=$sth->fetchrow_arrayref))
+ {
+ if ($row->[3] ne "OK")
+ {
+ print "Got error " . $row->[3] . " when doing $type on $table\n";
+ exit(1);
+ }
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_repair: Executed $i repairs\n";
+ exit(0);
+}
+
+#
+# Do a flush tables on table 3 and 4 once in a while
+#
+
+sub test_flush
+{
+ my ($dbh,$count,$tables);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $tables=$testtables[2]->[0] . "," . $testtables[3]->[0];
+
+ $count=0;
+ while (!test_if_abort($dbh))
+ {
+ sleep(3000);
+ $dbh->do("flush tables $tables") ||
+ die "Got error on flush $DBI::errstr\n";
+ $count++;
+ }
+ $dbh->disconnect; $dbh=0;
+ print "flush: Executed $count flushs\n";
+ exit(0);
+}
+
+#
+# Do a resize key cache every periodically
+#
+
+sub test_resize
+{
+ my ($dbh, $key_buffer_size);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $count=0;
+ $key_buffer_size=1024*64;
+ while (!test_if_abort($dbh))
+ {
+ sleep($opt_resize_interval);
+ $dbh->do("set global key_buffer_size=$key_buffer_size") ||
+ die "Got error on resize key cache $DBI::errstr\n";
+ $key_buffer_size+=1024*16;
+ $count++;
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_resize: Executed $count times resize key cache\n";
+ exit(0);
+}
+
+#
+# Test all tables in a database
+#
+
+sub test_database
+{
+ my ($database) = @_;
+ my ($dbh, $row, $i, $type, $tables);
+ $dbh = DBI->connect("DBI:MariaDB:$database:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $tables= join(',',$dbh->func('_ListTables'));
+ $type= "check";
+ for ($i=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(120);
+ $sth=$dbh->prepare("$type table $tables") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+
+ while (($row=$sth->fetchrow_arrayref))
+ {
+ if ($row->[3] ne "OK")
+ {
+ print "Got error " . $row->[2] . " " . $row->[3] . " when doing $type on " . $row->[0] . "\n";
+ exit(1);
+ }
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_check: Executed $i checks\n";
+ exit(0);
+}
+
+#
+# Test ALTER TABLE on the second table
+#
+
+sub test_alter
+{
+ my ($dbh, $row, $i, $type, $table);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($i=0 ; !test_if_abort($dbh) ; $i++)
+ {
+ sleep(100);
+ $table=$testtables[1]->[0];
+ $sth=$dbh->prepare("ALTER table $table modify info char(32)") || die "Got error on prepare: $DBI::errstr\n";
+ $sth->execute || die $DBI::errstr;
+ }
+ $dbh->disconnect; $dbh=0;
+ print "test_alter: Executed $i ALTER TABLE\n";
+ exit(0);
+}
+
+
+#
+# Help functions
+#
+
+sub signal_abort
+{
+ my ($dbh);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $dbh->do("insert into $abort_table values(1)") || die $DBI::errstr;
+ $dbh->disconnect; $dbh=0;
+}
+
+
+sub test_if_abort()
+{
+ my ($dbh)=@_;
+ $row=simple_query($dbh,"select * from $opt_db.$abort_table");
+ return (defined($row) && defined($row->[0]) != 0) ? 1 : 0;
+}
+
+
+sub make_count_query
+{
+ my ($table_count)= @_;
+ my ($tables, $count_query, $i, $tables_def);
+ $tables="";
+ $count_query="select high_priority ";
+ $table_count--;
+ for ($i=0 ; $i < $table_count ; $i++)
+ {
+ my ($table_def)= $testtables[$i];
+ $tables.=$table_def->[0] . ",";
+ $count_query.= "max(" . $table_def->[0] . ".id),";
+ }
+ $table_def=$testtables[$table_count];
+ $tables.=$table_def->[0];
+ $count_query.= "max(" . $table_def->[0] . ".id) from $tables";
+ return $count_query;
+}
+
+sub simple_query()
+{
+ my ($dbh, $query)= @_;
+ my ($sth,$row);
+
+ $sth=$dbh->prepare($query) || die "Got error on '$query': " . $dbh->errstr . "\n";
+ $sth->execute || die "Got error on '$query': " . $dbh->errstr . "\n";
+ $row= $sth->fetchrow_arrayref();
+ $sth=0;
+ return $row;
+}
diff --git a/tests/function.res b/tests/function.res
new file mode 100644
index 00000000..acd34f41
--- /dev/null
+++ b/tests/function.res
@@ -0,0 +1,258 @@
+--------------
+select 1+1,1-1,1+1*2,8/5,8%5,mod(8,5),mod(8,5)|0,-(1+1)*-2,sign(-5)
+--------------
+
+1+1 1-1 1+1*2 8/5 8%5 mod(8,5) mod(8,5)|0 -(1+1)*-2 sign(-5)
+2 0 3 1.60 3 3 3 4 -1
+--------------
+select floor(5.5),floor(-5.5),ceiling(5.5),ceiling(-5.5),round(5.5),round(-5.5)
+--------------
+
+floor(5.5) floor(-5.5) ceiling(5.5) ceiling(-5.5) round(5.5) round(-5.5)
+5 -6 6 -5 6 -6
+--------------
+select abs(-10),log(exp(10)),ln(exp(10)),log2(65535),log(2,65535),exp(log(sqrt(10))*2),pow(10,log10(10)),rand(999999),rand()
+--------------
+
+abs(-10) log(exp(10)) ln(exp(10)) log2(65535) log(2,65535) exp(log(sqrt(10))*2) pow(10,log10(10)) rand(999999) rand()
+10 10.000000 10.000000 2.000000 2.000000 10.000000 10.000000 0.1844 0.7637
+--------------
+select least(6,1.0,2.0),greatest(3,4,5,0)
+--------------
+
+least(6,1.0,2.0) greatest(3,4,5,0)
+1.0 5
+--------------
+select 1 | (1+1),5 & 3,bit_count(7)
+--------------
+
+1 | (1+1) 5 & 3 bit_count(7)
+3 1 3
+--------------
+select 0=0,1>0,1>=1,1<0,1<=0,strcmp("abc","abcd"),strcmp("b","a"),strcmp("a","a")
+--------------
+
+0=0 1>0 1>=1 1<0 1<=0 strcmp("abc","abcd") strcmp("b","a") strcmp("a","a")
+1 1 1 0 0 -1 1 0
+--------------
+select "a"<"b","a"<="b","b">="a","b">"a","a"="A","a"<>"b"
+--------------
+
+"a"<"b" "a"<="b" "b">="a" "b">"a" "a"="A" "a"<>"b"
+1 1 1 1 1 1
+--------------
+select "abc" like "a%", "abc" not like "%d%", "ab" like "a\%", "a%" like "a\%","abcd" like "a%b_%d"
+--------------
+
+"abc" like "a%" "abc" not like "%d%" "ab" like "a\%" "a%" like "a\%" "abcd" like "a%b_%d"
+1 1 0 1 1
+--------------
+select "Det här är svenska" regexp "h[[:alpha:]]+r", "aba" regexp "^(a|b)*$"
+--------------
+
+"Det här är svenska" regexp "h[[:alpha:]]+r" "aba" regexp "^(a|b)*$"
+1 1
+--------------
+select !0,NOT 0=1,!(0=0),1 AND 1,1 && 0,0 OR 1,1 || NULL, 1=1 or 1=1 and 1=0
+--------------
+
+!0 NOT 0=1 !(0=0) 1 AND 1 1 && 0 0 OR 1 1 || NULL 1=1 or 1=1 and 1=0
+1 1 0 1 0 1 1 1
+--------------
+select IF(0,"ERROR","this"),IF(1,"is","ERROR"),IF(NULL,"ERROR","a"),IF(1,2,3)|0,IF(1,2.0,3.0)+0
+--------------
+
+IF(0,"ERROR","this") IF(1,"is","ERROR") IF(NULL,"ERROR","a") IF(1,2,3)|0 IF(1,2.0,3.0)+0
+this is a 2 2.0
+--------------
+select 2 between 1 and 3, "monty" between "max" and "my",2=2 and "monty" between "max" and "my" and 3=3
+--------------
+
+2 between 1 and 3 "monty" between "max" and "my" 2=2 and "monty" between "max" and "my" and 3=3
+1 1 1
+--------------
+select 2 in (3,2,5,9,5,1),"monty" in ("david","monty","allan"), 1.2 in (1.4,1.2,1.0)
+--------------
+
+2 in (3,2,5,9,5,1) "monty" in ("david","monty","allan") 1.2 in (1.4,1.2,1.0)
+1 1 1
+--------------
+select 'hello',"'hello'",'""hello""','''h''e''l''l''o''',"hel""lo",'hel\'lo'
+--------------
+
+hello 'hello' ""hello"" 'h'e'l'l'o' hel"lo hel'lo
+hello 'hello' ""hello"" 'h'e'l'l'o' hel"lo hel'lo
+--------------
+select concat("monty"," was here ","again"),length("hello"),ascii("hello")
+--------------
+
+concat("monty"," was here ","again") length("hello") ascii("hello")
+monty was here again 5 104
+--------------
+select locate("he","hello"),locate("he","hello",2),locate("lo","hello",2)
+--------------
+
+locate("he","hello") locate("he","hello",2) locate("lo","hello",2)
+1 0 4
+--------------
+select left("hello",2),right("hello",2),substring("hello",2,2),mid("hello",1,5)
+--------------
+
+left("hello",2) right("hello",2) substring("hello",2,2) mid("hello",1,5)
+he lo el hello
+--------------
+select concat("",left(right(concat("what ",concat("is ","happening")),9),4),"",substring("monty",5,1))
+--------------
+
+concat("",left(right(concat("what ",concat("is ","happening")),9),4),"",substring("monty",5,1))
+happy
+--------------
+select concat("!",ltrim(" left "),"!",rtrim(" right "),"!")
+--------------
+
+concat("!",ltrim(" left "),"!",rtrim(" right "),"!")
+!left ! right!
+--------------
+select insert("txs",2,1,"hi"),insert("is ",4,0,"a"),insert("txxxxt",2,4,"es")
+--------------
+
+insert("txs",2,1,"hi") insert("is ",4,0,"a") insert("txxxxt",2,4,"es")
+this is a test
+--------------
+select replace("aaaa","a","b"),replace("aaaa","aa","b"),replace("aaaa","a","bb"),replace("aaaa","","b"),replace("bbbb","a","c")
+--------------
+
+replace("aaaa","a","b") replace("aaaa","aa","b") replace("aaaa","a","bb") replace("aaaa","","b") replace("bbbb","a","c")
+bbbb bb bbbbbbbb aaaa bbbb
+--------------
+select replace(concat(lcase(concat("THIS"," ","IS"," ","A"," ")),ucase("false")," ","test"),"FALSE","REAL")
+--------------
+
+replace(concat(lcase(concat("THIS"," ","IS"," ","A"," ")),ucase("false")," ","test"),"FALSE","REAL")
+this is a REAL test
+--------------
+select soundex(""),soundex("he"),soundex("hello all folks")
+--------------
+
+soundex("") soundex("he") soundex("hello all folks")
+ H000 H4142
+--------------
+select password("test")
+--------------
+
+password("test")
+378b243e220ca493
+--------------
+select 0x41,0x41+0,0x41 | 0x7fffffffffffffff | 0,0xffffffffffffffff | 0
+--------------
+
+0x41 0x41+0 0x41 | 0x7fffffffffffffff | 0 0xffffffffffffffff | 0
+A 65 9223372036854775807 -1
+--------------
+select interval(55,10,20,30,40,50,60,70,80,90,100),interval(3,1,1+1,1+1+1+1),field("IBM","NCA","ICL","SUN","IBM","DIGITAL"),field("A","B","C"),elt(2,"ONE","TWO","THREE"),interval(0,1,2,3,4),elt(1,1,2,3)|0,elt(1,1.1,1.2,1.3)+0
+--------------
+
+interval(55,10,20,30,40,50,60,70,80,90,100) interval(3,1,1+1,1+1+1+1) field("IBM","NCA","ICL","SUN","IBM","DIGITAL") field("A","B","C") elt(2,"ONE","TWO","THREE") interval(0,1,2,3,4) elt(1,1,2,3)|0 elt(1,1.1,1.2,1.3)+0
+5 2 4 0 TWO 0 1 1.1
+--------------
+select format(1.5555,0),format(123.5555,1),format(1234.5555,2),format(12345.5555,3),format(123456.5555,4),format(1234567.5555,5),format("12345.2399",2)
+--------------
+
+format(1.5555,0) format(123.5555,1) format(1234.5555,2) format(12345.5555,3) format(123456.5555,4) format(1234567.5555,5) format("12345.2399",2)
+2 123.6 1,234.56 12,345.556 123,456.5555 1,234,567.55550 12,345.24
+--------------
+select database(),user()
+--------------
+
+database() user()
+ monty
+--------------
+select null,isnull(null),isnull(1/0),isnull(1/0 = null),ifnull(null,1),ifnull(null,"TRUE"),ifnull("TRUE","ERROR"),1/0 is null,1 is not null
+--------------
+
+NULL isnull(null) isnull(1/0) isnull(1/0 = null) ifnull(null,1) ifnull(null,"TRUE") ifnull("TRUE","ERROR") 1/0 is null 1 is not null
+NULL 1 1 1 1 TRUE TRUE 1 1
+--------------
+select 1 | NULL,1 & NULL,1+NULL,1-NULL
+--------------
+
+1 | NULL 1 & NULL 1+NULL 1-NULL
+NULL NULL NULL NULL
+--------------
+select NULL=NULL,NULL<>NULL,NULL IS NULL, NULL IS NOT NULL,IFNULL(NULL,1.1)+0,IFNULL(NULL,1) | 0
+--------------
+
+NULL=NULL NULL<>NULL NULL IS NULL NULL IS NOT NULL IFNULL(NULL,1.1)+0 IFNULL(NULL,1) | 0
+NULL NULL 1 0 1.1 1
+--------------
+select strcmp("a",NULL),(1<NULL)+0.0,NULL regexp "a",null like "a%","a%" like null
+--------------
+
+strcmp("a",NULL) (1<NULL)+0.0 NULL regexp "a" null like "a%" "a%" like null
+NULL NULL NULL NULL NULL
+--------------
+select concat("a",NULL),replace(NULL,"a","b"),replace("string","i",NULL),replace("string",NULL,"i"),insert("abc",1,1,NULL),left(NULL,1)
+--------------
+
+concat("a",NULL) replace(NULL,"a","b") replace("string","i",NULL) replace("string",NULL,"i") insert("abc",1,1,NULL) left(NULL,1)
+NULL NULL NULL NULL NULL NULL
+--------------
+select field(NULL,"a","b","c")
+--------------
+
+field(NULL,"a","b","c")
+0
+--------------
+select 2 between null and 1,2 between 3 AND NULL,NULL between 1 and 2,2 between NULL and 3, 2 between 1 AND null,2 between null and 1,2 between 3 AND NULL
+--------------
+
+2 between null and 1 2 between 3 AND NULL NULL between 1 and 2 2 between NULL and 3 2 between 1 AND null 2 between null and 1 2 between 3 AND NULL
+0 0 NULL NULL NULL 0 0
+--------------
+select insert("aa",100,1,"b"),insert("aa",1,3,"b"),left("aa",-1),substring("a",1,2)
+--------------
+
+insert("aa",100,1,"b") insert("aa",1,3,"b") left("aa",-1) substring("a",1,2)
+aa b a
+--------------
+select elt(2,1),field(NULL,"a","b","c")
+--------------
+
+elt(2,1) field(NULL,"a","b","c")
+NULL 0
+--------------
+select locate("a","b",2),locate("","a",1),ltrim("a"),rtrim("a")
+--------------
+
+locate("a","b",2) locate("","a",1) ltrim("a") rtrim("a")
+0 1 a a
+--------------
+select concat("1","2")|0,concat("1",".5")+0.0
+--------------
+
+concat("1","2")|0 concat("1",".5")+0.0
+12 1.5
+--------------
+select from_days(to_days("960101")),to_days(960201)-to_days("19960101"),to_days(curdate()+1)-to_days(curdate()),weekday("1997-01-01")
+--------------
+
+from_days(to_days("960101")) to_days(960201)-to_days("19960101") to_days(curdate()+1)-to_days(curdate()) weekday("1997-01-01")
+1996-01-01 31 1 2
+--------------
+select period_add("9602",-12),period_diff(199505,"9404")
+--------------
+
+period_add("9602",-12) period_diff(199505,"9404")
+199502 13
+--------------
+select now()-now(),weekday(curdate())-weekday(now()),unix_timestamp()-unix_timestamp(now())
+--------------
+
+now()-now() weekday(curdate())-weekday(now()) unix_timestamp()-unix_timestamp(now())
+0 0 0
+--------------
+select now(),now()+0,curdate(),weekday(curdate()),weekday(now()),unix_timestamp(),unix_timestamp(now())
+--------------
+
+now() now()+0 curdate() weekday(curdate()) weekday(now()) unix_timestamp() unix_timestamp(now())
+1998-08-17 04:24:33 19980817042433 1998-08-17 0 0 903317073 903317073
diff --git a/tests/function.tst b/tests/function.tst
new file mode 100644
index 00000000..17e1cb6c
--- /dev/null
+++ b/tests/function.tst
@@ -0,0 +1,80 @@
+# Test of functions
+#
+# mysql -v < this_file
+
+#
+# numerical functions
+#
+select 1+1,1-1,1+1*2,8/5,8%5,mod(8,5),mod(8,5)|0,-(1+1)*-2,sign(-5) ;
+select floor(5.5),floor(-5.5),ceiling(5.5),ceiling(-5.5),round(5.5),round(-5.5);
+select abs(-10),log(exp(10)),ln(exp(10)),log2(65535),log(2,65535),exp(log(sqrt(10))*2),pow(10,log10(10)),rand(999999),rand();
+select least(6,1.0,2.0),greatest(3,4,5,0) ;
+select 1 | (1+1),5 & 3,bit_count(7) ;
+#
+# test functions
+#
+select 0=0,1>0,1>=1,1<0,1<=0,strcmp("abc","abcd"),strcmp("b","a"),strcmp("a","a") ;
+select "a"<"b","a"<="b","b">="a","b">"a","a"="A","a"<>"b";
+select "abc" like "a%", "abc" not like "%d%", "ab" like "a\%", "a%" like "a\%","abcd" like "a%b_%d";
+select "Det här är svenska" regexp "h[[:alpha:]]+r", "aba" regexp "^(a|b)*$";
+select !0,NOT 0=1,!(0=0),1 AND 1,1 && 0,0 OR 1,1 || NULL, 1=1 or 1=1 and 1=0;
+select IF(0,"ERROR","this"),IF(1,"is","ERROR"),IF(NULL,"ERROR","a"),IF(1,2,3)|0,IF(1,2.0,3.0)+0 ;
+select 2 between 1 and 3, "monty" between "max" and "my",2=2 and "monty" between "max" and "my" and 3=3;
+select 2 in (3,2,5,9,5,1),"monty" in ("david","monty","allan"), 1.2 in (1.4,1.2,1.0);
+
+#
+# string functions
+#
+select 'hello',"'hello'",'""hello""','''h''e''l''l''o''',"hel""lo",'hel\'lo';
+select concat("monty"," was here ","again"),length("hello"),ascii("hello");
+select locate("he","hello"),locate("he","hello",2),locate("lo","hello",2) ;
+select left("hello",2),right("hello",2),substring("hello",2,2),mid("hello",1,5) ;
+select concat("",left(right(concat("what ",concat("is ","happening")),9),4),"",substring("monty",5,1)) ;
+select concat("!",ltrim(" left "),"!",rtrim(" right "),"!");
+select insert("txs",2,1,"hi"),insert("is ",4,0,"a"),insert("txxxxt",2,4,"es");
+select replace("aaaa","a","b"),replace("aaaa","aa","b"),replace("aaaa","a","bb"),replace("aaaa","","b"),replace("bbbb","a","c");
+select replace(concat(lcase(concat("THIS"," ","IS"," ","A"," ")),ucase("false")," ","test"),"FALSE","REAL") ;
+select soundex(""),soundex("he"),soundex("hello all folks");
+select password("test");
+#
+# varbinary as string and number
+#
+select 0x41,0x41+0,0x41 | 0x7fffffffffffffff | 0,0xffffffffffffffff | 0 ;
+
+#
+# misc functions
+#
+select interval(55,10,20,30,40,50,60,70,80,90,100),interval(3,1,1+1,1+1+1+1),field("IBM","NCA","ICL","SUN","IBM","DIGITAL"),field("A","B","C"),elt(2,"ONE","TWO","THREE"),interval(0,1,2,3,4),elt(1,1,2,3)|0,elt(1,1.1,1.2,1.3)+0;
+select format(1.5555,0),format(123.5555,1),format(1234.5555,2),format(12345.5555,3),format(123456.5555,4),format(1234567.5555,5),format("12345.2399",2);
+
+#
+# system functions
+#
+select database(),user();
+
+#
+# Null tests
+#
+select null,isnull(null),isnull(1/0),isnull(1/0 = null),ifnull(null,1),ifnull(null,"TRUE"),ifnull("TRUE","ERROR"),1/0 is null,1 is not null;
+select 1 | NULL,1 & NULL,1+NULL,1-NULL;
+select NULL=NULL,NULL<>NULL,NULL IS NULL, NULL IS NOT NULL,IFNULL(NULL,1.1)+0,IFNULL(NULL,1) | 0;
+select strcmp("a",NULL),(1<NULL)+0.0,NULL regexp "a",null like "a%","a%" like null;
+select concat("a",NULL),replace(NULL,"a","b"),replace("string","i",NULL),replace("string",NULL,"i"),insert("abc",1,1,NULL),left(NULL,1);
+select field(NULL,"a","b","c");
+select 2 between null and 1,2 between 3 AND NULL,NULL between 1 and 2,2 between NULL and 3, 2 between 1 AND null,2 between null and 1,2 between 3 AND NULL;
+#
+# Wrong or 'funny' use of functions.
+#
+select insert("aa",100,1,"b"),insert("aa",1,3,"b"),left("aa",-1),substring("a",1,2);
+select elt(2,1),field(NULL,"a","b","c");
+select locate("a","b",2),locate("","a",1),ltrim("a"),rtrim("a");
+select concat("1","2")|0,concat("1",".5")+0.0;
+
+#
+# time functions
+# The last line should return new values for each test run
+#
+select from_days(to_days("960101")),to_days(960201)-to_days("19960101"),to_days(curdate()+1)-to_days(curdate()),weekday("1997-01-01") ;
+select period_add("9602",-12),period_diff(199505,"9404") ;
+select now()-now(),weekday(curdate())-weekday(now()),unix_timestamp()-unix_timestamp(now());
+select now(),now()+0,curdate(),weekday(curdate()),weekday(now()),unix_timestamp(),unix_timestamp(now());
diff --git a/tests/grant.res b/tests/grant.res
new file mode 100644
index 00000000..a4deafe2
--- /dev/null
+++ b/tests/grant.res
@@ -0,0 +1,616 @@
+delete from user where user='grant_user' or user='grant_user2'
+delete from db where user='grant_user'
+delete from tables_priv
+delete from columns_priv
+lock tables mysql.user write
+flush privileges
+unlock tables
+drop database grant_test
+create database grant_test
+Connecting grant_user
+Error on connect: Access denied for user: ''@'localhost' to database 'grant_test'
+grant select(user) on mysql.user to grant_user@localhost
+revoke select(user) on mysql.user from grant_user@localhost
+grant select on *.* to grant_user@localhost
+set password FOR grant_user2@localhost = password('test')
+Error in execute: Can't find any matching row in the user table
+set password FOR grant_user=password('test')
+Connecting grant_user
+Error on connect: Access denied for user: 'grant_user'@'localhost' (Using password: NO)
+set password FOR grant_user=''
+Connecting grant_user
+select * from mysql.user where user = 'grant_user'
+localhost grant_user Y N N N N N N N N N N N N N N N N N N N N 0 0 0
+
+select * from mysql.db where user = 'grant_user'
+grant select on *.* to grant_user@localhost,grant_user@localhost
+show grants for grant_user@localhost
+GRANT SELECT ON *.* TO 'grant_user'@'localhost'
+
+Connecting grant_user
+insert into mysql.user (host,user) values ('error','grant_user')
+Error in execute: insert command denied to user: 'grant_user'@'localhost' for table 'user'
+update mysql.user set host='error' WHERE user='grant_user'
+Error in execute: update command denied to user: 'grant_user'@'localhost' for table 'user'
+create table grant_test.test (a int,b int)
+Error in execute: create command denied to user: 'grant_user'@'localhost' for table 'test'
+grant select on *.* to grant_user2@localhost
+Error in execute: Access denied for user: 'grant_user'@'localhost' (Using password: NO)
+revoke select on grant_test.test from grant_user@opt_host
+Error in execute: There is no such grant defined for user 'grant_user' on host 'opt_host'
+revoke select on grant_test.* from grant_user@opt_host
+Error in execute: There is no such grant defined for user 'grant_user' on host 'opt_host'
+revoke select on *.* from grant_user
+Error in execute: There is no such grant defined for user 'grant_user' on host '%'
+grant select on grant_test.not_exists to grant_user
+Error in execute: Table 'grant_test.not_exists' doesn't exist
+grant FILE on grant_test.test to grant_user
+Error in execute: Illegal GRANT/REVOKE command. Please consult the manual which privileges can be used
+grant select on *.* to wrong___________user_name
+Error in execute: The host or user argument to GRANT is too long
+grant select on grant_test.* to wrong___________user_name
+Error in execute: The host or user argument to GRANT is too long
+Connecting grant_user
+grant select on grant_test.test to grant_user with grant option
+Error in execute: grant command denied to user: 'grant_user'@'localhost' for table 'test'
+set password FOR ''@''=''
+Error in execute: Can't find any matching row in the user table
+set password FOR root@localhost = password('test')
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'mysql'
+revoke select on *.* from grant_user@localhost
+grant create,update on *.* to grant_user@localhost
+Connecting grant_user
+flush privileges
+create table grant_test.test (a int,b int)
+update grant_test.test set b=b+1 where a > 0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+show grants for grant_user@localhost
+GRANT UPDATE, CREATE ON *.* TO 'grant_user'@'localhost'
+
+revoke update on *.* from grant_user@localhost
+Connecting grant_user
+grant select(c) on grant_test.test to grant_user@localhost
+Error in execute: Unknown column 'c' in 'test'
+revoke select(c) on grant_test.test from grant_user@localhost
+Error in execute: There is no such grant defined for user 'grant_user' on host 'localhost' on table 'test'
+grant select on grant_test.test to wrong___________user_name
+Error in execute: The host or user argument to GRANT is too long
+INSERT INTO grant_test.test values (2,0)
+Error in execute: insert command denied to user: 'grant_user'@'localhost' for table 'test'
+grant ALL PRIVILEGES on *.* to grant_user@localhost
+REVOKE INSERT on *.* from grant_user@localhost
+Connecting grant_user
+INSERT INTO grant_test.test values (1,0)
+Error in execute: insert command denied to user: 'grant_user'@'localhost' for table 'test'
+grant INSERT on *.* to grant_user@localhost
+Connecting grant_user
+INSERT INTO grant_test.test values (2,0)
+select count(*) from grant_test.test
+1
+
+revoke SELECT on *.* from grant_user@localhost
+Connecting grant_user
+select count(*) from grant_test.test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+INSERT INTO grant_test.test values (3,0)
+grant SELECT on *.* to grant_user@localhost
+Connecting grant_user
+select count(*) from grant_test.test
+2
+
+revoke ALL PRIVILEGES on *.* from grant_user@localhost
+Connecting grant_user
+Error on connect: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+delete from user where user='grant_user'
+flush privileges
+delete from user where user='grant_user'
+flush privileges
+grant select on grant_test.* to grant_user@localhost
+select * from mysql.user where user = 'grant_user'
+localhost grant_user N N N N N N N N N N N N N N N N N N N N N 0 0 0
+
+select * from mysql.db where user = 'grant_user'
+localhost grant_test grant_user Y N N N N N N N N N N N
+
+Connecting grant_user
+select count(*) from grant_test.test
+2
+
+select * from mysql.user where user = 'grant_user'
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'mysql'
+insert into grant_test.test values (4,0)
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+update grant_test.test set a=1
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+delete from grant_test.test
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+create table grant_test.test2 (a int)
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+ALTER TABLE grant_test.test add c int
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+CREATE INDEX dummy ON grant_test.test (a)
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+drop table grant_test.test
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+grant ALL PRIVILEGES on grant_test.* to grant_user2@localhost
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+grant ALL PRIVILEGES on grant_test.* to grant_user@localhost WITH GRANT OPTION
+Connecting grant_user
+insert into grant_test.test values (5,0)
+REVOKE ALL PRIVILEGES on * from grant_user@localhost
+Error in execute: There is no such grant defined for user 'grant_user' on host 'localhost'
+REVOKE ALL PRIVILEGES on *.* from grant_user@localhost
+REVOKE ALL PRIVILEGES on grant_test.* from grant_user@localhost
+REVOKE ALL PRIVILEGES on grant_test.* from grant_user@localhost
+Connecting grant_user
+insert into grant_test.test values (6,0)
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+REVOKE GRANT OPTION on grant_test.* from grant_user@localhost
+Connecting grant_user
+Error on connect: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+grant ALL PRIVILEGES on grant_test.* to grant_user@localhost
+Connecting grant_user
+select * from mysql.user where user = 'grant_user'
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'mysql'
+insert into grant_test.test values (7,0)
+update grant_test.test set a=3 where a=2
+delete from grant_test.test where a=3
+create table grant_test.test2 (a int not null)
+alter table grant_test.test2 add b int
+create index dummy on grant_test.test2 (a)
+update test,test2 SET test.a=test2.a where test.a=test2.a
+drop table grant_test.test2
+show tables from grant_test
+test
+
+insert into mysql.user (host,user) values ('error','grant_user',0)
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'mysql'
+revoke ALL PRIVILEGES on grant_test.* from grant_user@localhost
+select * from mysql.user where user = 'grant_user'
+localhost grant_user N N N N N N N N N N N N N N N N N N N N N 0 0 0
+
+select * from mysql.db where user = 'grant_user'
+grant CREATE,UPDATE,DROP on grant_test.* to grant_user@localhost
+Connecting grant_user
+create table grant_test.test2 (a int not null)
+update test,test2 SET test.a=1 where 1
+update test,test2 SET test.a=test2.a where 1
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+grant SELECT on grant_test.* to grant_user@localhost
+Connecting grant_user
+update test,test2 SET test.a=test2.a where test2.a=test.a
+drop table grant_test.test2
+revoke ALL PRIVILEGES on grant_test.* from grant_user@localhost
+Connecting grant_user
+Error on connect: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+grant create on grant_test.test2 to grant_user@localhost
+Connecting grant_user
+create table grant_test.test2 (a int not null)
+show tables
+test2
+
+show columns from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+show keys from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+show columns from test2
+a int(11) binary 0
+
+show keys from test2
+select * from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+grant insert on grant_test.test to grant_user@localhost
+show tables
+test
+test2
+
+insert into grant_test.test values (8,0)
+update grant_test.test set b=1
+Error in execute: update command denied to user: 'grant_user'@'localhost' for table 'test'
+grant update on grant_test.test to grant_user@localhost
+update grant_test.test set b=2
+update grant_test.test,test2 SET test.b=3
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant select on grant_test.test2 to grant_user@localhost
+update grant_test.test,test2 SET test.b=3
+revoke select on grant_test.test2 from grant_user@localhost
+delete from grant_test.test
+Error in execute: delete command denied to user: 'grant_user'@'localhost' for table 'test'
+grant delete on grant_test.test to grant_user@localhost
+delete from grant_test.test where a=1
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+update grant_test.test set b=3 where b=1
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+update grant_test.test set b=b+1
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+update grant_test.test,test2 SET test.a=test2.a
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant SELECT on *.* to grant_user@localhost
+Connecting grant_user
+update grant_test.test set b=b+1
+update grant_test.test set b=b+1 where a > 0
+update grant_test.test,test2 SET test.a=test2.a
+update grant_test.test,test2 SET test2.a=test.a
+Error in execute: UPDATE command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+revoke SELECT on *.* from grant_user@localhost
+grant SELECT on grant_test.* to grant_user@localhost
+Connecting grant_user
+update grant_test.test set b=b+1
+update grant_test.test set b=b+1 where a > 0
+grant UPDATE on *.* to grant_user@localhost
+Connecting grant_user
+update grant_test.test set b=b+1
+update grant_test.test set b=b+1 where a > 0
+revoke UPDATE on *.* from grant_user@localhost
+revoke SELECT on grant_test.* from grant_user@localhost
+Connecting grant_user
+update grant_test.test set b=b+1 where a > 0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+update grant_test.test set b=b+1
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+select * from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+grant select on grant_test.test to grant_user@localhost
+delete from grant_test.test where a=1
+update grant_test.test set b=2 where b=1
+update grant_test.test set b=b+1
+select count(*) from test
+3
+
+update test,test2 SET test.b=4
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+update test,test2 SET test2.a=test.a
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+update test,test2 SET test.a=test2.a
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+create table grant_test.test3 (a int)
+Error in execute: create command denied to user: 'grant_user'@'localhost' for table 'test3'
+alter table grant_test.test2 add c int
+Error in execute: alter command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant alter on grant_test.test2 to grant_user@localhost
+alter table grant_test.test2 add c int
+create index dummy ON grant_test.test (a)
+Error in execute: index command denied to user: 'grant_user'@'localhost' for table 'test'
+grant index on grant_test.test2 to grant_user@localhost
+create index dummy ON grant_test.test2 (a)
+insert into test2 SELECT a,a from test
+Error in execute: insert command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant insert on test2 to grant_user@localhost
+Error in execute: Table 'mysql.test2' doesn't exist
+grant insert(a) on grant_test.test2 to grant_user@localhost
+insert into test2 SELECT a,a from test
+Error in execute: insert command denied to user: 'grant_user'@'localhost' for column 'c' in table 'test2'
+grant insert(c) on grant_test.test2 to grant_user@localhost
+insert into test2 SELECT a,a from test
+select count(*) from test2,test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+select count(*) from test,test2
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+replace into test2 SELECT a from test
+Error in execute: delete command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant update on grant_test.test2 to grant_user@localhost
+update test,test2 SET test2.a=test.a
+update test,test2 SET test.b=test2.a where 0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+update test,test2 SET test.a=2 where test2.a>100
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+update test,test2 SET test.a=test2.a
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+replace into test2 SELECT a,a from test
+Error in execute: delete command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant DELETE on grant_test.test2 to grant_user@localhost
+replace into test2 SELECT a,a from test
+insert into test (a) SELECT a from test2
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant SELECT on grant_test.test2 to grant_user@localhost
+update test,test2 SET test.b=test2.a where 0
+update test,test2 SET test.a=test2.a where test2.a>100
+revoke UPDATE on grant_test.test2 from grant_user@localhost
+grant UPDATE (c) on grant_test.test2 to grant_user@localhost
+update test,test2 SET test.b=test2.a where 0
+update test,test2 SET test.a=test2.a where test2.a>100
+update test,test2 SET test2.a=test2.a where test2.a>100
+Error in execute: UPDATE command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+update test,test2 SET test2.c=test2.a where test2.a>100
+revoke SELECT,UPDATE on grant_test.test2 from grant_user@localhost
+grant UPDATE on grant_test.test2 to grant_user@localhost
+drop table grant_test.test2
+Error in execute: drop command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant select on grant_test.test2 to grant_user@localhost with grant option
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+grant drop on grant_test.test2 to grant_user@localhost with grant option
+grant drop on grant_test.test2 to grant_user@localhost with grant option
+grant select on grant_test.test2 to grant_user@localhost with grant option
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test2'
+rename table grant_test.test2 to grant_test.test3
+Error in execute: insert command denied to user: 'grant_user'@'localhost' for table 'test3'
+grant CREATE,DROP on grant_test.test3 to grant_user@localhost
+rename table grant_test.test2 to grant_test.test3
+Error in execute: insert command denied to user: 'grant_user'@'localhost' for table 'test3'
+create table grant_test.test3 (a int)
+grant INSERT on grant_test.test3 to grant_user@localhost
+drop table grant_test.test3
+rename table grant_test.test2 to grant_test.test3
+rename table grant_test.test3 to grant_test.test2
+Error in execute: alter command denied to user: 'grant_user'@'localhost' for table 'test3'
+grant ALTER on grant_test.test3 to grant_user@localhost
+rename table grant_test.test3 to grant_test.test2
+revoke DROP on grant_test.test2 from grant_user@localhost
+rename table grant_test.test2 to grant_test.test3
+drop table if exists grant_test.test2,grant_test.test3
+Error in execute: drop command denied to user: 'grant_user'@'localhost' for table 'test2'
+drop table if exists grant_test.test2,grant_test.test3
+create database grant_test
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+drop database grant_test
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+flush tables
+Error in execute: Access denied. You need the RELOAD privilege for this operation
+flush privileges
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv
+localhost grant_test grant_user test2 root@localhost Update,Delete,Create,Grant,Index,Alter Insert
+localhost grant_test grant_user test root@localhost Select,Insert,Update,Delete
+localhost grant_test grant_user test3 root@localhost Insert,Create,Drop,Alter
+
+revoke ALL PRIVILEGES on grant_test.test from grant_user@localhost
+revoke ALL PRIVILEGES on grant_test.test2 from grant_user@localhost
+revoke ALL PRIVILEGES on grant_test.test3 from grant_user@localhost
+revoke GRANT OPTION on grant_test.test2 from grant_user@localhost
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv
+select count(a) from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+grant create,update on grant_test.test2 to grant_user@localhost
+create table grant_test.test2 (a int not null)
+delete from grant_test.test where a=2
+Error in execute: delete command denied to user: 'grant_user'@'localhost' for table 'test'
+delete from grant_test.test where A=2
+Error in execute: delete command denied to user: 'grant_user'@'localhost' for table 'test'
+update test set b=5 where b>0
+Error in execute: update command denied to user: 'grant_user'@'localhost' for table 'test'
+update test,test2 SET test.b=5 where b>0
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+grant update(b),delete on grant_test.test to grant_user@localhost
+revoke update(a) on grant_test.test from grant_user@localhost
+Error in execute: There is no such grant defined for user 'grant_user' on host 'localhost' on table 'test'
+delete from grant_test.test where a=2
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+update test set b=5 where b>0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+grant select(a),select(b) on grant_test.test to grant_user@localhost
+delete from grant_test.test where a=2
+delete from grant_test.test where A=2
+update test set b=5 where b>0
+update test set a=11 where b>5
+Error in execute: UPDATE command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+update test,test2 SET test.b=5 where b>0
+update test,test2 SET test.a=11 where b>0
+Error in execute: UPDATE command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+update test,test2 SET test.b=test2.a where b>0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+update test,test2 SET test.b=11 where test2.a>0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test2'
+select a,A from test
+8 8
+5 5
+7 7
+
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv
+localhost grant_test grant_user test2 root@localhost Update,Create
+localhost grant_test grant_user test root@localhost Delete Select,Update
+
+revoke ALL PRIVILEGES on grant_test.test from grant_user@localhost
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv
+localhost grant_test grant_user test2 root@localhost Update,Create
+
+revoke GRANT OPTION on grant_test.test from grant_user@localhost
+Error in execute: There is no such grant defined for user 'grant_user' on host 'localhost' on table 'test'
+drop table grant_test.test2
+revoke create,update on grant_test.test2 from grant_user@localhost
+grant select(a) on grant_test.test to grant_user@localhost
+show full columns from test
+a int(11) binary YES NULL select
+b int(11) binary YES NULL
+
+grant insert (b), update (b) on grant_test.test to grant_user@localhost
+select count(a) from test
+3
+
+select count(skr.a) from test as skr
+3
+
+select count(a) from test where a > 5
+2
+
+insert into test (b) values (5)
+insert into test (b) values (a)
+update test set b=3 where a > 0
+select * from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+select b from test
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+select a from test where b > 0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+insert into test (a) values (10)
+Error in execute: INSERT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+insert into test (b) values (b)
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+insert into test (a,b) values (1,5)
+Error in execute: INSERT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+insert into test (b) values (1),(b)
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+update test set b=3 where b > 0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv
+localhost grant_test grant_user test root@localhost Select,Insert,Update
+
+select Host, Db, User, Table_name, Column_name, Column_priv from mysql.columns_priv
+localhost grant_test grant_user test b Insert,Update
+localhost grant_test grant_user test a Select
+
+revoke select(a), update (b) on grant_test.test from grant_user@localhost
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv
+localhost grant_test grant_user test root@localhost Insert
+
+select Host, Db, User, Table_name, Column_name, Column_priv from mysql.columns_priv
+localhost grant_test grant_user test b Insert
+
+select count(a) from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+update test set b=4
+Error in execute: update command denied to user: 'grant_user'@'localhost' for table 'test'
+grant select(a,b), update (a,b) on grant_test.test to grant_user@localhost
+select count(a),count(b) from test where a+b > 0
+3 3
+
+insert into test (b) values (9)
+update test set b=6 where b > 0
+flush privileges
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv
+localhost grant_test grant_user test root@localhost Select,Insert,Update
+
+select Host, Db, User, Table_name, Column_name, Column_priv from mysql.columns_priv
+localhost grant_test grant_user test b Select,Insert,Update
+localhost grant_test grant_user test a Select,Update
+
+insert into test (a,b) values (12,12)
+Error in execute: INSERT command denied to user: 'grant_user'@'localhost' for column 'a' in table 'test'
+grant insert on grant_test.* to grant_user@localhost
+Connecting grant_user
+insert into test (a,b) values (13,13)
+revoke select(b) on grant_test.test from grant_user@localhost
+select count(a) from test where a+b > 0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+update test set b=5 where a=2
+grant select on grant_test.test to grant_user@localhost
+Connecting grant_user
+select count(a) from test where a+b > 0
+4
+
+revoke select(b) on grant_test.test from grant_user@localhost
+select count(a) from test where a+b > 0
+4
+
+revoke select on grant_test.test from grant_user@localhost
+Connecting grant_user
+select count(a) from test where a+b > 0
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+grant select(a) on grant_test.test to grant_user@localhost
+select count(a) from test where a+b > 0
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test'
+grant select on *.* to grant_user@localhost
+Connecting grant_user
+select count(a) from test where a+b > 0
+4
+
+revoke select on *.* from grant_user@localhost
+grant select(b) on grant_test.test to grant_user@localhost
+Connecting grant_user
+select count(a) from test where a+b > 0
+4
+
+select * from mysql.db where user = 'grant_user'
+localhost grant_test grant_user N Y N N N N N N N N N N
+
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv where user = 'grant_user'
+localhost grant_test grant_user test root@localhost Select,Insert,Update
+
+select Host, Db, User, Table_name, Column_name, Column_priv from mysql.columns_priv where user = 'grant_user'
+localhost grant_test grant_user test b Select,Insert,Update
+localhost grant_test grant_user test a Select,Update
+
+revoke ALL PRIVILEGES on grant_test.test from grant_user@localhost
+select count(a) from test
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'test'
+select * from mysql.user order by hostname
+Error in execute: select command denied to user: 'grant_user'@'localhost' for table 'user'
+select * from mysql.db where user = 'grant_user'
+localhost grant_test grant_user N Y N N N N N N N N N N
+
+select Host, Db, User, Table_name, Grantor, Table_priv, Column_priv from mysql.tables_priv where user = 'grant_user'
+select Host, Db, User, Table_name, Column_name, Column_priv from mysql.columns_priv where user = 'grant_user'
+delete from user where user='grant_user'
+delete from db where user='grant_user'
+flush privileges
+show grants for grant_user@localhost
+Error in execute: There is no such grant defined for user 'grant_user' on host 'localhost'
+grant ALL PRIVILEGES on grant_test.test to grant_user@localhost identified by 'dummy', grant_user@127.0.0.1 identified by 'dummy2'
+Connecting grant_user
+grant SELECT on grant_test.* to grant_user@localhost identified by ''
+Connecting grant_user
+revoke ALL PRIVILEGES on grant_test.test from grant_user@localhost identified by '', grant_user@127.0.0.1 identified by 'dummy2'
+revoke ALL PRIVILEGES on grant_test.* from grant_user@localhost identified by ''
+show grants for grant_user@localhost
+GRANT USAGE ON *.* TO 'grant_user'@'localhost'
+
+create table grant_test.test3 (a int, b int)
+grant SELECT on grant_test.test3 to grant_user@localhost
+grant FILE on *.* to grant_user@localhost
+insert into grant_test.test3 values (1,1)
+Connecting grant_user
+select * into outfile '/tmp/mysql-grant.test' from grant_test.test3
+revoke SELECT on grant_test.test3 from grant_user@localhost
+grant SELECT(a) on grant_test.test3 to grant_user@localhost
+select a from grant_test.test3
+1
+
+select * from grant_test.test3
+Error in execute: select command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test3'
+select a,b from grant_test.test3
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test3'
+select b from grant_test.test3
+Error in execute: SELECT command denied to user: 'grant_user'@'localhost' for column 'b' in table 'test3'
+revoke SELECT(a) on grant_test.test3 from grant_user@localhost
+revoke FILE on *.* from grant_user@localhost
+drop table grant_test.test3
+create table grant_test.test3 (a int)
+Connecting grant_user
+Error on connect: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+grant INSERT on grant_test.test3 to grant_user@localhost
+Connecting grant_user
+select * into outfile '/tmp/mysql-grant.test' from grant_test.test3
+Error in execute: Access denied for user: 'grant_user'@'localhost' (Using password: NO)
+grant SELECT on grant_test.test3 to grant_user@localhost
+Connecting grant_user
+LOCK TABLES grant_test.test3 READ
+Error in execute: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+grant LOCK TABLES on *.* to grant_user@localhost
+show grants for grant_user@localhost
+GRANT LOCK TABLES ON *.* TO 'grant_user'@'localhost'
+GRANT SELECT, INSERT ON `grant_test`.`test3` TO 'grant_user'@'localhost'
+
+select * from mysql.user where user='grant_user'
+127.0.0.1 grant_user *042a99b3d247ae587783f647f2d69496d390aa71eab3 N N N N N N N N N N N N N N N N N N N N N 0 0 0
+localhost grant_user N N N N N N N N N N N N N N N N N Y N N N 0 0 0
+
+Connecting grant_user
+LOCK TABLES grant_test.test3 READ
+UNLOCK TABLES
+revoke SELECT,INSERT,UPDATE,DELETE on grant_test.test3 from grant_user@localhost
+Connecting grant_user
+revoke LOCK TABLES on *.* from grant_user@localhost
+Connecting grant_user
+Error on connect: Access denied for user: 'grant_user'@'localhost' to database 'grant_test'
+drop table grant_test.test3
+show grants for grant_user@localhost
+GRANT USAGE ON *.* TO 'grant_user'@'localhost'
+
+grant all on *.* to grant_user@localhost WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 2 MAX_CONNECTIONS_PER_HOUR 3
+show grants for grant_user@localhost
+GRANT ALL PRIVILEGES ON *.* TO 'grant_user'@'localhost' WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 2 MAX_CONNECTIONS_PER_HOUR 3
+
+revoke LOCK TABLES on *.* from grant_user@localhost
+flush privileges
+show grants for grant_user@localhost
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'grant_user'@'localhost' WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 2 MAX_CONNECTIONS_PER_HOUR 3
+
+revoke ALL PRIVILEGES on *.* from grant_user@localhost
+show grants for grant_user@localhost
+GRANT USAGE ON *.* TO 'grant_user'@'localhost' WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 2 MAX_CONNECTIONS_PER_HOUR 3
+
+drop database grant_test
+delete from user where user='grant_user'
+delete from db where user='grant_user'
+delete from tables_priv
+delete from columns_priv
+flush privileges
+end of test
diff --git a/tests/index_corrupt.pl b/tests/index_corrupt.pl
new file mode 100755
index 00000000..275747d8
--- /dev/null
+++ b/tests/index_corrupt.pl
@@ -0,0 +1,229 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# This is a test for a key cache bug (bug #10167)
+# To expose the bug mysqld should be started with --key-buffer-size=64K
+#
+
+$opt_loop_count=100000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+ $opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_host=$opt_user=$opt_password=""; $opt_db="test";
+
+GetOptions("host=s","db=s","loop-count=i","skip-create","skip-in",
+ "skip-delete","verbose","fast-insert","lock-tables","debug","fast",
+ "force","user=s","password=s") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+$firsttable = "bench_f1";
+$secondtable = "bench_f2";
+$kill_file= "/tmp/mysqltest_index_corrupt.$$";
+
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+if (!$opt_skip_create)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table if exists $firsttable, $secondtable");
+
+ print "Creating tables in $opt_db\n";
+ $dbh->do("create table $firsttable (
+c_pollid INTEGER NOT NULL,
+c_time BIGINT NOT NULL,
+c_data DOUBLE NOT NULL,
+c_error INTEGER NOT NULL,
+c_warning INTEGER NOT NULL,
+c_okay INTEGER NOT NULL,
+c_unknown INTEGER NOT NULL,
+c_rolled_up BIT NOT NULL,
+INDEX t_mgmt_hist_r_i1 (c_pollid),
+INDEX t_mgmt_hist_r_i2 (c_time),
+INDEX t_mgmt_hist_r_i3 (c_rolled_up))") or die $DBI::errstr;
+
+ $dbh->do("create table $secondtable (
+c_pollid INTEGER NOT NULL,
+c_min_time BIGINT NOT NULL,
+c_max_time BIGINT NOT NULL,
+c_min_data DOUBLE NOT NULL,
+c_max_data DOUBLE NOT NULL,
+c_avg_data DOUBLE NOT NULL,
+c_error INTEGER NOT NULL,
+c_warning INTEGER NOT NULL,
+c_okay INTEGER NOT NULL,
+c_unknown INTEGER NOT NULL,
+c_rolled_up BIT NOT NULL,
+INDEX t_mgmt_hist_d_i1 (c_pollid),
+INDEX t_mgmt_hist_d_i2 (c_min_time),
+INDEX t_mgmt_hist_d_i3 (c_max_time),
+INDEX t_mgmt_hist_d_i4 (c_rolled_up))") or die $DBI::errstr;
+
+
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+print "Running tests\n";
+insert_in_bench() if (($pid=fork()) == 0); $work{$pid}="insert";
+select_from_bench() if (($pid=fork()) == 0); $work{$pid}="insert-select;
+delete_from_bench() if (($pid=fork()) == 0); $work{$pid}="delete";
+
+$errors=0;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ $errors++ if ($ret != 0);
+}
+
+if (!$opt_skip_delete && !$errors)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table $firsttable, $secondtable");
+}
+print ($errors ? "Test failed\n" :"Test ok\n");
+
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+unlink $kill_file;
+
+exit(0);
+
+#
+# Insert records in the two tables
+#
+
+sub insert_in_bench
+{
+ my ($dbh,$rows,$found,$i);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ for ($rows= 1; $rows <= $opt_loop_count ; $rows++)
+ {
+ $c_pollid = sprintf("%d",rand 1000);
+ $c_time = sprintf("%d",rand 100000);
+ $c_data = rand 1000000;
+ $test = rand 1;
+ $c_error=0;
+ $c_warning=0;
+ $c_okay=0;
+ $c_unknown=0;
+ if ($test < .8) {
+ $c_okay=1;
+ } elsif ($test <.9) {
+ $c_error=1;
+ } elsif ($test <.95) {
+ $c_warning=1;
+ } else {
+ $c_unknown=1;
+ }
+ $statement = "INSERT INTO $firsttable (c_pollid, c_time, c_data, c_error
+, c_warning, c_okay, c_unknown, c_rolled_up) ".
+ "VALUES ($c_pollid,$c_time,$c_data,$c_error,$c_warning,$c_okay,$c_unknown,0)";
+ $cursor = $dbh->prepare($statement);
+ $cursor->execute();
+ $cursor->finish();
+ }
+
+ $dbh->disconnect; $dbh=0;
+ print "insert_in_bench: Inserted $rows rows\n";
+
+ # Kill other threads
+ open(KILLFILE, "> $kill_file");
+ close(KILLFILE);
+
+ exit(0);
+}
+
+
+sub select_from_bench
+{
+ my ($dbh,$rows,$cursor);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ for ($rows= 1; $rows < $opt_loop_count ; $rows++)
+ {
+ $t_value = rand 100000;
+ $t_value2 = $t_value+10000;
+ $statement = "INSERT INTO $secondtable (c_pollid, c_min_time, c_max_time
+, c_min_data, c_max_data, c_avg_data, c_error, c_warning, c_okay, c_unknown, c_rolled_up) SELECT c_pollid, MIN(c_time), MAX(c_time), MIN(c_data), MAX(c_data), AVG(c_data), SUM(c_error), SUM(c_warning), SUM(c_okay), SUM(c_unknown), 0 FROM $firsttable WHERE (c_time>=$t_value) AND (c_time<$t_value2) AND (c_rolled_up=0) GROUP BY c_pollid";
+ $cursor = $dbh->prepare($statement);
+ $cursor->execute();
+ $cursor->finish();
+ sleep 1;
+ if (-e $kill_file)
+ {
+ last;
+ }
+ }
+ print "select_from_bench: insert-select executed $rows times\n";
+ exit(0);
+}
+
+
+sub delete_from_bench
+{
+ my ($dbh,$row, $t_value, $t2_value, $statement, $cursor);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($rows= 1; $rows < $opt_loop_count ; $rows++)
+ {
+ $t_value = rand 50000;
+ $t2_value = $t_value + 50001;
+ $statement = "DELETE FROM $firsttable WHERE (c_time>$t_value) AND (c_time<$t2_value)";
+ $cursor = $dbh->prepare($statement);
+ $cursor->execute();
+ $cursor->finish();
+ sleep 10;
+ if (-e $kill_file)
+ {
+ last;
+ }
+ }
+ print "delete: delete executed $rows times\n";
+ exit(0);
+}
diff --git a/tests/insert_and_repair.pl b/tests/insert_and_repair.pl
new file mode 100755
index 00000000..35ecd52a
--- /dev/null
+++ b/tests/insert_and_repair.pl
@@ -0,0 +1,197 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000, 2001 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# This is a test of insert and repair/check.
+#
+
+$opt_loop_count=100000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+ $opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_host=$opt_user=$opt_password=""; $opt_db="test";
+
+GetOptions("host=s","db=s","loop-count=i","skip-create","skip-in",
+ "skip-delete","verbose","fast-insert","lock-tables","debug","fast",
+ "force","user=s","password=s") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+$firsttable = "bench_f1";
+$secondtable = "bench_f2";
+
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+if (!$opt_skip_create)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table if exists $firsttable, $secondtable");
+
+ print "Creating tables $firsttable and $secondtable in database $opt_db\n";
+ $dbh->do("create table $firsttable (id int(7) not null, thread tinyint not null, info varchar(32), marker char(1), primary key(id,thread))") or die $DBI::errstr;
+ $dbh->do("create table $secondtable (id int(7) not null, thread tinyint not null, row int(3) not null,value double, primary key(id,thread,row)) delay_key_write=1") or die $DBI::errstr;
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+insert_in_bench1() if (($pid=fork()) == 0); $work{$pid}="insert in bench1";
+insert_in_bench2() if (($pid=fork()) == 0); $work{$pid}="insert in bench2";
+repair_and_check() if (($pid=fork()) == 0); $work{$pid}="repair/check";
+
+$errors=0;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ $errors++ if ($ret != 0);
+}
+
+if (!$opt_skip_delete && !$errors)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table $firsttable,$secondtable");
+}
+print ($errors ? "Test failed\n" :"Test ok\n");
+
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+#
+# Insert records in the two tables
+#
+
+sub insert_in_bench1
+{
+ my ($dbh,$rows,$found,$i);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $rows=$found=0;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ $sth=$dbh->do("insert into $firsttable values ($i,0,'This is entry $i','')") || die "Got error on insert: $DBI::errstr\n";
+ $row_count=($i % 7)+1;
+ $rows+=1+$row_count;
+ for ($j=0 ; $j < $row_count; $j++)
+ {
+ $sth=$dbh->do("insert into $secondtable values ($i,0,$j,0)") || die "Got error on insert: $DBI::errstr\n";
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "insert_in_bench1: Inserted $rows rows\n";
+ exit(0);
+}
+
+sub insert_in_bench2
+{
+ my ($dbh,$rows,$found,$i);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $rows=$found=0;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ $sth=$dbh->do("insert into $firsttable values ($i,1,'This is entry $i','')") || die "Got error on insert: $DBI::errstr\n";
+ $row_count=((7-$i) % 7)+1;
+ $rows+=1+$row_count;
+ for ($j=0 ; $j < $row_count; $j++)
+ {
+ $sth=$dbh->do("insert into $secondtable values ($i,1,$j,0)") || die "Got error on insert: $DBI::errstr\n";
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "insert_in_bench2: Inserted $rows rows\n";
+ exit(0);
+}
+
+
+sub repair_and_check
+{
+ my ($dbh,$row,@row,$found1,$found2,$last_found1,$last_found2,$i,$type,
+ $table);
+ $found1=$found2=0; $last_found1=$last_found2= -1;
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($i=0; $found1 != $last_found1 && $found2 != $last_found1 ; $i++)
+ {
+ $type=($i & 2) ? "repair" : "check";
+ if ($i & 1)
+ {
+ $table=$firsttable;
+ $last_found1=$found1;
+ }
+ else
+ {
+ $table=$secondtable;
+ $last_found2=$found2;
+ }
+ $sth=$dbh->prepare("$type table $table") || die "Got error on prepare: $dbh->errstr\n";
+ $sth->execute || die $dbh->errstr;
+
+ while (($row=$sth->fetchrow_arrayref))
+ {
+ if ($row->[3] ne "OK")
+ {
+ print "Got error " . $row->[3] . " when doing $type on $table\n";
+ exit(1);
+ }
+ }
+ $sth=$dbh->prepare("select count(*) from $table") || die "Got error on prepare: $dbh->errstr\n";
+ $sth->execute || die $dbh->errstr;
+ @row = $sth->fetchrow_array();
+ if ($i & 1)
+ {
+ $found1= $row[0];
+ }
+ else
+ {
+ $found2= $row[0];
+ }
+ $sth->finish;
+ sleep(2);
+ }
+ $dbh->disconnect; $dbh=0;
+ print "check/repair: Did $i repair/checks\n";
+ exit(0);
+}
diff --git a/tests/insert_test.c b/tests/insert_test.c
new file mode 100644
index 00000000..f2e6b61f
--- /dev/null
+++ b/tests/insert_test.c
@@ -0,0 +1,60 @@
+/* Copyright (c) 2000-2004 MySQL AB
+ Use is subject to license terms
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql.h"
+
+#define INSERT_QUERY "insert into test (name,num) values ('item %d', %d)"
+
+
+int main(int argc, char **argv)
+{
+ int count,num;
+ MYSQL *sock,mysql;
+ char qbuf[160];
+
+ if (argc != 3)
+ {
+ fprintf(stderr,"usage : insert_test <dbname> <Num>\n\n");
+ exit(1);
+ }
+
+ mysql_init(&mysql);
+ if (!(sock = mysql_real_connect(&mysql,NULL,NULL,NULL,argv[1],0,NULL,0)))
+ {
+ fprintf(stderr,"Couldn't connect to engine!\n%s\n",mysql_error(&mysql));
+ perror("");
+ exit(1);
+ }
+ mysql.reconnect= 1;
+
+ num = atoi(argv[2]);
+ count = 0;
+ while (count < num)
+ {
+ sprintf(qbuf,INSERT_QUERY,count,count);
+ if(mysql_query(sock,qbuf))
+ {
+ fprintf(stderr,"Query failed (%s)\n",mysql_error(sock));
+ exit(1);
+ }
+ count++;
+ }
+ mysql_close(sock);
+ exit(0);
+ return 0;
+}
diff --git a/tests/list_test.c b/tests/list_test.c
new file mode 100644
index 00000000..6c915d47
--- /dev/null
+++ b/tests/list_test.c
@@ -0,0 +1,71 @@
+/* Copyright (c) 2000, 2003, 2004 MySQL AB
+ Use is subject to license terms
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql.h"
+
+#define SELECT_QUERY "select name from test where num = %d"
+
+
+int main(int argc, char **argv)
+{
+ int count, num;
+ MYSQL mysql,*sock;
+ MYSQL_RES *res;
+ char qbuf[160];
+
+ if (argc != 2)
+ {
+ fprintf(stderr,"usage : select_test <dbname>\n\n");
+ exit(1);
+ }
+
+ if (!(sock = mysql_connect(&mysql,NULL,0,0)))
+ {
+ fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql));
+ perror("");
+ exit(1);
+ }
+ mysql.reconnect= 1;
+
+ if (mysql_select_db(sock,argv[1]) < 0)
+ {
+ fprintf(stderr,"Couldn't select database %s!\n%s\n",argv[1],
+ mysql_error(sock));
+ exit(1);
+ }
+
+ if (!(res=mysql_list_dbs(sock,NULL)))
+ {
+ fprintf(stderr,"Couldn't list dbs!\n%s\n",mysql_error(sock));
+ exit(1);
+ }
+ mysql_free_result(res);
+ if (!(res=mysql_list_tables(sock,NULL)))
+ {
+ fprintf(stderr,"Couldn't list tables!\n%s\n",mysql_error(sock));
+ exit(1);
+ }
+ mysql_free_result(res);
+
+ mysql_close(sock);
+ exit(0);
+ return 0;
+}
diff --git a/tests/lock_test.pl b/tests/lock_test.pl
new file mode 100755
index 00000000..8a8a0322
--- /dev/null
+++ b/tests/lock_test.pl
@@ -0,0 +1,110 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+# This is a test with uses two processes to a database.
+# The other inserts records in two tables, the other does a lot of joins
+# on these.
+# Every time the read thread outputs info, it does a ALTER TABLE command
+# which should stop the insert thread until the ALTER TABLE command is ready.
+#
+# Warning, the output from this test will differ in 'found' from time to time,
+# but there should never be any errors
+#
+
+$host = shift || "";
+$test_db="test";
+$test_count=10000;
+srand 0; # Repeatable test
+
+use Mysql;
+$|= 1; # Autoflush
+
+$dbh = Mysql->Connect($host) || die "Can't connect: $Mysql::db_errstr\n";
+$dbh->SelectDB($test_db) || die "Can't use database $test_db: $Mysql::db_errstr\n";
+
+$firsttable = "test_lock_1";
+$secondtable = "test_lock_2";
+$dbh->Query("drop table $firsttable");
+$dbh->Query("drop table $secondtable");
+
+print "Creating tables $firsttable and $secondtable in database $test_db\n";
+$dbh->Query("create table $firsttable (id int(6) not null, info char(32), auto int(11) not null auto_increment, primary key(id),key(auto))") or die $Mysql::db_errstr;
+
+$dbh->Query("create table $secondtable (id int(6) not null, info varchar(32), key(id))") or die $Mysql::db_errstr;
+
+$dbh=0; # Close handler
+
+if (fork() == 0)
+{ # Insert process
+ $dbh = Mysql->Connect($host) || die "Can't connect: $Mysql::db_errstr\n";
+ $dbh->SelectDB($test_db) || die "Can't use database $test_db: $Mysql::db_errstr\n";
+ $first_id=1; $second_id=1;
+ $first_count=$second_count=0;
+ print "Writing started\n";
+ for ($i=1 ; $i <= $test_count ; $i++)
+ {
+ if (rand(3) <= 1)
+ {
+ $sth=$dbh->Query("insert into $firsttable values ($first_id,'This is entry $i',NULL)") || die "Got error on insert: $Mysql::db_errstr\n";
+ die "Row not inserted, aborting\n" if ($sth->affected_rows != 1);
+ $first_id++;
+ $first_count++;
+ }
+ else
+ {
+ $sth=$dbh->Query("insert into $secondtable values ($second_id,'This is entry $i')") || die "Got error on insert: $Mysql::db_errstr\n";
+ die "Row not inserted, aborting\n" if ($sth->affected_rows != 1);
+ $second_id++ if (rand(10) <= 1); # Don't always count it up
+ $second_count++;
+ }
+ print "Write: $i\n" if ($i % 1000 == 0);
+ }
+ print "Writing done ($first_count $second_count)\n";
+}
+else
+{
+ $dbh = Mysql->Connect($host) || die "Can't connect: $Mysql::db_errstr\n";
+ $dbh->SelectDB($test_db) || die "Can't use database $test_db: $Mysql::db_errstr\n";
+ $locked=$found=0;
+ print "Reading started\n";
+ for ($i=1 ; $i <= $test_count ; $i++)
+ {
+ $id=int(rand($test_count)/3)+1;
+ $sth=$dbh->Query("select count(*) from $firsttable,$secondtable where $firsttable.id = $secondtable.id and $firsttable.id=$id") || die "Got error on select: $Mysql::db_errstr\n";
+ $found++ if ($sth->numrows);
+ if ($i % 1000 == 0)
+ {
+ print "Read: $i Found: $found\n";
+ if ($found)
+ {
+ $locked=1-$locked;
+ if ($locked)
+ {
+ $sth=$dbh->Query("lock tables $firsttable write,$secondtable write");
+ }
+ $sth=$dbh->Query("alter table $firsttable CHANGE id id int(6) not null") || die "Got error on ALTER TABLE: $Mysql::db_errstr\n";
+ $sth=$dbh->Query("alter table $secondtable CHANGE info info char(32) not null") || die "Got error on ALTER TABLE: $Mysql::db_errstr\n";
+ if ($locked)
+ {
+ $sth=$dbh->Query("unlock tables");
+ }
+ }
+ }
+ }
+ print "Reading done Found: $found\n";
+}
diff --git a/tests/lock_test.res b/tests/lock_test.res
new file mode 100644
index 00000000..5a9464d5
--- /dev/null
+++ b/tests/lock_test.res
@@ -0,0 +1,25 @@
+Creating tables test_lock_1 and test_lock_2 in database test
+Reading started
+Writing started
+Write: 1000
+Read: 1000 Found: 28
+Write: 2000
+Write: 3000
+Read: 2000 Found: 79
+Write: 4000
+Write: 5000
+Read: 3000 Found: 165
+Write: 6000
+Write: 7000
+Read: 4000 Found: 291
+Write: 8000
+Write: 9000
+Write: 10000
+Writing done
+Read: 5000 Found: 482
+Read: 6000 Found: 680
+Read: 7000 Found: 862
+Read: 8000 Found: 1076
+Read: 9000 Found: 1275
+Read: 10000 Found: 1507
+Reading done Found: 1507
diff --git a/tests/mail_to_db.pl b/tests/mail_to_db.pl
new file mode 100755
index 00000000..b160582d
--- /dev/null
+++ b/tests/mail_to_db.pl
@@ -0,0 +1,624 @@
+#!/usr/bin/env perl
+# Copyright Abandoned 1998 TCX DataKonsult AB & Monty Program KB & Detron HB
+# This file is public domain and comes with NO WARRANTY of any kind
+#
+# This program is brought to you by Janne-Petteri Koilo with the
+# administration of Michael Widenius.
+#
+# Rewritten with a lot of bug fixes by Jani Tolonen and Thimble Smith
+# 15.12.2000
+#
+# This program takes your mails and puts them into your database. It ignores
+# messages with the same from, date and message text.
+# You can use mail-files that are compressed or gzipped and ends with
+# -.gz or -.Z.
+
+use DBI;
+use Getopt::Long;
+
+$| = 1;
+$VER = "3.0";
+
+$opt_help = 0;
+$opt_version = 0;
+$opt_debug = 0;
+$opt_host = undef();
+$opt_port = undef();
+$opt_socket = undef();
+$opt_db = "mail";
+$opt_user = undef();
+$opt_password = undef();
+$opt_max_mail_size = 65536;
+$opt_create = 0;
+$opt_test = 0;
+$opt_no_path = 0;
+$opt_stop_on_error = 0;
+$opt_stdin = 0;
+
+my ($dbh, $progname, $mail_no_from_f, $mail_no_txt_f, $mail_too_big,
+ $mail_forwarded, $mail_duplicates, $mail_no_subject_f, $mail_inserted);
+
+$mail_no_from_f = $mail_no_txt_f = $mail_too_big = $mail_forwarded =
+$mail_duplicates = $mail_no_subject_f = $mail_inserted = 0;
+$mail_fixed=0;
+
+#
+# Remove the following message-ends from message
+#
+@remove_tail= (
+"\n-*\nSend a mail to .*\n.*\n.*\$",
+"\n-*\nPlease check .*\n.*\n\nTo unsubscribe, .*\n.*\n.*\nIf you have a broken.*\n.*\n.*\$",
+"\n-*\nPlease check .*\n(.*\n){1,3}\nTo unsubscribe.*\n.*\n.*\$",
+"\n-*\nPlease check .*\n.*\n\nTo unsubscribe.*\n.*\$",
+"\n-*\nTo request this thread.*\nTo unsubscribe.*\n.*\.*\n.*\$",
+"\n -*\n.*Send a mail to.*\n.*\n.*unsubscribe.*\$",
+"\n-*\nTo request this thread.*\n\nTo unsubscribe.*\n.*\$"
+);
+
+# Generate regexp to remove tails where the unsubscribed is quoted
+{
+ my (@tmp, $tail);
+ @tmp=();
+ foreach $tail (@remove_tail)
+ {
+ $tail =~ s/\n/\n[> ]*/g;
+ push(@tmp, $tail);
+ }
+ push @remove_tail,@tmp;
+}
+
+my %months = ('Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, 'May' => 5,
+ 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, 'Oct' => 10,
+ 'Nov' => 11, 'Dec' => 12);
+
+$progname = $0;
+$progname =~ s/.*[\/]//;
+
+main();
+
+####
+#### main sub routine
+####
+
+sub main
+{
+ my ($connect_arg, @args, $ignored, @defops, $i);
+
+ if (defined(my_which("my_print_defaults")))
+ {
+ @defops = `my_print_defaults mail_to_db`;
+ chop @defops;
+ splice @ARGV, 0, 0, @defops;
+ }
+ else
+ {
+ print "WARNING: No command 'my_print_defaults' found; unable to read\n";
+ print "the my.cnf file. This command is available from the latest MySQL\n";
+ print "distribution.\n";
+ }
+ GetOptions("help","version","host=s","port=i","socket=s","db=s",
+ "user=s","password=s","max_mail_size=i","create","test",
+ "no_path","debug","stop_on_error","stdin")
+ || die "Wrong option! See $progname --help\n";
+
+ usage($VER) if ($opt_help || $opt_version ||
+ (!$ARGV[0] && !$opt_create && !$opt_stdin));
+
+ # Check that the given inbox files exist and are regular files
+ for ($i = 0; ! $opt_stdin && defined($ARGV[$i]); $i++)
+ {
+ die "FATAL: Can't find inbox file: $ARGV[$i]\n" if (! -f $ARGV[$i]);
+ }
+
+ $connect_arg = "DBI:MariaDB:";
+ push @args, "database=$opt_db" if defined($opt_db);
+ push @args, "host=$opt_host" if defined($opt_host);
+ push @args, "port=$opt_port" if defined($opt_port);
+ push @args, "mariadb_socket=$opt_socket" if defined($opt_socket);
+ push @args, "mariadb_read_default_group=mail_to_db";
+ $connect_arg .= join ';', @args;
+ $dbh = DBI->connect("$connect_arg", $opt_user, $opt_password,
+ { PrintError => 0})
+ || die "Couldn't connect: $DBI::errstr\n";
+
+ die "You must specify the database; use --db=" if (!defined($opt_db));
+
+ create_table($dbh) if ($opt_create);
+
+ if ($opt_stdin)
+ {
+ open(FILE, "-");
+ process_mail_file($dbh, "READ-FROM-STDIN");
+ }
+ else
+ {
+ foreach (@ARGV)
+ {
+ # Check if the file is compressed
+ if (/^(.*)\.(gz|Z)$/)
+ {
+ open(FILE, "zcat $_ |");
+ process_mail_file($dbh, $1);
+ }
+ else
+ {
+ open(FILE, $_);
+ process_mail_file($dbh, $_);
+ }
+ }
+ }
+ $dbh->disconnect if (!$opt_test);
+
+ $ignored = ($mail_no_from_f + $mail_no_subject_f + $mail_no_txt_f +
+ $mail_too_big + $mail_duplicates + $mail_fixed);
+ print "################################ Mail Report #################################\n\n";
+ print "Mails inserted:\t\t\t\t\t$mail_inserted\n";
+ print "--------------- ";
+ print "=" . "=" x length("$mail_inserted") . "=\n\n";
+ if ($ignored)
+ {
+ print "Ignored mails\n";
+ print "-------------\n";
+ if ($mail_no_from_f)
+ {
+ print "Reason: mail without \"From:\" -field:\t\t$mail_no_from_f\n";
+ }
+ else
+ {
+ print "";
+ }
+ if ($mail_no_txt_f)
+ {
+ print "Reason: mail without message:\t\t\t$mail_no_txt_f\n";
+ }
+ else
+ {
+ print "";
+ }
+ if ($mail_no_subject_f)
+ {
+ print "Reason: mail without subject:\t\t\t$mail_no_subject_f\n";
+ }
+ else
+ {
+ print "";
+ }
+ if ($mail_too_big)
+ {
+ print "Reason: mail too big, over $opt_max_mail_size bytes:\t\t";
+ print $mail_too_big;
+ print " (see --max_mail_size=#)\n";
+ }
+ else
+ {
+ print "";
+ }
+ if ($mail_duplicates)
+ {
+ print "Reason: duplicate mail, or in db already:\t$mail_duplicates\n";
+ }
+ else
+ {
+ print "";
+ }
+ if ($mail_fixed)
+ {
+ print "Reason: mail was an unsubscribe - mail:\t\t$mail_fixed\n";
+ }
+ else
+ {
+ print "";
+ }
+ print " ";
+ print "=" . "=" x length("$ignored") . "=\n";
+ print "Total number of ignored mails:\t\t\t$ignored\n\n";
+ }
+ print "Total number of mails:\t\t\t\t";
+ print $mail_inserted + $ignored;
+ print " (OK: ";
+ print sprintf("%.1f", ($mail_inserted + $ignored) ? (($mail_inserted / ($mail_inserted+$ignored)) * 100) : 0.0);
+ print "% Ignored: ";
+ print sprintf("%.1f", ($mail_inserted + $ignored) ? (($ignored / ($mail_inserted + $ignored)) * 100) : 0);
+ print "%)\n";
+ print "################################ End Report ##################################\n";
+ exit(0);
+}
+
+####
+#### table creation
+####
+
+sub create_table
+{
+ my ($dbh)= @_;
+ my ($sth, $query);
+
+ $query= <<EOF;
+CREATE TABLE my_mail
+(
+ mail_id MEDIUMINT UNSIGNED NOT NULL auto_increment,
+ message_id VARCHAR(255),
+ in_reply_to VARCHAR(255),
+ date DATETIME NOT NULL,
+ time_zone VARCHAR(20),
+ mail_from VARCHAR(120) NOT NULL,
+ reply VARCHAR(120),
+ mail_to TEXT,
+ cc TEXT,
+ sbj VARCHAR(200),
+ txt MEDIUMTEXT NOT NULL,
+ file VARCHAR(64) NOT NULL,
+ hash INTEGER NOT NULL,
+ KEY (mail_id),
+ KEY (message_id),
+ KEY (in_reply_to),
+ PRIMARY KEY (mail_from, date, hash))
+ ENGINE=MyISAM COMMENT=''
+EOF
+ $sth = $dbh->prepare($query) or die $DBI::errstr;
+ $sth->execute() or die "Couldn't create table: $DBI::errstr\n";
+}
+
+####
+#### inbox processing. Can be either a real file, or standard input.
+####
+
+sub process_mail_file
+{
+ my ($dbh, $file_name) = @_;
+ my (%values, $type, $check);
+
+ $file_name =~ s/.*[\/]// if ($opt_no_path);
+
+ %values = ();
+ $type = "";
+ $check = 0;
+ while (<FILE>)
+ {
+ chop;
+ chop if (substr($_, -1, 1) eq "\r");
+ if ($type ne "message")
+ {
+ if (/^Reply-To:\s*(.*)/i)
+ {
+ $type = "reply";
+ $values{$type} = $1;
+ }
+ elsif (/^From: (.*)/i)
+ {
+ $type = "from";
+ $values{$type} = $1;
+ }
+ elsif (/^To: (.*)/i)
+ {
+ $type = "to";
+ $values{$type} = $1;
+ }
+ elsif (/^Cc: (.*)/i)
+ {
+ $type = "cc";
+ $values{$type} = $1;
+ }
+ elsif (/^Subject: (.*)/i)
+ {
+ $type = "subject";
+ $values{$type} = $1;
+ }
+ elsif (/^Message-Id:\s*(.*)/i)
+ {
+ $type = "message_id";
+ s/^\s*(<.*>)\s*/$1/;
+ $values{$type} = $1;
+ }
+ elsif (/^In-Reply-To:\s*(.*)/i)
+ {
+ $type = "in_reply_to";
+ s/^\s*(<.*>)\s*/$1/;
+ $values{$type} = $1;
+ }
+ elsif (/^Date: (.*)/i)
+ {
+ date_parser($1, \%values, $file_name);
+ $type = "rubbish";
+ }
+ # Catch those fields that we don't or can't handle (yet)
+ elsif (/^[\w\W-]+:/)
+ {
+ $type = "rubbish";
+ }
+ elsif ($_ eq "")
+ {
+ $type = "message";
+ $values{$type} = "";
+ }
+ else
+ {
+ s/^\s*/ /;
+ if ($type eq 'message_id' || $type eq 'in_reply_to')
+ {
+ s/^\s*(<.*>)\s*/$1/;
+ }
+ $values{$type} .= $_;
+ }
+ }
+ elsif ($check != 0 && $_ ne "") # in case of forwarded messages
+ {
+ $values{$type} .= "\n" . $_;
+ $check--;
+ }
+ elsif (/^From .* \d\d:\d\d:\d\d\s\d\d\d\d/ ||
+ /^From .* \d\d\d\d\s\d\d:\d\d:\d\d/)
+ {
+ $values{'hash'} = checksum("$values{'message'}");
+ update_table($dbh, $file_name, \%values);
+ %values = ();
+ $type = "";
+ $check = 0;
+ }
+ elsif (/-* forwarded message .*-*/i) # in case of forwarded messages
+ {
+ $values{$type} .= "\n" . $_;
+ $check++;
+ $mail_forwarded++;
+ }
+ else
+ {
+ $values{$type} .= "\n" . $_;
+ }
+ }
+ if (defined($values{'message'}))
+ {
+ $values{'hash'} = checksum("$values{'message'}");
+ update_table($dbh, $file_name, \%values);
+ }
+}
+
+####
+#### get date and timezone
+####
+
+sub date_parser
+{
+ my ($date_raw, $values, $file_name, $tmp) = @_;
+
+ # If you ever need to change this test, be especially careful with
+ # the timezone; it may be just a number (-0600), or just a name (EET), or
+ # both (-0600 (EET), or -0600 (EET GMT)), or without parenthesis: GMT.
+ # You probably should use a 'greedy' regexp in the end
+ $date_raw =~ /^\D*(\d{1,2})\s+(\w+)\s+(\d{2,4})\s+(\d+:\d+)(:\d+)?\s*(\S+.*)?/;
+
+ if (!defined($1) || !defined($2) || !defined($3) || !defined($4) ||
+ !defined($months{$2}))
+ {
+ if ($opt_debug || $opt_stop_on_error)
+ {
+ print "FAILED: date_parser: 1: $1 2: $2 3: $3 4: $4 5: $5\n";
+ print "months{2}: $months{$2}\n";
+ print "date_raw: $date_raw\n";
+ print "Inbox filename: $file_name\n";
+ }
+ exit(1) if ($opt_stop_on_error);
+ $values->{'date'} = "";
+ $values->{'time_zone'} = "";
+ return;
+ }
+ $tmp = $3 . "-" . $months{$2} . "-" . "$1 $4";
+ $tmp.= defined($5) ? $5 : ":00";
+ $values->{'date'} = $tmp;
+ print "INSERTING DATE: $tmp\n" if ($opt_debug);
+ $values->{'time_zone'} = $6;
+}
+
+####
+#### Insert to table
+####
+
+sub update_table
+{
+ my($dbh, $file_name, $values) = @_;
+ my($q, $tail, $message);
+
+ if (!defined($values->{'subject'}) || !defined($values->{'to'}))
+ {
+ $mail_no_subject_f++;
+ return; # Ignore these
+ }
+ $message = $values->{'message'};
+ $message =~ s/^\s*//; # removes whitespaces from the beginning
+
+ restart:
+ $message =~ s/[\s\n>]*$//; # removes whitespaces and '>' from the end
+ $values->{'message'} = $message;
+ foreach $tail (@remove_tail)
+ {
+ $message =~ s/$tail//;
+ }
+ if ($message ne $values->{'message'})
+ {
+ $message =~ s/\s*$//; # removes whitespaces from the end
+ $mail_fixed++;
+ goto restart; # Some mails may have duplicated messages
+ }
+
+ $q = "INSERT INTO my_mail (";
+ $q.= "mail_id,";
+ $q.= "message_id,";
+ $q.= "in_reply_to,";
+ $q.= "date,";
+ $q.= "time_zone,";
+ $q.= "mail_from,";
+ $q.= "reply,";
+ $q.= "mail_to,";
+ $q.= "cc,";
+ $q.= "sbj,";
+ $q.= "txt,";
+ $q.= "file,";
+ $q.= "hash";
+ $q.= ") VALUES (";
+ $q.= "NULL,";
+ $q.= (defined($values->{'message_id'}) ?
+ $dbh->quote($values->{'message_id'}) : "NULL");
+ $q.= ",";
+ $q.= (defined($values->{'in_reply_to'}) ?
+ $dbh->quote($values->{'in_reply_to'}) : "NULL");
+ $q.= ",";
+ $q.= "'" . $values->{'date'} . "',";
+ $q.= (defined($values->{'time_zone'}) ?
+ $dbh->quote($values->{'time_zone'}) : "NULL");
+ $q.= ",";
+ $q.= defined($values->{'from'}) ? $dbh->quote($values->{'from'}) : "NULL";
+ $q.= ",";
+ $q.= defined($values->{'reply'}) ? $dbh->quote($values->{'reply'}) : "NULL";
+ $q.= ",";
+ $q.= defined($values->{'to'}) ? $dbh->quote($values->{'to'}) : "NULL";
+ $q.= ",";
+ $q.= defined($values->{'cc'}) ? $dbh->quote($values->{'cc'}) : "NULL";
+ $q.= ",";
+ $q.= $dbh->quote($values->{'subject'});
+ $q.= ",";
+ $q.= $dbh->quote($message);
+ $q.= ",";
+ $q.= $dbh->quote($file_name);
+ $q.= ",";
+ $q.= "'" . $values->{'hash'} . "'";
+ $q.= ")";
+
+ # Don't insert mails bigger than $opt_max_mail_size
+ if (length($message) > $opt_max_mail_size)
+ {
+ $mail_too_big++;
+ }
+ # Don't insert mails without 'From' field
+ elsif (!defined($values->{'from'}) || $values->{'from'} eq "")
+ {
+ $mail_no_from_f++;
+ }
+ elsif ($opt_test)
+ {
+ print "$q\n";
+ $mail_inserted++;
+ }
+ # Don't insert mails without the 'message'
+ elsif ($message eq "")
+ {
+ $mail_no_txt_f++;
+ }
+ elsif ($dbh->do($q))
+ {
+ $mail_inserted++;
+ }
+ # This should never happen. This means that the above q failed,
+ # but it wasn't because of a duplicate mail entry
+ elsif (!($DBI::errstr =~ /Duplicate entry /))
+ {
+ die "FATAL: Got error :$DBI::errstr\nAttempted query was: $q\n";
+ }
+ else
+ {
+ $mail_duplicates++;
+ print "Duplicate mail: query: $q\n" if ($opt_debug);
+ }
+ $q = "";
+}
+
+####
+#### In case you have two identical messages we wanted to identify them
+#### and remove additionals; We do this by calculating a hash number of the
+#### message and ignoring messages with the same from, date and hash.
+#### This function calculates a simple 32 bit hash value for the message.
+####
+
+sub checksum
+{
+ my ($txt)= @_;
+ my ($crc, $i, $count);
+ $count = length($txt);
+ for ($crc = $i = 0; $i < $count ; $i++)
+ {
+ $crc = (($crc << 1) + (ord (substr ($txt, $i, 1)))) +
+ (($crc & (1 << 30)) ? 1 : 0);
+ $crc &= ((1 << 31) -1);
+ }
+ return $crc;
+}
+
+####
+#### my_which is used, because we can't assume that every system has the
+#### which -command. my_which can take only one argument at a time.
+#### Return values: requested system command with the first found path,
+#### or undefined, if not found.
+####
+
+sub my_which
+{
+ my ($command) = @_;
+ my (@paths, $path);
+
+ return $command if (-f $command && -x $command);
+ @paths = split(':', $ENV{'PATH'});
+ foreach $path (@paths)
+ {
+ $path = "." if ($path eq "");
+ $path .= "/$command";
+ return $path if (-f $path && -x $path);
+ }
+ return undef();
+}
+
+####
+#### usage and version
+####
+
+sub usage
+{
+ my ($VER)= @_;
+
+ if ($opt_version)
+ {
+ print "$progname version $VER\n";
+ }
+ else
+ {
+ print <<EOF;
+$progname version $VER
+
+Description: Insert mails from inbox file(s) into a table. This program
+can read group [mail_to_db] from the my.cnf file. You may want to have db
+and table set there at least.
+
+Usage: $progname [options] file1 [file2 file3 ...]
+or: $progname [options] --create [file1 file2...]
+or: cat inbox | $progname [options] --stdin
+
+The last example can be used to read mails from standard input and can
+useful when inserting mails to database via a program 'on-the-fly'.
+The filename will be 'READ-FROM-STDIN' in this case.
+
+Options:
+--help Show this help and exit.
+--version Show the version number and exit.
+--debug Print some extra information during the run.
+--host=... Hostname to be used.
+--port=# TCP/IP port to be used with connection.
+--socket=... MySQL UNIX socket to be used with connection.
+--db=... Database to be used.
+--user=... Username for connecting.
+--password=... Password for the user.
+--stdin Read mails from stdin.
+--max_mail_size=# Maximum size of a mail in bytes.
+ Beware of the downside letting this variable be too big;
+ you may easily end up inserting a lot of attached
+ binary files (like MS Word documents etc), which take
+ space, make the database slower and are not really
+ searchable anyway. (Default $opt_max_mail_size)
+--create Create the mails table. This can be done with the first run.
+--test Dry run. Print the queries and the result as it would be.
+--no_path When inserting the file name, leave out any paths of
+ the name.
+--stop_on_error Stop the run, if an unexpected, but not fatal error occurs
+ during the run. Without this option some fields may get
+ unwanted values. --debug will also report about these.
+EOF
+ }
+ exit(0);
+}
diff --git a/tests/myisam-big-rows.tst b/tests/myisam-big-rows.tst
new file mode 100644
index 00000000..97147999
--- /dev/null
+++ b/tests/myisam-big-rows.tst
@@ -0,0 +1,72 @@
+#
+# Test rows with length above > 16M
+# Note that for this to work, you should start mysqld with
+# --max_allowed_packet=32M
+#
+
+drop table if exists t1;
+create table t1 (a tinyint not null auto_increment, b longblob not null, primary key (a)) checksum=1;
+
+insert into t1 (b) values(repeat(char(65),10));
+insert into t1 (b) values(repeat(char(66),10));
+insert into t1 (b) values(repeat(char(67),10));
+update t1 set b=repeat(char(68),16777216) where a=1;
+check table t1;
+update t1 set b=repeat(char(69),16777000) where a=2;
+update t1 set b=repeat(char(70),167) where a=3;
+update t1 set b=repeat(char(71),16778000) where a=1;
+update t1 set b=repeat(char(72),16778000) where a=3;
+select a,length(b) from t1;
+set @a=1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+update t1 set b=('A') where a=5;
+delete from t1 where a=7;
+set @a=@a+1;
+insert into t1 (b) values (repeat(char(73+@a),16777200+@a));
+update t1 set b=repeat(char(73+@a+1),17000000+@a) where a=last_insert_id();
+
+select a,mid(b,1,5),length(b) from t1;
+check table t1;
+repair table t1;
+check table t1;
+select a from table where b<>repeat(mid(b,1,1),length(b));
+delete from t1 where (a & 1);
+select a from table where b<>repeat(mid(b,1,1),length(b));
+check table t1;
+repair table t1;
+check table t1;
+drop table t1;
diff --git a/tests/mysql_client_fw.c b/tests/mysql_client_fw.c
new file mode 100644
index 00000000..5c0c7ce2
--- /dev/null
+++ b/tests/mysql_client_fw.c
@@ -0,0 +1,1509 @@
+/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <mysql.h>
+#include <errmsg.h>
+#include <my_compare.h>
+#include <my_getopt.h>
+#include <m_string.h>
+#include <mysqld_error.h>
+#include <mysql_version.h>
+#include <sql_common.h>
+#include <mysql/client_plugin.h>
+
+/*
+ If non_blocking_api_enabled is true, we will re-define all the blocking
+ API functions as wrappers that call the corresponding non-blocking API
+ and use poll()/select() to wait for them to complete. This way we can get
+ a good coverage testing of the non-blocking API as well.
+*/
+static my_bool non_blocking_api_enabled= 0;
+#if !defined(EMBEDDED_LIBRARY)
+#define WRAP_NONBLOCK_ENABLED non_blocking_api_enabled
+#include "nonblock-wrappers.h"
+#endif
+
+#define VER "2.1"
+#define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */
+#define MAX_KEY MAX_INDEXES
+#define MAX_SERVER_ARGS 64
+
+/* set default options */
+static int opt_testcase __attribute__((unused)) = 0;
+static char *opt_db= 0;
+static char *opt_user= 0;
+static char *opt_password= 0;
+static char *opt_host= 0;
+static char *opt_unix_socket= 0;
+static unsigned int opt_port;
+static my_bool tty_password= 0, opt_silent= 0;
+
+static MYSQL *mysql= 0;
+static char current_db[]= "client_test_db";
+static unsigned int test_count= 0;
+static unsigned int opt_count= 0;
+static unsigned int opt_count_read= 0;
+static unsigned int iter_count= 0;
+static my_bool have_innodb= FALSE;
+static char *opt_plugin_dir= 0, *opt_default_auth= 0;
+static unsigned int opt_drop_db= 1;
+
+static const char *opt_basedir= "./";
+static const char *opt_vardir= "mysql-test/var";
+static char mysql_charsets_dir[FN_REFLEN+1];
+
+static longlong opt_getopt_ll_test= 0;
+
+static char **defaults_argv;
+static int original_argc;
+static char **original_argv;
+static int embedded_server_arg_count= 0;
+static char *embedded_server_args[MAX_SERVER_ARGS];
+
+static const char *embedded_server_groups[]= {
+ "server",
+ "embedded",
+ "mysql_client_test_SERVER",
+ NullS
+};
+
+static time_t start_time, end_time;
+static double total_time;
+
+const char *default_dbug_option= "d:t:o,/tmp/mysql_client_test.trace";
+
+struct my_tests_st
+{
+ const char *name;
+ void (*function)();
+};
+
+#define myheader(str) \
+DBUG_PRINT("test", ("name: %s", str)); \
+ if (opt_silent < 2) \
+ { \
+ fprintf(stdout, "\n\n#####################################\n"); \
+ fprintf(stdout, "%u of (%u/%u): %s", test_count++, iter_count, \
+ opt_count, str); \
+ fprintf(stdout, " \n#####################################\n"); \
+ fflush(stdout); \
+ }
+
+#define myheader_r(str) \
+DBUG_PRINT("test", ("name: %s", str)); \
+ if (!opt_silent) \
+ { \
+ fprintf(stdout, "\n\n#####################################\n"); \
+ fprintf(stdout, "%s", str); \
+ fprintf(stdout, " \n#####################################\n"); \
+ fflush(stdout); \
+ }
+
+static void print_error(const char *msg);
+static void print_st_error(MYSQL_STMT *stmt, const char *msg);
+static void client_disconnect(MYSQL* mysql);
+static void get_options(int *argc, char ***argv);
+
+
+/*
+ Abort unless given experssion is non-zero.
+
+ SYNOPSIS
+ DIE_UNLESS(expr)
+
+ DESCRIPTION
+ We can't use any kind of system assert as we need to
+ preserve tested invariants in release builds as well.
+*/
+
+#define DIE_UNLESS(expr) \
+ ((void) ((expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0)))
+#define DIE_IF(expr) \
+ ((void) ((expr) ? (die(__FILE__, __LINE__, #expr), 0) : 0))
+#define DIE(expr) \
+ die(__FILE__, __LINE__, #expr)
+
+static void die(const char *file, int line, const char *expr)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s:%d: check failed: '%s'\n", file, line, expr);
+ fprintf(stderr, "MySQL error %d: %s\n", mysql_errno(0), mysql_error(0));
+ fflush(stderr);
+ exit(1);
+}
+
+
+#define myerror(msg) print_error(msg)
+#define mysterror(stmt, msg) print_st_error(stmt, msg)
+
+#define myquery(RES) \
+{ \
+ int r= (RES); \
+ if (r) \
+ myerror(NULL); \
+ DIE_UNLESS(r == 0); \
+}
+
+#define myquery_r(r) \
+{ \
+ if (r) \
+ myerror(NULL); \
+ DIE_UNLESS(r != 0); \
+}
+
+#define check_execute(stmt, r) \
+{ \
+ if (r) \
+ mysterror(stmt, NULL); \
+ DIE_UNLESS(r == 0); \
+}
+
+#define check_execute_r(stmt, r) \
+{ \
+ if (r) \
+ mysterror(stmt, NULL); \
+ DIE_UNLESS(r != 0); \
+}
+
+#define check_stmt(stmt) \
+{ \
+ if ( stmt == 0) \
+ myerror(NULL); \
+ DIE_UNLESS(stmt != 0); \
+}
+
+#define check_stmt_r(stmt) \
+{ \
+ if (stmt == 0) \
+ myerror(NULL); \
+ DIE_UNLESS(stmt == 0); \
+}
+
+#define mytest(x) if (!(x)) {myerror(NULL);DIE_UNLESS(FALSE);}
+#define mytest_r(x) if ((x)) {myerror(NULL);DIE_UNLESS(FALSE);}
+
+
+/* A workaround for Sun Forte 5.6 on Solaris x86 */
+
+static int cmp_double(double *a, double *b)
+{
+ return *a == *b;
+}
+
+
+/* Print the error message */
+
+static void print_error(const char *msg)
+{
+ if (!opt_silent)
+ {
+ if (mysql && mysql_errno(mysql))
+ {
+ if (mysql->server_version)
+ fprintf(stdout, "\n [MySQL-%s]", mysql->server_version);
+ else
+ fprintf(stdout, "\n [MySQL]");
+ fprintf(stdout, "[%d] %s\n", mysql_errno(mysql), mysql_error(mysql));
+ }
+ else if (msg)
+ fprintf(stderr, " [MySQL] %s\n", msg);
+ }
+}
+
+
+static void print_st_error(MYSQL_STMT *stmt, const char *msg)
+{
+ if (!opt_silent)
+ {
+ if (stmt && mysql_stmt_errno(stmt))
+ {
+ if (stmt->mysql && stmt->mysql->server_version)
+ fprintf(stdout, "\n [MySQL-%s]", stmt->mysql->server_version);
+ else
+ fprintf(stdout, "\n [MySQL]");
+
+ fprintf(stdout, "[%d] %s\n", mysql_stmt_errno(stmt),
+ mysql_stmt_error(stmt));
+ }
+ else if (msg)
+ fprintf(stderr, " [MySQL] %s\n", msg);
+ }
+}
+
+/*
+ Enhanced version of mysql_client_init(), which may also set shared memory
+ base on Windows.
+*/
+static MYSQL *mysql_client_init(MYSQL* con)
+{
+ MYSQL* res = mysql_init(con);
+ if (res && non_blocking_api_enabled)
+ mysql_options(res, MYSQL_OPT_NONBLOCK, 0);
+ if (opt_plugin_dir && *opt_plugin_dir)
+ mysql_options(res, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+ if (opt_default_auth && *opt_default_auth)
+ mysql_options(res, MYSQL_DEFAULT_AUTH, opt_default_auth);
+ return res;
+}
+
+/*
+ Disable direct calls of mysql_init, as it disregards shared memory base.
+*/
+#define mysql_init(A) Please use mysql_client_init instead of mysql_init
+
+
+/* Check if the connection has InnoDB tables */
+
+static my_bool check_have_innodb(MYSQL *conn)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ int rc;
+ my_bool result= FALSE;
+
+ rc= mysql_query(conn,
+ "SELECT (support = 'YES' or support = 'DEFAULT' or support = 'ENABLED') "
+ "AS `TRUE` FROM information_schema.engines WHERE engine = 'innodb'");
+ myquery(rc);
+ res= mysql_use_result(conn);
+ DIE_UNLESS(res);
+
+ row= mysql_fetch_row(res);
+ DIE_UNLESS(row);
+
+ if (row[0] && row[1])
+ result= strcmp(row[1], "1") == 0;
+ mysql_free_result(res);
+ return result;
+}
+
+
+/*
+ This is to be what mysql_query() is for mysql_real_query(), for
+ mysql_simple_prepare(): a variant without the 'length' parameter.
+*/
+
+static MYSQL_STMT *STDCALL
+mysql_simple_prepare(MYSQL *mysql_arg, const char *query)
+{
+ MYSQL_STMT *stmt= mysql_stmt_init(mysql_arg);
+ if (stmt && mysql_stmt_prepare(stmt, query, (uint) strlen(query)))
+ {
+ mysql_stmt_close(stmt);
+ return 0;
+ }
+ return stmt;
+}
+
+
+/**
+ Connect to the server with options given by arguments to this application,
+ stored in global variables opt_host, opt_user, opt_password, opt_db,
+ opt_port and opt_unix_socket.
+
+ @param flag[in] client_flag passed on to mysql_real_connect
+ @param protocol[in] MYSQL_PROTOCOL_* to use for this connection
+ @param auto_reconnect[in] set to 1 for auto reconnect
+
+ @return pointer to initialized and connected MYSQL object
+*/
+static MYSQL* client_connect(ulong flag, uint protocol, my_bool auto_reconnect)
+{
+ MYSQL* mysql;
+ int rc;
+ static char query[MAX_TEST_QUERY_LENGTH];
+ myheader_r("client_connect");
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a connection to '%s' ...",
+ opt_host ? opt_host : "");
+
+ if (!(mysql= mysql_client_init(NULL)))
+ {
+ opt_silent= 0;
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+ /* enable local infile, in non-binary builds often disabled by default */
+ mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0);
+ mysql_options(mysql, MYSQL_OPT_PROTOCOL, &protocol);
+ if (opt_plugin_dir && *opt_plugin_dir)
+ mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+ if (opt_default_auth && *opt_default_auth)
+ mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
+
+ if (!(mysql_real_connect(mysql, opt_host, opt_user,
+ opt_password, opt_db ? opt_db:"test", opt_port,
+ opt_unix_socket, flag)))
+ {
+ opt_silent= 0;
+ myerror("connection failed");
+ mysql_close(mysql);
+ fprintf(stdout, "\n Check the connection options using --help or -?\n");
+ exit(1);
+ }
+ mysql_options(mysql, MYSQL_OPT_RECONNECT, &auto_reconnect);
+
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+
+ /* set AUTOCOMMIT to ON*/
+ mysql_autocommit(mysql, TRUE);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\nConnected to MySQL server version: %s (%lu)\n",
+ mysql_get_server_info(mysql),
+ (ulong) mysql_get_server_version(mysql));
+ fprintf(stdout, "\n Creating a test database '%s' ...", current_db);
+ }
+ strxmov(query, "CREATE DATABASE IF NOT EXISTS ", current_db, NullS);
+
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ strxmov(query, "USE ", current_db, NullS);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+ have_innodb= check_have_innodb(mysql);
+
+ if (!opt_silent)
+ fprintf(stdout, "OK\n");
+
+ return mysql;
+}
+
+
+/* Close the connection */
+
+static void client_disconnect(MYSQL* mysql)
+{
+ static char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader_r("client_disconnect");
+
+ if (mysql)
+ {
+ if (opt_drop_db)
+ {
+ if (!opt_silent)
+ fprintf(stdout, "\n dropping the test database '%s' ...", current_db);
+ strxmov(query, "DROP DATABASE IF EXISTS ", current_db, NullS);
+
+ mysql_query(mysql, query);
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+ }
+
+ if (!opt_silent)
+ fprintf(stdout, "\n closing the connection ...");
+ mysql_close(mysql);
+ if (!opt_silent)
+ fprintf(stdout, "OK\n");
+ }
+}
+
+
+/* Print dashes */
+
+static void my_print_dashes(MYSQL_RES *result)
+{
+ MYSQL_FIELD *field;
+ unsigned int i, j;
+
+ mysql_field_seek(result, 0);
+ fputc('\t', stdout);
+ fputc('+', stdout);
+
+ for(i= 0; i< mysql_num_fields(result); i++)
+ {
+ field= mysql_fetch_field(result);
+ for(j= 0; j < field->max_length+2; j++)
+ fputc('-', stdout);
+ fputc('+', stdout);
+ }
+ fputc('\n', stdout);
+}
+
+
+/* Print resultset metadata information */
+
+static void my_print_result_metadata(MYSQL_RES *result)
+{
+ MYSQL_FIELD *field;
+ unsigned int i, j;
+ unsigned int field_count;
+
+ mysql_field_seek(result, 0);
+ if (!opt_silent)
+ {
+ fputc('\n', stdout);
+ fputc('\n', stdout);
+ }
+
+ field_count= mysql_num_fields(result);
+ for(i= 0; i< field_count; i++)
+ {
+ field= mysql_fetch_field(result);
+ j= strlen(field->name);
+ if (j < field->max_length)
+ j= field->max_length;
+ if (j < 4 && !IS_NOT_NULL(field->flags))
+ j= 4;
+ field->max_length= j;
+ }
+ if (!opt_silent)
+ {
+ my_print_dashes(result);
+ fputc('\t', stdout);
+ fputc('|', stdout);
+ }
+
+ mysql_field_seek(result, 0);
+ for(i= 0; i< field_count; i++)
+ {
+ field= mysql_fetch_field(result);
+ if (!opt_silent)
+ fprintf(stdout, " %-*s |", (int) field->max_length, field->name);
+ }
+ if (!opt_silent)
+ {
+ fputc('\n', stdout);
+ my_print_dashes(result);
+ }
+}
+
+
+/* Process the result set */
+
+static int my_process_result_set(MYSQL_RES *result)
+{
+ MYSQL_ROW row;
+ MYSQL_FIELD *field;
+ unsigned int i;
+ unsigned int row_count= 0;
+
+ if (!result)
+ return 0;
+
+ my_print_result_metadata(result);
+
+ while ((row= mysql_fetch_row(result)) != NULL)
+ {
+ mysql_field_seek(result, 0);
+ if (!opt_silent)
+ {
+ fputc('\t', stdout);
+ fputc('|', stdout);
+ }
+
+ for(i= 0; i< mysql_num_fields(result); i++)
+ {
+ field= mysql_fetch_field(result);
+ if (!opt_silent)
+ {
+ if (row[i] == NULL)
+ fprintf(stdout, " %-*s |", (int) field->max_length, "NULL");
+ else if (IS_NUM(field->type))
+ fprintf(stdout, " %*s |", (int) field->max_length, row[i]);
+ else
+ fprintf(stdout, " %-*s |", (int) field->max_length, row[i]);
+ }
+ }
+ if (!opt_silent)
+ {
+ fputc('\t', stdout);
+ fputc('\n', stdout);
+ }
+ row_count++;
+ }
+ if (!opt_silent)
+ {
+ if (row_count)
+ my_print_dashes(result);
+
+ if (mysql_errno(mysql) != 0)
+ fprintf(stderr, "\n\tmysql_fetch_row() failed\n");
+ else
+ fprintf(stdout, "\n\t%d %s returned\n", row_count,
+ row_count == 1 ? "row" : "rows");
+ }
+ return row_count;
+}
+
+
+static int my_process_result(MYSQL *mysql_arg)
+{
+ MYSQL_RES *result;
+ int row_count;
+
+ if (!(result= mysql_store_result(mysql_arg)))
+ return 0;
+
+ row_count= my_process_result_set(result);
+
+ mysql_free_result(result);
+ return row_count;
+}
+
+
+/* Process the statement result set */
+
+#define MAX_RES_FIELDS 50
+#define MAX_FIELD_DATA_SIZE 255
+
+static int my_process_stmt_result(MYSQL_STMT *stmt)
+{
+ int field_count;
+ int row_count= 0;
+ MYSQL_BIND buffer[MAX_RES_FIELDS];
+ MYSQL_FIELD *field;
+ MYSQL_RES *result;
+ char data[MAX_RES_FIELDS][MAX_FIELD_DATA_SIZE];
+ ulong length[MAX_RES_FIELDS];
+ my_bool is_null[MAX_RES_FIELDS];
+ int rc, i;
+
+ if (!(result= mysql_stmt_result_metadata(stmt))) /* No meta info */
+ {
+ while (!mysql_stmt_fetch(stmt))
+ row_count++;
+ return row_count;
+ }
+
+ field_count= MY_MIN(mysql_num_fields(result), MAX_RES_FIELDS);
+
+ bzero((char*) buffer, sizeof(buffer));
+ bzero((char*) length, sizeof(length));
+ bzero((char*) is_null, sizeof(is_null));
+
+ for(i= 0; i < field_count; i++)
+ {
+ buffer[i].buffer_type= MYSQL_TYPE_STRING;
+ buffer[i].buffer_length= MAX_FIELD_DATA_SIZE;
+ buffer[i].length= &length[i];
+ buffer[i].buffer= (void *) data[i];
+ buffer[i].is_null= &is_null[i];
+ }
+
+ rc= mysql_stmt_bind_result(stmt, buffer);
+ check_execute(stmt, rc);
+
+ rc= 1;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*)&rc);
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+ my_print_result_metadata(result);
+
+ mysql_field_seek(result, 0);
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
+ {
+ if (!opt_silent)
+ {
+ fputc('\t', stdout);
+ fputc('|', stdout);
+ }
+ mysql_field_seek(result, 0);
+ for (i= 0; i < field_count; i++)
+ {
+ field= mysql_fetch_field(result);
+ if (!opt_silent)
+ {
+ if (is_null[i])
+ fprintf(stdout, " %-*s |", (int) field->max_length, "NULL");
+ else if (length[i] == 0)
+ {
+ data[i][0]= '\0'; /* unmodified buffer */
+ fprintf(stdout, " %*s |", (int) field->max_length, data[i]);
+ }
+ else if (IS_NUM(field->type))
+ fprintf(stdout, " %*s |", (int) field->max_length, data[i]);
+ else
+ fprintf(stdout, " %-*s |", (int) field->max_length, data[i]);
+ }
+ }
+ if (!opt_silent)
+ {
+ fputc('\t', stdout);
+ fputc('\n', stdout);
+ }
+ row_count++;
+ }
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ if (!opt_silent)
+ {
+ if (row_count)
+ my_print_dashes(result);
+ fprintf(stdout, "\n\t%d %s returned\n", row_count,
+ row_count == 1 ? "row" : "rows");
+ }
+ mysql_free_result(result);
+ return row_count;
+}
+
+
+/* Prepare statement, execute, and process result set for given query */
+
+int my_stmt_result(const char *buff)
+{
+ MYSQL_STMT *stmt;
+ int row_count;
+ int rc;
+
+ if (!opt_silent)
+ fprintf(stdout, "\n\n %s", buff);
+ stmt= mysql_simple_prepare(mysql, buff);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ row_count= my_process_stmt_result(stmt);
+ mysql_stmt_close(stmt);
+
+ return row_count;
+}
+
+/* Print the total number of warnings and the warnings themselves. */
+
+void my_process_warnings(MYSQL *conn, unsigned expected_warning_count)
+{
+ MYSQL_RES *result;
+ int rc;
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total warnings: %u (expected: %u)\n",
+ mysql_warning_count(conn), expected_warning_count);
+
+ DIE_UNLESS(mysql_warning_count(mysql) == expected_warning_count);
+
+ rc= mysql_query(conn, "SHOW WARNINGS");
+ DIE_UNLESS(rc == 0);
+
+ result= mysql_store_result(conn);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ mysql_free_result(result);
+}
+
+
+/* Utility function to verify a particular column data */
+
+static void verify_col_data(const char *table, const char *col,
+ const char *exp_data)
+{
+ static char query[MAX_TEST_QUERY_LENGTH];
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ int rc, field= 1;
+
+ if (table && col)
+ {
+ strxmov(query, "SELECT ", col, " FROM ", table, " LIMIT 1", NullS);
+ if (!opt_silent)
+ fprintf(stdout, "\n %s", query);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ field= 0;
+ }
+
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ if (!(row= mysql_fetch_row(result)) || !row[field])
+ {
+ fprintf(stdout, "\n *** ERROR: FAILED TO GET THE RESULT ***");
+ exit(1);
+ }
+ if (strcmp(row[field], exp_data))
+ {
+ fprintf(stdout, "\n obtained: `%s` (expected: `%s`)",
+ row[field], exp_data);
+ DIE_UNLESS(FALSE);
+ }
+ mysql_free_result(result);
+}
+
+
+/* Utility function to verify the field members */
+
+#define verify_prepare_field(result,no,name,org_name,type,table,\
+ org_table,db,length,def) \
+ do_verify_prepare_field((result),(no),(name),(org_name),(type), \
+ (table),(org_table),(db),(length),(def), \
+ __FILE__, __LINE__)
+
+static void do_verify_prepare_field(MYSQL_RES *result,
+ unsigned int no, const char *name,
+ const char *org_name,
+ enum enum_field_types type,
+ const char *table,
+ const char *org_table, const char *db,
+ unsigned long length, const char *def,
+ const char *file, int line)
+{
+ MYSQL_FIELD *field;
+ CHARSET_INFO *cs;
+ ulonglong expected_field_length= length;
+
+ if (!(field= mysql_fetch_field_direct(result, no)))
+ {
+ fprintf(stdout, "\n *** ERROR: FAILED TO GET THE RESULT ***");
+ exit(1);
+ }
+ cs= get_charset(field->charsetnr, 0);
+ DIE_UNLESS(cs);
+ if ((expected_field_length*= cs->mbmaxlen) > UINT_MAX32)
+ expected_field_length= UINT_MAX32;
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n field[%d]:", no);
+ fprintf(stdout, "\n name :`%s`\t(expected: `%s`)", field->name, name);
+ fprintf(stdout, "\n org_name :`%s`\t(expected: `%s`)",
+ field->org_name, org_name);
+ fprintf(stdout, "\n type :`%d`\t(expected: `%d`)", field->type, type);
+ if (table)
+ fprintf(stdout, "\n table :`%s`\t(expected: `%s`)",
+ field->table, table);
+ if (org_table)
+ fprintf(stdout, "\n org_table:`%s`\t(expected: `%s`)",
+ field->org_table, org_table);
+ fprintf(stdout, "\n database :`%s`\t(expected: `%s`)", field->db, db);
+ fprintf(stdout, "\n length :`%lu`\t(expected: `%llu`)",
+ field->length, expected_field_length);
+ fprintf(stdout, "\n maxlength:`%ld`", field->max_length);
+ fprintf(stdout, "\n charsetnr:`%d`", field->charsetnr);
+ fprintf(stdout, "\n default :`%s`\t(expected: `%s`)",
+ field->def ? field->def : "(null)", def ? def: "(null)");
+ fprintf(stdout, "\n");
+ }
+ DIE_UNLESS(strcmp(field->name, name) == 0);
+ DIE_UNLESS(strcmp(field->org_name, org_name) == 0);
+ /*
+ XXX: silent column specification change works based on number of
+ bytes a column occupies. So CHAR -> VARCHAR upgrade is possible even
+ for CHAR(2) column if its character set is multibyte.
+ VARCHAR -> CHAR downgrade won't work for VARCHAR(3) as one would
+ expect.
+ */
+ if (cs->mbmaxlen == 1)
+ {
+ if (field->type != type)
+ {
+ fprintf(stderr,
+ "Expected field type: %d, got type: %d in file %s, line %d\n",
+ (int) type, (int) field->type, file, line);
+ DIE_UNLESS(field->type == type);
+ }
+ }
+ if (table)
+ DIE_UNLESS(strcmp(field->table, table) == 0);
+ if (org_table)
+ DIE_UNLESS(strcmp(field->org_table, org_table) == 0);
+ DIE_UNLESS(strcmp(field->db, db) == 0);
+ /*
+ Character set should be taken into account for multibyte encodings, such
+ as utf8. Field length is calculated as number of characters * maximum
+ number of bytes a character can occupy.
+ */
+ if (length && (field->length != expected_field_length))
+ {
+ fflush(stdout);
+ fprintf(stderr, "Expected field length: %llu, got length: %lu\n",
+ expected_field_length, field->length);
+ fflush(stderr);
+ DIE_UNLESS(field->length == expected_field_length);
+ }
+ if (def)
+ DIE_UNLESS(strcmp(field->def, def) == 0);
+}
+
+
+/* Utility function to verify the parameter count */
+
+static void verify_param_count(MYSQL_STMT *stmt, long exp_count)
+{
+ long param_count= mysql_stmt_param_count(stmt);
+ if (!opt_silent)
+ fprintf(stdout, "\n total parameters in stmt: `%ld` (expected: `%ld`)",
+ param_count, exp_count);
+ DIE_UNLESS(param_count == exp_count);
+}
+
+
+/* Utility function to verify the total affected rows */
+
+static void verify_st_affected_rows(MYSQL_STMT *stmt, ulonglong exp_count)
+{
+ ulonglong affected_rows= mysql_stmt_affected_rows(stmt);
+ if (!opt_silent)
+ fprintf(stdout, "\n total affected rows: `%ld` (expected: `%ld`)",
+ (long) affected_rows, (long) exp_count);
+ DIE_UNLESS(affected_rows == exp_count);
+}
+
+
+/* Utility function to verify the total affected rows */
+
+static void verify_affected_rows(ulonglong exp_count)
+{
+ ulonglong affected_rows= mysql_affected_rows(mysql);
+ if (!opt_silent)
+ fprintf(stdout, "\n total affected rows: `%ld` (expected: `%ld`)",
+ (long) affected_rows, (long) exp_count);
+ DIE_UNLESS(affected_rows == exp_count);
+}
+
+
+/* Utility function to verify the total fields count */
+
+static void verify_field_count(MYSQL_RES *result, uint exp_count)
+{
+ uint field_count= mysql_num_fields(result);
+ if (!opt_silent)
+ fprintf(stdout, "\n total fields in the result set: `%d` (expected: `%d`)",
+ field_count, exp_count);
+ DIE_UNLESS(field_count == exp_count);
+}
+
+
+/* Utility function to execute a query using prepare-execute */
+
+#ifndef EMBEDDED_LIBRARY
+static void execute_prepare_query(const char *query, ulonglong exp_count)
+{
+ MYSQL_STMT *stmt;
+ ulonglong affected_rows;
+ int rc;
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ myquery(rc);
+
+ affected_rows= mysql_stmt_affected_rows(stmt);
+ if (!opt_silent)
+ fprintf(stdout, "\n total affected rows: `%ld` (expected: `%ld`)",
+ (long) affected_rows, (long) exp_count);
+
+ DIE_UNLESS(affected_rows == exp_count);
+ mysql_stmt_close(stmt);
+}
+#endif
+
+/*
+Accepts arbitrary number of queries and runs them against the database.
+Used to fill tables for each test.
+*/
+
+void fill_tables(const char **query_list, unsigned query_count)
+{
+ int rc;
+ const char **query;
+ DBUG_ENTER("fill_tables");
+ for (query= query_list; query < query_list + query_count;
+ ++query)
+ {
+ rc= mysql_query(mysql, *query);
+ myquery(rc);
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+All state of fetch from one statement: statement handle, out buffers,
+fetch position.
+See fetch_n for for the only use case.
+*/
+
+enum { MAX_COLUMN_LENGTH= 255 };
+
+typedef struct st_stmt_fetch
+{
+const char *query;
+unsigned stmt_no;
+MYSQL_STMT *handle;
+my_bool is_open;
+MYSQL_BIND *bind_array;
+char **out_data;
+unsigned long *out_data_length;
+unsigned column_count;
+unsigned row_count;
+} Stmt_fetch;
+
+
+/*
+Create statement handle, prepare it with statement, execute and allocate
+fetch buffers.
+*/
+
+void stmt_fetch_init(Stmt_fetch *fetch, unsigned stmt_no_arg,
+const char *query_arg)
+{
+ unsigned long type= CURSOR_TYPE_READ_ONLY;
+ int rc;
+ unsigned i;
+ MYSQL_RES *metadata;
+ DBUG_ENTER("stmt_fetch_init");
+
+ /* Save query and statement number for error messages */
+ fetch->stmt_no= stmt_no_arg;
+ fetch->query= query_arg;
+
+ fetch->handle= mysql_stmt_init(mysql);
+
+ rc= mysql_stmt_prepare(fetch->handle, fetch->query, (ulong)strlen(fetch->query));
+ check_execute(fetch->handle, rc);
+
+ /*
+ The attribute is sent to server on execute and asks to open read-only
+ for result set
+ */
+ mysql_stmt_attr_set(fetch->handle, STMT_ATTR_CURSOR_TYPE,
+ (const void*) &type);
+
+ rc= mysql_stmt_execute(fetch->handle);
+ check_execute(fetch->handle, rc);
+
+ /* Find out total number of columns in result set */
+ metadata= mysql_stmt_result_metadata(fetch->handle);
+ fetch->column_count= mysql_num_fields(metadata);
+ mysql_free_result(metadata);
+
+ /*
+ Now allocate bind handles and buffers for output data:
+ calloc memory to reduce number of MYSQL_BIND members we need to
+ set up.
+ */
+
+ fetch->bind_array= (MYSQL_BIND *) calloc(1, sizeof(MYSQL_BIND) *
+ fetch->column_count);
+ fetch->out_data= (char**) calloc(1, sizeof(char*) * fetch->column_count);
+ fetch->out_data_length= (ulong*) calloc(1, sizeof(ulong) *
+ fetch->column_count);
+ for (i= 0; i < fetch->column_count; ++i)
+ {
+ fetch->out_data[i]= (char*) calloc(1, MAX_COLUMN_LENGTH);
+ fetch->bind_array[i].buffer_type= MYSQL_TYPE_STRING;
+ fetch->bind_array[i].buffer= fetch->out_data[i];
+ fetch->bind_array[i].buffer_length= MAX_COLUMN_LENGTH;
+ fetch->bind_array[i].length= fetch->out_data_length + i;
+ }
+
+ mysql_stmt_bind_result(fetch->handle, fetch->bind_array);
+
+ fetch->row_count= 0;
+ fetch->is_open= TRUE;
+
+ /* Ready for reading rows */
+ DBUG_VOID_RETURN;
+}
+
+
+/* Fetch and print one row from cursor */
+
+int stmt_fetch_fetch_row(Stmt_fetch *fetch)
+{
+ int rc;
+ unsigned i;
+ DBUG_ENTER("stmt_fetch_fetch_row");
+
+ if ((rc= mysql_stmt_fetch(fetch->handle)) == 0)
+ {
+ ++fetch->row_count;
+ if (!opt_silent)
+ printf("Stmt %d fetched row %d:\n", fetch->stmt_no, fetch->row_count);
+ for (i= 0; i < fetch->column_count; ++i)
+ {
+ fetch->out_data[i][fetch->out_data_length[i]]= '\0';
+ if (!opt_silent)
+ printf("column %d: %s\n", i+1, fetch->out_data[i]);
+ }
+ }
+ else
+ fetch->is_open= FALSE;
+ DBUG_RETURN(rc);
+}
+
+
+void stmt_fetch_close(Stmt_fetch *fetch)
+{
+ unsigned i;
+ DBUG_ENTER("stmt_fetch_close");
+
+ for (i= 0; i < fetch->column_count; ++i)
+ free(fetch->out_data[i]);
+ free(fetch->out_data);
+ free(fetch->out_data_length);
+ free(fetch->bind_array);
+ mysql_stmt_close(fetch->handle);
+ DBUG_VOID_RETURN;
+}
+
+/*
+For given array of queries, open query_count cursors and fetch
+from them in simultaneous manner.
+In case there was an error in one of the cursors, continue
+reading from the rest.
+*/
+
+enum fetch_type { USE_ROW_BY_ROW_FETCH= 0, USE_STORE_RESULT= 1 };
+
+my_bool fetch_n(const char **query_list, unsigned query_count,
+enum fetch_type fetch_type)
+{
+ unsigned open_statements= query_count;
+ int rc, error_count= 0;
+ Stmt_fetch *fetch_array= (Stmt_fetch*) calloc(1, sizeof(Stmt_fetch) *
+ query_count);
+ Stmt_fetch *fetch;
+ DBUG_ENTER("fetch_n");
+
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ {
+ /* Init will exit(1) in case of error */
+ stmt_fetch_init(fetch, (uint)(fetch - fetch_array),
+ query_list[fetch - fetch_array]);
+ }
+
+ if (fetch_type == USE_STORE_RESULT)
+ {
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ {
+ rc= mysql_stmt_store_result(fetch->handle);
+ check_execute(fetch->handle, rc);
+ }
+ }
+
+ while (open_statements)
+ {
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ {
+ if (fetch->is_open && (rc= stmt_fetch_fetch_row(fetch)))
+ {
+ open_statements--;
+ /*
+ We try to fetch from the rest of the statements in case of
+ error
+ */
+ if (rc != MYSQL_NO_DATA)
+ {
+ fprintf(stderr,
+ "Got error reading rows from statement %d,\n"
+ "query is: %s,\n"
+ "error message: %s", (int) (fetch - fetch_array),
+ fetch->query,
+ mysql_stmt_error(fetch->handle));
+ error_count++;
+ }
+ }
+ }
+ }
+ if (error_count)
+ fprintf(stderr, "Fetch FAILED");
+ else
+ {
+ unsigned total_row_count= 0;
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ total_row_count+= fetch->row_count;
+ if (!opt_silent)
+ printf("Success, total rows fetched: %d\n", total_row_count);
+ }
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ stmt_fetch_close(fetch);
+ free(fetch_array);
+ DBUG_RETURN(error_count != 0);
+}
+
+/* Separate thread query to test some cases */
+
+static my_bool thread_query(const char *query)
+{
+ MYSQL *l_mysql;
+ my_bool error;
+ my_bool reconnect= 1;
+ error= 0;
+ if (!opt_silent)
+ fprintf(stdout, "\n in thread_query(%s)", query);
+ if (!(l_mysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ return 1;
+ }
+ if (!(mysql_real_connect(l_mysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ error= 1;
+ goto end;
+ }
+ mysql_options(l_mysql, MYSQL_OPT_RECONNECT, &reconnect);
+ if (mysql_query(l_mysql, query))
+ {
+ fprintf(stderr, "Query failed (%s)\n", mysql_error(l_mysql));
+ error= 1;
+ goto end;
+ }
+ mysql_commit(l_mysql);
+ end:
+ mysql_close(l_mysql);
+ return error;
+}
+
+
+static int mysql_query_or_error(MYSQL *mysql, const char *query)
+{
+ int rc= mysql_query(mysql, query);
+ if (rc)
+ fprintf(stderr, "ERROR %d: %s", mysql_errno(mysql), mysql_error(mysql));
+ return rc;
+}
+
+
+/*
+ Read and parse arguments and MySQL options from my.cnf
+*/
+
+static const char *client_test_load_default_groups[]=
+{ "client", "client-server", "client-mariadb", 0 };
+static char **defaults_argv;
+
+static struct my_option client_test_long_options[] =
+{
+ {"basedir", 'b', "Basedir for tests.",(void *)&opt_basedir,
+ (void *)&opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"character-sets-dir", 'C',
+ "Directory for character set files.", (void *)&charsets_dir,
+ (void *)&charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"count", 't', "Number of times test to be executed", &opt_count_read,
+ &opt_count_read, 0, GET_UINT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0},
+ {"database", 'D', "Database to use", &opt_db, &opt_db,
+ 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"do-not-drop-database", 'd', "Do not drop database while disconnecting",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug", '#', "Output debug log", (void *)&default_dbug_option,
+ (void *)&default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
+ 0, 0, 0, 0, 0},
+ {"host", 'h', "Connect to host", &opt_host, &opt_host,
+ 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"password", 'p',
+ "Password to use when connecting to server. If password is not given it's asked from the tty.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"port", 'P', "Port number to use for connection or 0 for default to, in "
+ "order of preference, my.cnf, $MYSQL_TCP_PORT, "
+#if MYSQL_PORT_DEFAULT == 0
+ "/etc/services, "
+#endif
+ "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
+ &opt_port, &opt_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"server-arg", 'A', "Send embedded server this as a parameter.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"show-tests", 'T', "Show all tests' names", 0, 0, 0, GET_NO_ARG, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"silent", 's', "Be more silent", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0,
+ 0},
+ {"socket", 'S', "Socket file to use for connection",
+ &opt_unix_socket, &opt_unix_socket, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"testcase", 'c',
+ "May disable some code when runs as mysql-test-run testcase.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifndef DONT_ALLOW_USER_CHANGE
+ {"user", 'u', "User for login if not current user", &opt_user,
+ &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"vardir", 'v', "Data dir for tests.", (void *)&opt_vardir,
+ (void *)&opt_vardir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"non-blocking-api", 'n',
+ "Use the non-blocking client API for communication.",
+ &non_blocking_api_enabled, &non_blocking_api_enabled, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"getopt-ll-test", 'g', "Option for testing bug in getopt library",
+ &opt_getopt_ll_test, &opt_getopt_ll_test, 0,
+ GET_LL, REQUIRED_ARG, 0, 0, LONGLONG_MAX, 0, 0, 0},
+ {"plugin_dir", 0, "Directory for client-side plugins.",
+ &opt_plugin_dir, &opt_plugin_dir, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"default_auth", 0, "Default authentication client-side plugin to use.",
+ &opt_default_auth, &opt_default_auth, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+static void usage(void)
+{
+ /* show the usage string when the user asks for this */
+ putc('\n', stdout);
+ printf("%s Ver %s Distrib %s, for %s (%s)\n",
+ my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
+ puts("By Monty, Venu, Kent and others\n");
+ printf("\
+Copyright (C) 2002-2004 MySQL AB\n\
+This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\
+and you are welcome to modify and redistribute it under the GPL license\n");
+ printf("Usage: %s [OPTIONS] [TESTNAME1 TESTNAME2...]\n", my_progname);
+ my_print_help(client_test_long_options);
+ print_defaults("my", client_test_load_default_groups);
+ my_print_variables(client_test_long_options);
+}
+
+static struct my_tests_st *get_my_tests(); /* To be defined in main .c file */
+
+static struct my_tests_st *my_testlist= 0;
+
+static my_bool
+get_one_option(const struct my_option *opt, const char *argument,
+ const char *filename __attribute__((unused)))
+{
+ switch (opt->id) {
+ case '#':
+ DBUG_PUSH(argument ? argument : default_dbug_option);
+ break;
+ case 'c':
+ opt_testcase = 1;
+ break;
+ case 'p':
+ if (argument)
+ {
+ /*
+ One should not really change the argument, but we make an
+ exception for passwords
+ */
+ char *start= (char*) argument;
+ my_free(opt_password);
+ opt_password= my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(MY_FAE));
+ while (*argument)
+ *(char*) argument++= 'x'; /* Destroy argument */
+ if (*start)
+ start[1]=0;
+ }
+ else
+ tty_password= 1;
+ break;
+ case 's':
+ if (argument == disabled_my_option)
+ opt_silent= 0;
+ else
+ opt_silent++;
+ break;
+ case 'd':
+ opt_drop_db= 0;
+ break;
+ case 'A':
+ /*
+ When the embedded server is being tested, the test suite needs to be
+ able to pass command-line arguments to the embedded server so it can
+ locate the language files and data directory. The test suite
+ (mysql-test-run) never uses config files, just command-line options.
+ */
+ if (!embedded_server_arg_count)
+ {
+ embedded_server_arg_count= 1;
+ embedded_server_args[0]= (char*) "";
+ }
+ if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
+ !(embedded_server_args[embedded_server_arg_count++]=
+ my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(MY_FAE))))
+ {
+ DIE("Can't use server argument");
+ }
+ break;
+ case 'T':
+ {
+ struct my_tests_st *fptr;
+
+ printf("All possible test names:\n\n");
+ for (fptr= my_testlist; fptr->name; fptr++)
+ printf("%s\n", fptr->name);
+ exit(0);
+ break;
+ }
+ case 'C':
+ strmake_buf(mysql_charsets_dir, argument);
+ charsets_dir = mysql_charsets_dir;
+ break;
+ case '?':
+ case 'I': /* Info */
+ usage();
+ exit(0);
+ break;
+ }
+ return 0;
+}
+
+static void get_options(int *argc, char ***argv)
+{
+ int ho_error;
+ /* Copy argv from load_defaults, so we can free it when done. */
+ defaults_argv= *argv;
+ /* reset --silent option */
+ opt_silent= 0;
+
+ if ((ho_error= handle_options(argc, argv, client_test_long_options,
+ get_one_option)))
+ exit(ho_error);
+
+ if (tty_password)
+ opt_password= my_get_tty_password(NullS);
+ return;
+}
+
+/*
+ Print the test output on successful execution before exiting
+*/
+
+static void print_test_output()
+{
+ if (opt_silent < 3)
+ {
+ fprintf(stdout, "\n\n");
+ fprintf(stdout, "All '%d' tests were successful (in '%d' iterations)",
+ test_count-1, opt_count);
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n Total execution time: %g SECS", total_time);
+ if (opt_count > 1)
+ fprintf(stdout, " (Avg: %g SECS)", total_time/opt_count);
+ }
+
+ fprintf(stdout, "\n\n!!! SUCCESS !!!\n");
+ }
+}
+
+/***************************************************************************
+ main routine
+***************************************************************************/
+
+
+int main(int argc, char **argv)
+{
+ int i;
+ char **tests_to_run= NULL, **curr_test;
+ struct my_tests_st *fptr;
+ my_testlist= get_my_tests();
+
+ MY_INIT(argv[0]);
+ /* Copy the original arguments, so it can be reused for restarting. */
+ original_argc= argc;
+ original_argv= malloc(argc * sizeof(char*));
+ if (argc && !original_argv)
+ exit(1);
+ for (i= 0; i < argc; i++)
+ original_argv[i]= strdup(argv[i]);
+
+ load_defaults_or_exit("my", client_test_load_default_groups, &argc, &argv);
+
+ get_options(&argc, &argv);
+ /* Set main opt_count. */
+ opt_count= opt_count_read;
+
+ /* If there are any arguments left (named tests), save them. */
+ if (argc)
+ {
+ tests_to_run= malloc((argc + 1) * sizeof(char*));
+ if (!tests_to_run)
+ exit(1);
+ for (i= 0; i < argc; i++)
+ tests_to_run[i]= strdup(argv[i]);
+ tests_to_run[i]= NULL;
+ }
+
+ if (mysql_server_init(embedded_server_arg_count,
+ embedded_server_args,
+ (char**) embedded_server_groups))
+ DIE("Can't initialize MySQL server");
+
+ /* connect to server with no flags, default protocol, auto reconnect true */
+ mysql= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 1);
+
+ total_time= 0;
+ for (iter_count= 1; iter_count <= opt_count; iter_count++)
+ {
+ /* Start of tests */
+ test_count= 1;
+ start_time= time((time_t *)0);
+ if (!tests_to_run)
+ {
+ for (fptr= my_testlist; fptr->name; fptr++)
+ (*fptr->function)();
+ }
+ else
+ {
+ for (curr_test= tests_to_run ; *curr_test ; curr_test++)
+ {
+ for (fptr= my_testlist; fptr->name; fptr++)
+ {
+ if (!strcmp(fptr->name, *curr_test))
+ {
+ (*fptr->function)();
+ break;
+ }
+ }
+ if (!fptr->name)
+ {
+ fprintf(stderr, "\n\nGiven test not found: '%s'\n", *argv);
+ fprintf(stderr, "See legal test names with %s -T\n\nAborting!\n",
+ my_progname);
+ client_disconnect(mysql);
+ free_defaults(defaults_argv);
+ mysql_server_end();
+ exit(1);
+ }
+ }
+ }
+
+ end_time= time((time_t *)0);
+ total_time+= difftime(end_time, start_time);
+
+ /* End of tests */
+ }
+
+ client_disconnect(mysql); /* disconnect from server */
+
+ free_defaults(defaults_argv);
+ print_test_output();
+
+ while (embedded_server_arg_count > 1)
+ my_free(embedded_server_args[--embedded_server_arg_count]);
+
+ mysql_server_end();
+
+ my_end(0);
+
+ for (i= 0; i < original_argc; i++)
+ free(original_argv[i]);
+ if (original_argc)
+ free(original_argv);
+ if (tests_to_run)
+ {
+ for (curr_test= tests_to_run ; *curr_test ; curr_test++)
+ free(*curr_test);
+ free(tests_to_run);
+ }
+ my_free(opt_password);
+ my_free(opt_host);
+
+ exit(0);
+}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
new file mode 100644
index 00000000..c0077681
--- /dev/null
+++ b/tests/mysql_client_test.c
@@ -0,0 +1,22495 @@
+/* Copyright (c) 2002, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2022, MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/***************************************************************************
+ This is a test sample to test the new features in MySQL client-server
+ protocol
+
+ Main author: venu ( venu@mysql.com )
+***************************************************************************/
+
+/*
+ XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
+ DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.
+*/
+
+
+/*
+ The fw.c file includes all the mysql_client_test framework; this file
+ contains only the actual tests, plus the list of test functions to call.
+*/
+#ifdef _MSC_VER
+#pragma warning (disable : 4267)
+#endif
+
+#include "mysql_client_fw.c"
+#ifndef _WIN32
+#include <arpa/inet.h>
+#endif
+
+#include "my_valgrind.h"
+
+static const my_bool my_true= 1;
+
+
+/* Query processing */
+
+static my_bool get_reconnect(MYSQL *mysql)
+{
+#ifdef EMBEDDED_LIBRARY
+ return mysql->reconnect;
+#else
+ my_bool reconnect;
+ mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect);
+ return reconnect;
+#endif
+}
+
+static void client_query()
+{
+ int rc;
+
+ myheader("client_query");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1("
+ "id int primary key auto_increment, "
+ "name varchar(20))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(id int, name varchar(20))");
+ myquery_r(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('mysql')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('monty')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('venu')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('deleted')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('deleted')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "UPDATE t1 SET name= 'updated' "
+ "WHERE name= 'deleted'");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "UPDATE t1 SET id= 3 WHERE name= 'updated'");
+ myquery_r(rc);
+
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+/* Store result processing */
+
+static void client_store_result()
+{
+ MYSQL_RES *result;
+ int rc;
+
+ myheader("client_store_result");
+
+ rc= mysql_query(mysql, "SELECT * FROM t1");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+}
+
+
+/* Fetch the results */
+
+static void client_use_result()
+{
+ MYSQL_RES *result;
+ int rc;
+ myheader("client_use_result");
+
+ rc= mysql_query(mysql, "SELECT * FROM t1");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+}
+
+
+/* Query processing */
+
+static void test_debug_example()
+{
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_debug_example");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_debug_example");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_debug_example("
+ "id INT PRIMARY KEY AUTO_INCREMENT, "
+ "name VARCHAR(20), xxx INT)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_debug_example (name) "
+ "VALUES ('mysql')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "UPDATE test_debug_example SET name='updated' "
+ "WHERE name='deleted'");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SELECT * FROM test_debug_example where name='mysql'");
+ myquery(rc);
+
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+
+ rc= mysql_query(mysql, "DROP TABLE test_debug_example");
+ myquery(rc);
+}
+
+
+/* Test autocommit feature for BDB tables */
+
+static void test_tran_bdb()
+{
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ int rc;
+
+ myheader("test_tran_bdb");
+
+ /* set AUTOCOMMIT to OFF */
+ rc= mysql_autocommit(mysql, FALSE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_demo_transaction");
+ myquery(rc);
+
+
+ /* create the table 'mytran_demo' of type BDB' or 'InnoDB' */
+ rc= mysql_query(mysql, "CREATE TABLE my_demo_transaction( "
+ "col1 int , col2 varchar(30)) ENGINE= BDB");
+ myquery(rc);
+
+ /* insert a row and commit the transaction */
+ rc= mysql_query(mysql, "INSERT INTO my_demo_transaction VALUES(10, 'venu')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* now insert the second row, and roll back the transaction */
+ rc= mysql_query(mysql, "INSERT INTO my_demo_transaction VALUES(20, 'mysql')");
+ myquery(rc);
+
+ rc= mysql_rollback(mysql);
+ myquery(rc);
+
+ /* delete first row, and roll it back */
+ rc= mysql_query(mysql, "DELETE FROM my_demo_transaction WHERE col1= 10");
+ myquery(rc);
+
+ rc= mysql_rollback(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM my_demo_transaction");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM my_demo_transaction");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ row= mysql_fetch_row(result);
+ mytest(row);
+
+ row= mysql_fetch_row(result);
+ mytest_r(row);
+
+ mysql_free_result(result);
+ mysql_autocommit(mysql, TRUE);
+}
+
+
+/* Test autocommit feature for InnoDB tables */
+
+static void test_tran_innodb()
+{
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ int rc;
+
+ myheader("test_tran_innodb");
+
+ /* set AUTOCOMMIT to OFF */
+ rc= mysql_autocommit(mysql, FALSE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_demo_transaction");
+ myquery(rc);
+
+ /* create the table 'mytran_demo' of type BDB' or 'InnoDB' */
+ rc= mysql_query(mysql, "CREATE TABLE my_demo_transaction(col1 int, "
+ "col2 varchar(30)) ENGINE= InnoDB");
+ myquery(rc);
+
+ /* insert a row and commit the transaction */
+ rc= mysql_query(mysql, "INSERT INTO my_demo_transaction VALUES(10, 'venu')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* now insert the second row, and roll back the transaction */
+ rc= mysql_query(mysql, "INSERT INTO my_demo_transaction VALUES(20, 'mysql')");
+ myquery(rc);
+
+ rc= mysql_rollback(mysql);
+ myquery(rc);
+
+ /* delete first row, and roll it back */
+ rc= mysql_query(mysql, "DELETE FROM my_demo_transaction WHERE col1= 10");
+ myquery(rc);
+
+ rc= mysql_rollback(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM my_demo_transaction");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM my_demo_transaction");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ row= mysql_fetch_row(result);
+ mytest(row);
+
+ row= mysql_fetch_row(result);
+ mytest_r(row);
+
+ mysql_free_result(result);
+ mysql_autocommit(mysql, TRUE);
+}
+
+
+/* Test for BUG#7242 */
+
+static void test_prepare_insert_update()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ int i;
+ const char *testcase[]= {
+ "CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE (A), UNIQUE(B))",
+ "INSERT t1 VALUES (1,2,10), (3,4,20)",
+ "INSERT t1 VALUES (5,6,30), (7,4,40), (8,9,60) ON DUPLICATE KEY UPDATE c=c+100",
+ "SELECT * FROM t1",
+ "INSERT t1 SET a=5 ON DUPLICATE KEY UPDATE b=0",
+ "SELECT * FROM t1",
+ "INSERT t1 VALUES (2,1,11), (7,4,40) ON DUPLICATE KEY UPDATE c=c+VALUES(a)",
+ NULL};
+ const char **cur_query;
+
+ myheader("test_prepare_insert_update");
+
+ for (cur_query= testcase; *cur_query; cur_query++)
+ {
+ char query[MAX_TEST_QUERY_LENGTH];
+ printf("\nRunning query: %s", *cur_query);
+ strmov(query, *cur_query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 0);
+ rc= mysql_stmt_execute(stmt);
+
+ check_execute(stmt, rc);
+ /* try the last query several times */
+ if (!cur_query[1])
+ {
+ for (i=0; i < 3;i++)
+ {
+ printf("\nExecuting last statement again");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+ }
+ mysql_stmt_close(stmt);
+ }
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+}
+
+
+/* Test simple prepares of all DML statements */
+
+static void test_prepare_simple()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_prepare_simple");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_simple");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_prepare_simple("
+ "id int, name varchar(50))");
+ myquery(rc);
+
+ /* insert */
+ strmov(query, "INSERT INTO test_prepare_simple VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+ mysql_stmt_close(stmt);
+
+ /* update */
+ strmov(query, "UPDATE test_prepare_simple SET id=? "
+ "WHERE id=? AND CONVERT(name USING utf8)= ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 3);
+ mysql_stmt_close(stmt);
+
+ /* delete */
+ strmov(query, "DELETE FROM test_prepare_simple WHERE id=10");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 0);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+
+ /* delete */
+ strmov(query, "DELETE FROM test_prepare_simple WHERE id=?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 1);
+
+ mysql_stmt_close(stmt);
+
+ /* select */
+ strmov(query, "SELECT * FROM test_prepare_simple WHERE id=? "
+ "AND CONVERT(name USING utf8)= ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ mysql_stmt_close(stmt);
+
+ /* show create */
+ strmov(query, "SHOW CREATE TABLE test_prepare_simple");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 2);
+ mysql_stmt_close(stmt);
+
+ /* show create database */
+ strmov(query, "SHOW CREATE DATABASE test");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 2);
+ mysql_stmt_close(stmt);
+
+ /* show grants */
+ strmov(query, "SHOW GRANTS");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 1);
+ mysql_stmt_close(stmt);
+
+ /* show slave status */
+ strmov(query, "SHOW SLAVE STATUS");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 54);
+ mysql_stmt_close(stmt);
+
+ /* show master status */
+ strmov(query, "SHOW MASTER STATUS");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 4);
+ mysql_stmt_close(stmt);
+
+ /* show create procedure */
+ strmov(query, "SHOW CREATE PROCEDURE e1;");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 6);
+ mysql_stmt_close(stmt);
+
+ /* show create function */
+ strmov(query, "SHOW CREATE FUNCTION e1;");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 6);
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+}
+
+/************************************************************************/
+
+#define FILE_PATH_SIZE 4096
+
+char mct_log_file_path[FILE_PATH_SIZE];
+FILE *mct_log_file= NULL;
+
+void mct_start_logging(const char *test_case_name)
+{
+ const char *tmp_dir= getenv("MYSQL_TMP_DIR");
+
+ if (!tmp_dir)
+ {
+ printf("Warning: MYSQL_TMP_DIR is not set. Logging is disabled.\n");
+ return;
+ }
+
+ if (mct_log_file)
+ {
+ printf("Warning: can not start logging for test case '%s' "
+ "because log is already open\n",
+ (const char *) test_case_name);
+ return;
+ }
+
+ /*
+ Path is: <tmp_dir>/<test_case_name>.out.log
+ 10 is length of '/' + '.out.log' + \0
+ */
+
+ if (strlen(tmp_dir) + strlen(test_case_name) + 10 > FILE_PATH_SIZE)
+ {
+ printf("Warning: MYSQL_TMP_DIR is too long. Logging is disabled.\n");
+ return;
+ }
+
+ my_snprintf(mct_log_file_path, FILE_PATH_SIZE,
+ "%s/%s.out.log",
+ (const char *) tmp_dir,
+ (const char *) test_case_name);
+
+ mct_log_file= my_fopen(mct_log_file_path, O_WRONLY | O_BINARY, MYF(MY_WME));
+
+ if (!mct_log_file)
+ {
+ printf("Warning: can not open log file (%s): %s. Logging is disabled.\n",
+ (const char *) mct_log_file_path,
+ (const char *) strerror(errno));
+ return;
+ }
+}
+
+void mct_log(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+
+ if (mct_log_file)
+ {
+ va_list args;
+ va_start(args, format);
+ vfprintf(mct_log_file, format, args);
+ va_end(args);
+ }
+}
+
+void mct_close_log()
+{
+ if (!mct_log_file)
+ return;
+
+ my_fclose(mct_log_file, MYF(0));
+ mct_log_file= NULL;
+}
+
+#define WL4435_NUM_PARAMS 10
+#define WL4435_STRING_SIZE 30
+
+static void test_wl4435()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ char str_data[20][WL4435_STRING_SIZE];
+ double dbl_data[20];
+ char dec_data[20][WL4435_STRING_SIZE];
+ int int_data[20];
+ ulong str_length= WL4435_STRING_SIZE;
+ my_bool is_null;
+ MYSQL_BIND ps_params[WL4435_NUM_PARAMS];
+
+ int exec_counter;
+
+ myheader("test_wl4435");
+ mct_start_logging("test_wl4435");
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(a1 INT, a2 CHAR(32), "
+ " a3 DOUBLE(4, 2), a4 DECIMAL(3, 1))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t2(b0 INT, b1 INT, b2 CHAR(32), "
+ " b3 DOUBLE(4, 2), b4 DECIMAL(3, 1))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES"
+ "(1, '11', 12.34, 56.7), "
+ "(2, '12', 56.78, 90.1), "
+ "(3, '13', 23.45, 67.8)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t2 VALUES"
+ "(100, 10, '110', 70.70, 10.1), "
+ "(200, 20, '120', 80.80, 20.2), "
+ "(300, 30, '130', 90.90, 30.3)");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE PROCEDURE p1("
+ " IN v0 INT, "
+ " OUT v_str_1 CHAR(32), "
+ " OUT v_dbl_1 DOUBLE(4, 2), "
+ " OUT v_dec_1 DECIMAL(6, 3), "
+ " OUT v_int_1 INT, "
+ " IN v1 INT, "
+ " INOUT v_str_2 CHAR(64), "
+ " INOUT v_dbl_2 DOUBLE(5, 3), "
+ " INOUT v_dec_2 DECIMAL(7, 4), "
+ " INOUT v_int_2 INT)"
+ "BEGIN "
+ " SET v0 = -1; "
+ " SET v1 = -1; "
+ " SET v_str_1 = 'test_1'; "
+ " SET v_dbl_1 = 12.34; "
+ " SET v_dec_1 = 567.891; "
+ " SET v_int_1 = 2345; "
+ " SET v_str_2 = 'test_2'; "
+ " SET v_dbl_2 = 67.891; "
+ " SET v_dec_2 = 234.6789; "
+ " SET v_int_2 = 6789; "
+ " SELECT * FROM t1; "
+ " SELECT * FROM t2; "
+ "END");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE PROCEDURE p2("
+ " IN i1 VARCHAR(255) CHARACTER SET koi8r, "
+ " OUT o1 VARCHAR(255) CHARACTER SET cp1251, "
+ " OUT o2 VARBINARY(255)) "
+ "BEGIN "
+ " SET o1 = i1; "
+ " SET o2 = i1; "
+ "END");
+ myquery(rc);
+
+ strmov(query, "CALL p1(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ /* Init PS-parameters. */
+
+ memset(str_data, 0, sizeof str_data);
+ memset(dbl_data, 0, sizeof dbl_data);
+ memset(dec_data, 0, sizeof dec_data);
+ memset(int_data, 0, sizeof int_data);
+
+ bzero((char *) ps_params, sizeof (ps_params));
+
+ /* - v0 -- INT */
+
+ ps_params[0].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[0].buffer= (char *) &int_data[0];
+ ps_params[0].length= 0;
+ ps_params[0].is_null= 0;
+
+ /* - v_str_1 -- CHAR(32) */
+
+ ps_params[1].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[1].buffer= (char *) str_data[0];
+ ps_params[1].buffer_length= WL4435_STRING_SIZE;
+ ps_params[1].length= &str_length;
+ ps_params[1].is_null= 0;
+
+ /* - v_dbl_1 -- DOUBLE */
+
+ ps_params[2].buffer_type= MYSQL_TYPE_DOUBLE;
+ ps_params[2].buffer= (char *) &dbl_data[0];
+ ps_params[2].length= 0;
+ ps_params[2].is_null= 0;
+
+ /* - v_dec_1 -- DECIMAL */
+
+ ps_params[3].buffer_type= MYSQL_TYPE_NEWDECIMAL;
+ ps_params[3].buffer= (char *) dec_data[0];
+ ps_params[3].buffer_length= WL4435_STRING_SIZE;
+ ps_params[3].length= 0;
+ ps_params[3].is_null= 0;
+
+ /* - v_int_1 -- INT */
+
+ ps_params[4].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[4].buffer= (char *) &int_data[0];
+ ps_params[4].length= 0;
+ ps_params[4].is_null= 0;
+
+ /* - v1 -- INT */
+
+ ps_params[5].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[5].buffer= (char *) &int_data[0];
+ ps_params[5].length= 0;
+ ps_params[5].is_null= 0;
+
+ /* - v_str_2 -- CHAR(32) */
+
+ ps_params[6].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[6].buffer= (char *) str_data[0];
+ ps_params[6].buffer_length= WL4435_STRING_SIZE;
+ ps_params[6].length= &str_length;
+ ps_params[6].is_null= 0;
+
+ /* - v_dbl_2 -- DOUBLE */
+
+ ps_params[7].buffer_type= MYSQL_TYPE_DOUBLE;
+ ps_params[7].buffer= (char *) &dbl_data[0];
+ ps_params[7].length= 0;
+ ps_params[7].is_null= 0;
+
+ /* - v_dec_2 -- DECIMAL */
+
+ ps_params[8].buffer_type= MYSQL_TYPE_DECIMAL;
+ ps_params[8].buffer= (char *) dec_data[0];
+ ps_params[8].buffer_length= WL4435_STRING_SIZE;
+ ps_params[8].length= 0;
+ ps_params[8].is_null= 0;
+
+ /* - v_int_2 -- INT */
+
+ ps_params[9].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[9].buffer= (char *) &int_data[0];
+ ps_params[9].length= 0;
+ ps_params[9].is_null= 0;
+
+ /* Bind parameters. */
+
+ rc= mysql_stmt_bind_param(stmt, ps_params);
+
+ /* Execute! */
+
+ for (exec_counter= 0; exec_counter < 3; ++exec_counter)
+ {
+ int i;
+ int num_fields;
+ MYSQL_BIND *rs_bind;
+
+ mct_log("\nexec_counter: %d\n", (int) exec_counter);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ while (1)
+ {
+ MYSQL_FIELD *fields;
+
+ MYSQL_RES *rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ fields= mysql_fetch_fields(rs_metadata);
+
+ rs_bind= (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields);
+ bzero(rs_bind, sizeof (MYSQL_BIND) * num_fields);
+
+ mct_log("num_fields: %d\n", (int) num_fields);
+
+ for (i = 0; i < num_fields; ++i)
+ {
+ mct_log(" - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
+ "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
+ "type: %d; decimals: %d\n",
+ (int) i,
+ (const char *) fields[i].name,
+ (const char *) fields[i].org_name,
+ (const char *) fields[i].table,
+ (const char *) fields[i].org_table,
+ (const char *) fields[i].db,
+ (const char *) fields[i].catalog,
+ (int) fields[i].length,
+ (int) fields[i].max_length,
+ (int) fields[i].type,
+ (int) fields[i].decimals);
+
+ rs_bind[i].buffer_type= fields[i].type;
+ rs_bind[i].is_null= &is_null;
+
+ switch (fields[i].type)
+ {
+ case MYSQL_TYPE_LONG:
+ rs_bind[i].buffer= (char *) &(int_data[i]);
+ rs_bind[i].buffer_length= sizeof (int_data);
+ break;
+
+ case MYSQL_TYPE_STRING:
+ rs_bind[i].buffer= (char *) str_data[i];
+ rs_bind[i].buffer_length= WL4435_STRING_SIZE;
+ rs_bind[i].length= &str_length;
+ break;
+
+ case MYSQL_TYPE_DOUBLE:
+ rs_bind[i].buffer= (char *) &dbl_data[i];
+ rs_bind[i].buffer_length= sizeof (dbl_data);
+ break;
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ rs_bind[i].buffer= (char *) dec_data[i];
+ rs_bind[i].buffer_length= WL4435_STRING_SIZE;
+ rs_bind[i].length= &str_length;
+ break;
+
+ default:
+ fprintf(stderr, "ERROR: unexpected type: %d.\n", fields[i].type);
+ exit(1);
+ }
+ }
+
+ rc= mysql_stmt_bind_result(stmt, rs_bind);
+ check_execute(stmt, rc);
+
+ mct_log("Data:\n");
+
+ while (1)
+ {
+ int rc= mysql_stmt_fetch(stmt);
+
+ if (rc == 1 || rc == MYSQL_NO_DATA)
+ break;
+
+ mct_log(" ");
+
+ for (i = 0; i < num_fields; ++i)
+ {
+ switch (rs_bind[i].buffer_type)
+ {
+ case MYSQL_TYPE_LONG:
+ mct_log(" int: %ld;",
+ (long) *((int *) rs_bind[i].buffer));
+ break;
+
+ case MYSQL_TYPE_STRING:
+ mct_log(" str: '%s';",
+ (char *) rs_bind[i].buffer);
+ break;
+
+ case MYSQL_TYPE_DOUBLE:
+ mct_log(" dbl: %lf;",
+ (double) *((double *) rs_bind[i].buffer));
+ break;
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ mct_log(" dec: '%s';",
+ (char *) rs_bind[i].buffer);
+ break;
+
+ default:
+ printf(" unexpected type (%d)\n",
+ rs_bind[i].buffer_type);
+ }
+ }
+ mct_log("\n");
+ }
+
+ mct_log("EOF\n");
+
+ rc= mysql_stmt_next_result(stmt);
+ mct_log("mysql_stmt_next_result(): %d; field_count: %d\n",
+ (int) rc, (int) mysql->field_count);
+
+ free(rs_bind);
+ mysql_free_result(rs_metadata);
+
+ if (rc > 0)
+ {
+ printf("Error: %s (errno: %d)\n",
+ mysql_stmt_error(stmt), mysql_stmt_errno(stmt));
+ DIE(rc > 0);
+ }
+
+ if (rc)
+ break;
+
+ if (!mysql->field_count)
+ {
+ /* This is the last OK-packet. No more resultsets. */
+ break;
+ }
+ }
+
+ }
+
+ mysql_stmt_close(stmt);
+
+ mct_close_log();
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* i18n part of test case. */
+
+ {
+ const char *str_koi8r= "\xee\xd5\x2c\x20\xda\xc1\x20\xd2\xd9\xc2\xc1\xcc\xcb\xd5";
+ const char *str_cp1251= "\xcd\xf3\x2c\x20\xe7\xe0\x20\xf0\xfb\xe1\xe0\xeb\xea\xf3";
+ char o1_buffer[255];
+ ulong o1_length;
+ char o2_buffer[255];
+ ulong o2_length;
+
+ MYSQL_BIND rs_bind[2];
+
+ strmov(query, "CALL p2(?, ?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ /* Init PS-parameters. */
+
+ bzero((char *) ps_params, sizeof (ps_params));
+
+ ps_params[0].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[0].buffer= (char *) str_koi8r;
+ ps_params[0].buffer_length= strlen(str_koi8r);
+
+ ps_params[1].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[1].buffer= o1_buffer;
+ ps_params[1].buffer_length= 0;
+
+ ps_params[2].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[2].buffer= o2_buffer;
+ ps_params[2].buffer_length= 0;
+
+ /* Bind parameters. */
+
+ rc= mysql_stmt_bind_param(stmt, ps_params);
+ check_execute(stmt, rc);
+
+ /* Prevent converting to character_set_results. */
+
+ rc= mysql_query(mysql, "SET NAMES binary");
+ myquery(rc);
+
+ /* Execute statement. */
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* Bind result. */
+
+ bzero(rs_bind, sizeof (rs_bind));
+
+ rs_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ rs_bind[0].buffer= o1_buffer;
+ rs_bind[0].buffer_length= sizeof (o1_buffer);
+ rs_bind[0].length= &o1_length;
+
+ rs_bind[1].buffer_type= MYSQL_TYPE_BLOB;
+ rs_bind[1].buffer= o2_buffer;
+ rs_bind[1].buffer_length= sizeof (o2_buffer);
+ rs_bind[1].length= &o2_length;
+
+ rc= mysql_stmt_bind_result(stmt, rs_bind);
+ check_execute(stmt, rc);
+
+ /* Fetch result. */
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ /* Check result. */
+
+ DIE_UNLESS(o1_length == strlen(str_cp1251));
+ DIE_UNLESS(o2_length == strlen(str_koi8r));
+ DIE_UNLESS(!memcmp(o1_buffer, str_cp1251, o1_length));
+ DIE_UNLESS(!memcmp(o2_buffer, str_koi8r, o2_length));
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_next_result(stmt);
+ DIE_UNLESS(rc == 0 && mysql->field_count == 0);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+ }
+}
+
+static void test_wl4435_2()
+{
+ MYSQL_STMT *stmt;
+ int i;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_wl4435_2");
+ mct_start_logging("test_wl4435_2");
+
+ /*
+ Do a few iterations so that we catch any problem with incorrect
+ handling/flushing prepared statement results.
+ */
+
+ for (i= 0; i < 10; ++i)
+ {
+ /*
+ Prepare a procedure. That can be moved out of the loop, but it was
+ left in the loop for the sake of having as many statements as
+ possible.
+ */
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE PROCEDURE p1()"
+ "BEGIN "
+ " SELECT 1; "
+ " SELECT 2, 3 UNION SELECT 4, 5; "
+ " SELECT 6, 7, 8; "
+ "END");
+ myquery(rc);
+
+ /* Invoke a procedure, that returns several result sets. */
+
+ strmov(query, "CALL p1()");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ /* Execute! */
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* Flush all the results. */
+
+ mysql_stmt_close(stmt);
+
+ /* Clean up. */
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP PROCEDURE p1");
+ myquery(rc);
+ }
+ mct_close_log();
+}
+
+
+#define WL4435_TEST(sql_type, sql_value, \
+ c_api_in_type, c_api_out_type, \
+ c_type, c_type_ext, \
+ printf_args, assert_condition) \
+\
+ do { \
+ int rc; \
+ MYSQL_STMT *ps; \
+ MYSQL_BIND psp; \
+ MYSQL_RES *rs_metadata; \
+ MYSQL_FIELD *fields; \
+ c_type pspv c_type_ext; \
+ my_bool psp_null= FALSE; \
+ \
+ bzero(&pspv, sizeof (pspv)); \
+ \
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); \
+ myquery(rc); \
+ \
+ rc= mysql_query(mysql, \
+ "CREATE PROCEDURE p1(OUT v " sql_type ") SET v = " sql_value ";"); \
+ myquery(rc); \
+ \
+ ps = mysql_simple_prepare(mysql, "CALL p1(?)"); \
+ check_stmt(ps); \
+ \
+ bzero(&psp, sizeof (psp)); \
+ psp.buffer_type= c_api_in_type; \
+ psp.is_null= &psp_null; \
+ psp.buffer= (char *) &pspv; \
+ psp.buffer_length= sizeof (psp); \
+ \
+ rc= mysql_stmt_bind_param(ps, &psp); \
+ check_execute(ps, rc); \
+ \
+ rc= mysql_stmt_execute(ps); \
+ check_execute(ps, rc); \
+ \
+ DIE_UNLESS(mysql->server_status & SERVER_PS_OUT_PARAMS); \
+ DIE_UNLESS(mysql_stmt_field_count(ps) == 1); \
+ \
+ rs_metadata= mysql_stmt_result_metadata(ps); \
+ fields= mysql_fetch_fields(rs_metadata); \
+ mysql_free_result(rs_metadata); \
+ \
+ rc= mysql_stmt_bind_result(ps, &psp); \
+ check_execute(ps, rc); \
+ \
+ rc= mysql_stmt_fetch(ps); \
+ DIE_UNLESS(rc == 0); \
+ \
+ DIE_UNLESS(fields[0].type == c_api_out_type); \
+ printf printf_args; \
+ printf("; in type: %d; out type: %d\n", \
+ (int) c_api_in_type, (int) c_api_out_type); \
+ \
+ rc= mysql_stmt_fetch(ps); \
+ DIE_UNLESS(rc == MYSQL_NO_DATA); \
+ \
+ rc= mysql_stmt_next_result(ps); \
+ DIE_UNLESS(rc == 0); \
+ \
+ mysql_stmt_free_result(ps); \
+ mysql_stmt_close(ps); \
+ \
+ DIE_UNLESS(assert_condition); \
+ \
+ } while (0)
+
+static void test_wl4435_3()
+{
+ char tmp[255];
+
+ memset(tmp, 0, sizeof tmp);
+ puts("");
+
+ /*
+ // The following types are not supported:
+ // - ENUM
+ // - SET
+ //
+ // The following types are supported but can not be used for
+ // OUT-parameters:
+ // - MEDIUMINT;
+ // - BIT(..);
+ //
+ // The problem is that those types are not supported for IN-parameters,
+ // and OUT-parameters should be bound as IN-parameters before execution.
+ //
+ // The following types should not be used:
+ // - MYSQL_TYPE_YEAR (use MYSQL_TYPE_SHORT instead);
+ // - MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB
+ // (use MYSQL_TYPE_BLOB instead);
+ */
+
+ WL4435_TEST("TINYINT", "127",
+ MYSQL_TYPE_TINY, MYSQL_TYPE_TINY,
+ char, ,
+ (" - TINYINT / char / MYSQL_TYPE_TINY:\t\t\t %d", (int) pspv),
+ pspv == 127);
+
+ WL4435_TEST("SMALLINT", "32767",
+ MYSQL_TYPE_SHORT, MYSQL_TYPE_SHORT,
+ short, ,
+ (" - SMALLINT / short / MYSQL_TYPE_SHORT:\t\t %d", (int) pspv),
+ pspv == 32767);
+
+ WL4435_TEST("INT", "2147483647",
+ MYSQL_TYPE_LONG, MYSQL_TYPE_LONG,
+ int, ,
+ (" - INT / int / MYSQL_TYPE_LONG:\t\t\t %d", pspv),
+ pspv == 2147483647l);
+
+ WL4435_TEST("BIGINT", "9223372036854775807",
+ MYSQL_TYPE_LONGLONG, MYSQL_TYPE_LONGLONG,
+ long long, ,
+ (" - BIGINT / long long / MYSQL_TYPE_LONGLONG:\t\t %lld", pspv),
+ pspv == 9223372036854775807ll);
+
+ WL4435_TEST("TIMESTAMP", "'2007-11-18 15:01:02'",
+ MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_TIMESTAMP,
+ MYSQL_TIME, ,
+ (" - TIMESTAMP / MYSQL_TIME / MYSQL_TYPE_TIMESTAMP:\t "
+ "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
+ (int) pspv.year, (int) pspv.month, (int) pspv.day,
+ (int) pspv.hour, (int) pspv.minute, (int) pspv.second),
+ pspv.year == 2007 && pspv.month == 11 && pspv.day == 18 &&
+ pspv.hour == 15 && pspv.minute == 1 && pspv.second == 2);
+
+ WL4435_TEST("DATETIME", "'1234-11-12 12:34:59'",
+ MYSQL_TYPE_DATETIME, MYSQL_TYPE_DATETIME,
+ MYSQL_TIME, ,
+ (" - DATETIME / MYSQL_TIME / MYSQL_TYPE_DATETIME:\t "
+ "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
+ (int) pspv.year, (int) pspv.month, (int) pspv.day,
+ (int) pspv.hour, (int) pspv.minute, (int) pspv.second),
+ pspv.year == 1234 && pspv.month == 11 && pspv.day == 12 &&
+ pspv.hour == 12 && pspv.minute == 34 && pspv.second == 59);
+
+ WL4435_TEST("TIME", "'123:45:01'",
+ MYSQL_TYPE_TIME, MYSQL_TYPE_TIME,
+ MYSQL_TIME, ,
+ (" - TIME / MYSQL_TIME / MYSQL_TYPE_TIME:\t\t "
+ "%.3d:%.2d:%.2d",
+ (int) pspv.hour, (int) pspv.minute, (int) pspv.second),
+ pspv.hour == 123 && pspv.minute == 45 && pspv.second == 1);
+
+ WL4435_TEST("DATE", "'1234-11-12'",
+ MYSQL_TYPE_DATE, MYSQL_TYPE_DATE,
+ MYSQL_TIME, ,
+ (" - DATE / MYSQL_TIME / MYSQL_TYPE_DATE:\t\t "
+ "%.4d-%.2d-%.2d",
+ (int) pspv.year, (int) pspv.month, (int) pspv.day),
+ pspv.year == 1234 && pspv.month == 11 && pspv.day == 12);
+
+ WL4435_TEST("YEAR", "'2010'",
+ MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR,
+ short, ,
+ (" - YEAR / short / MYSQL_TYPE_SHORT:\t\t\t %.4d", (int) pspv),
+ pspv == 2010);
+
+ WL4435_TEST("FLOAT(7, 4)", "123.4567",
+ MYSQL_TYPE_FLOAT, MYSQL_TYPE_FLOAT,
+ float, ,
+ (" - FLOAT / float / MYSQL_TYPE_FLOAT:\t\t\t %g", (double) pspv),
+ pspv - 123.4567 < 0.0001);
+
+ WL4435_TEST("DOUBLE(8, 5)", "123.45678",
+ MYSQL_TYPE_DOUBLE, MYSQL_TYPE_DOUBLE,
+ double, ,
+ (" - DOUBLE / double / MYSQL_TYPE_DOUBLE:\t\t %g", (double) pspv),
+ pspv - 123.45678 < 0.00001);
+
+ WL4435_TEST("DECIMAL(9, 6)", "123.456789",
+ MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL,
+ char, [255],
+ (" - DECIMAL / char[] / MYSQL_TYPE_NEWDECIMAL:\t\t '%s'", (char *) pspv),
+ !strcmp(pspv, "123.456789"));
+
+ WL4435_TEST("CHAR(32)", "REPEAT('C', 16)",
+ MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
+ char, [255],
+ (" - CHAR(32) / char[] / MYSQL_TYPE_STRING:\t\t '%s'", (char *) pspv),
+ !strcmp(pspv, "CCCCCCCCCCCCCCCC"));
+
+ WL4435_TEST("VARCHAR(32)", "REPEAT('V', 16)",
+ MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_VAR_STRING,
+ char, [255],
+ (" - VARCHAR(32) / char[] / MYSQL_TYPE_VAR_STRING:\t '%s'", (char *) pspv),
+ !strcmp(pspv, "VVVVVVVVVVVVVVVV"));
+
+ WL4435_TEST("TINYTEXT", "REPEAT('t', 16)",
+ MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - TINYTEXT / char[] / MYSQL_TYPE_TINY_BLOB:\t\t '%s'", (char *) pspv),
+ !strcmp(pspv, "tttttttttttttttt"));
+
+ WL4435_TEST("TEXT", "REPEAT('t', 16)",
+ MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - TEXT / char[] / MYSQL_TYPE_BLOB:\t\t\t '%s'", (char *) pspv),
+ !strcmp(pspv, "tttttttttttttttt"));
+
+ WL4435_TEST("MEDIUMTEXT", "REPEAT('t', 16)",
+ MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - MEDIUMTEXT / char[] / MYSQL_TYPE_MEDIUM_BLOB:\t '%s'", (char *) pspv),
+ !strcmp(pspv, "tttttttttttttttt"));
+
+ WL4435_TEST("LONGTEXT", "REPEAT('t', 16)",
+ MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - LONGTEXT / char[] / MYSQL_TYPE_LONG_BLOB:\t\t '%s'", (char *) pspv),
+ !strcmp(pspv, "tttttttttttttttt"));
+
+ WL4435_TEST("BINARY(32)", "REPEAT('\1', 16)",
+ MYSQL_TYPE_STRING, MYSQL_TYPE_STRING,
+ char, [255],
+ (" - BINARY(32) / char[] / MYSQL_TYPE_STRING:\t\t '%s'", (char *) pspv),
+ memset(tmp, 1, 16) && !memcmp(tmp, pspv, 16));
+
+ WL4435_TEST("VARBINARY(32)", "REPEAT('\1', 16)",
+ MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_VAR_STRING,
+ char, [255],
+ (" - VARBINARY(32) / char[] / MYSQL_TYPE_VAR_STRING:\t '%s'", (char *) pspv),
+ memset(tmp, 1, 16) && !memcmp(tmp, pspv, 16));
+
+ WL4435_TEST("TINYBLOB", "REPEAT('\2', 16)",
+ MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - TINYBLOB / char[] / MYSQL_TYPE_TINY_BLOB:\t\t '%s'", (char *) pspv),
+ memset(tmp, 2, 16) && !memcmp(tmp, pspv, 16));
+
+ WL4435_TEST("BLOB", "REPEAT('\2', 16)",
+ MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - BLOB / char[] / MYSQL_TYPE_BLOB:\t\t\t '%s'", (char *) pspv),
+ memset(tmp, 2, 16) && !memcmp(tmp, pspv, 16));
+
+ WL4435_TEST("MEDIUMBLOB", "REPEAT('\2', 16)",
+ MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - MEDIUMBLOB / char[] / MYSQL_TYPE_MEDIUM_BLOB:\t '%s'", (char *) pspv),
+ memset(tmp, 2, 16) && !memcmp(tmp, pspv, 16));
+
+ WL4435_TEST("LONGBLOB", "REPEAT('\2', 16)",
+ MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB,
+ char, [255],
+ (" - LONGBLOB / char[] / MYSQL_TYPE_LONG_BLOB:\t\t '%s'", (char *) pspv),
+ memset(tmp, 2, 16) && !memcmp(tmp, pspv, 16));
+}
+
+
+/* Test simple prepare field results */
+
+static void test_prepare_field_result()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_prepare_field_result");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_field_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_prepare_field_result(int_c int, "
+ "var_c varchar(50), ts_c timestamp, "
+ "char_c char(4), date_c date, extra tinyint)");
+ myquery(rc);
+
+ /* insert */
+ strmov(query, "SELECT int_c, var_c, date_c as date, ts_c, char_c FROM "
+ " test_prepare_field_result as t1 WHERE int_c=?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 1);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+
+ my_print_result_metadata(result);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n\n field attributes:\n");
+ verify_prepare_field(result, 0, "int_c", "int_c", MYSQL_TYPE_LONG,
+ "t1", "test_prepare_field_result", current_db, 11, 0);
+ verify_prepare_field(result, 1, "var_c", "var_c", MYSQL_TYPE_VAR_STRING,
+ "t1", "test_prepare_field_result", current_db, 50, 0);
+ verify_prepare_field(result, 2, "date", "date_c", MYSQL_TYPE_DATE,
+ "t1", "test_prepare_field_result", current_db, 10, 0);
+ verify_prepare_field(result, 3, "ts_c", "ts_c", MYSQL_TYPE_TIMESTAMP,
+ "t1", "test_prepare_field_result", current_db, 19, 0);
+ verify_prepare_field(result, 4, "char_c", "char_c",
+ (mysql_get_server_version(mysql) <= 50000 ?
+ MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING),
+ "t1", "test_prepare_field_result", current_db, 4, 0);
+
+ verify_field_count(result, 5);
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple prepare field results */
+
+static void test_prepare_syntax()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_prepare_syntax");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_syntax");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_prepare_syntax("
+ "id int, name varchar(50), extra int)");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_prepare_syntax VALUES(?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt_r(stmt);
+
+ strmov(query, "SELECT id, name FROM test_prepare_syntax WHERE id=? AND WHERE");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt_r(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+}
+
+
+/* Test a simple prepare */
+
+static void test_prepare()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ int int_data, o_int_data;
+ char str_data[50], data[50];
+ char tiny_data, o_tiny_data;
+ short small_data, o_small_data;
+ longlong big_data, o_big_data;
+ float real_data, o_real_data;
+ double double_data, o_double_data;
+ ulong length[7], len;
+ my_bool is_null[7];
+ char llbuf[22];
+ MYSQL_BIND my_bind[7];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_prepare");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_prepare");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE my_prepare(col1 tinyint, "
+ "col2 varchar(15), col3 int, "
+ "col4 smallint, col5 bigint, "
+ "col6 float, col7 double )");
+ myquery(rc);
+
+ /* insert by prepare */
+ strxmov(query, "INSERT INTO my_prepare VALUES(?, ?, ?, ?, ?, ?, ?)", NullS);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 7);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /* tinyint */
+ my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[0].buffer= (void *)&tiny_data;
+ /* string */
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)str_data;
+ my_bind[1].buffer_length= 1000; /* Max string length */
+ /* integer */
+ my_bind[2].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[2].buffer= (void *)&int_data;
+ /* short */
+ my_bind[3].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[3].buffer= (void *)&small_data;
+ /* bigint */
+ my_bind[4].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[4].buffer= (void *)&big_data;
+ /* float */
+ my_bind[5].buffer_type= MYSQL_TYPE_FLOAT;
+ my_bind[5].buffer= (void *)&real_data;
+ /* double */
+ my_bind[6].buffer_type= MYSQL_TYPE_DOUBLE;
+ my_bind[6].buffer= (void *)&double_data;
+
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].length= &length[i];
+ my_bind[i].is_null= &is_null[i];
+ is_null[i]= 0;
+ }
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ int_data= 320;
+ small_data= 1867;
+ big_data= 1000;
+ real_data= 2;
+ double_data= 6578.001;
+
+ /* now, execute the prepared statement to insert 10 records.. */
+ for (tiny_data= 0; tiny_data < 100; tiny_data++)
+ {
+ length[1]= sprintf(str_data, "MySQL%d", int_data);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ int_data += 25;
+ small_data += 10;
+ big_data += 100;
+ real_data += 1;
+ double_data += 10.09;
+ }
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= my_stmt_result("SELECT * FROM my_prepare");
+ DIE_UNLESS(tiny_data == (char) rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM my_prepare");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /* get the result */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ o_int_data= 320;
+ o_small_data= 1867;
+ o_big_data= 1000;
+ o_real_data= 2;
+ o_double_data= 6578.001;
+
+ /* now, execute the prepared statement to insert 10 records.. */
+ for (o_tiny_data= 0; o_tiny_data < 100; o_tiny_data++)
+ {
+ len= sprintf(data, "MySQL%d", o_int_data);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "\n\t tiny : %d (%lu)", tiny_data, length[0]);
+ fprintf(stdout, "\n\t short : %d (%lu)", small_data, length[3]);
+ fprintf(stdout, "\n\t int : %d (%lu)", int_data, length[2]);
+ fprintf(stdout, "\n\t big : %s (%lu)", llstr(big_data, llbuf),
+ length[4]);
+
+ fprintf(stdout, "\n\t float : %f (%lu)", real_data, length[5]);
+ fprintf(stdout, "\n\t double : %f (%lu)", double_data, length[6]);
+
+ fprintf(stdout, "\n\t str : %s (%lu)", str_data, length[1]);
+ }
+
+ DIE_UNLESS(tiny_data == o_tiny_data);
+ DIE_UNLESS(is_null[0] == 0);
+ DIE_UNLESS(length[0] == 1);
+
+ DIE_UNLESS(int_data == o_int_data);
+ DIE_UNLESS(length[2] == 4);
+
+ DIE_UNLESS(small_data == o_small_data);
+ DIE_UNLESS(length[3] == 2);
+
+ DIE_UNLESS(big_data == o_big_data);
+ DIE_UNLESS(length[4] == 8);
+
+ DIE_UNLESS(real_data == o_real_data);
+ DIE_UNLESS(length[5] == 4);
+
+ DIE_UNLESS(cmp_double(&double_data, &o_double_data));
+ DIE_UNLESS(length[6] == 8);
+
+ DIE_UNLESS(strcmp(data, str_data) == 0);
+ DIE_UNLESS(length[1] == len);
+
+ o_int_data += 25;
+ o_small_data += 10;
+ o_big_data += 100;
+ o_real_data += 1;
+ o_double_data += 10.09;
+ }
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+}
+
+
+/* Test double comparision */
+
+static void test_double_compare()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char real_data[10], tiny_data;
+ double double_data;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[3];
+ ulong length[3];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_double_compare");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_double_compare");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_double_compare(col1 tinyint, "
+ " col2 float, col3 double )");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_double_compare "
+ "VALUES (1, 10.2, 34.5)");
+ myquery(rc);
+
+ strmov(query, "UPDATE test_double_compare SET col1=100 "
+ "WHERE col1 = ? AND col2 = ? AND COL3 = ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 3);
+
+ /* Always bzero bind array because there can be internal members */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /* tinyint */
+ my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[0].buffer= (void *)&tiny_data;
+
+ /* string->float */
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)&real_data;
+ my_bind[1].buffer_length= sizeof(real_data);
+ my_bind[1].length= &length[1];
+ length[1]= 10;
+
+ /* double */
+ my_bind[2].buffer_type= MYSQL_TYPE_DOUBLE;
+ my_bind[2].buffer= (void *)&double_data;
+
+ tiny_data= 1;
+ memset(real_data, 0, sizeof real_data);
+ strmov(real_data, "10.2");
+ double_data= 34.5;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_affected_rows(0);
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM test_double_compare");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS((int)tiny_data == rc);
+ mysql_free_result(result);
+}
+
+
+/* Test simple null */
+
+static void test_null()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ uint nData;
+ MYSQL_BIND my_bind[2];
+ my_bool is_null[2];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_null");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_null");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_null(col1 int, col2 varchar(50))");
+ myquery(rc);
+
+ /* insert by prepare, wrong column name */
+ strmov(query, "INSERT INTO test_null(col3, col2) VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt_r(stmt);
+
+ strmov(query, "INSERT INTO test_null(col1, col2) VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].is_null= &is_null[0];
+ is_null[0]= 1;
+ my_bind[1]= my_bind[0];
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /* now, execute the prepared statement to insert 10 records.. */
+ for (nData= 0; nData<10; nData++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+
+ /* Re-bind with MYSQL_TYPE_NULL */
+ my_bind[0].buffer_type= MYSQL_TYPE_NULL;
+ is_null[0]= 0; /* reset */
+ my_bind[1]= my_bind[0];
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ for (nData= 0; nData<10; nData++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ nData*= 2;
+ rc= my_stmt_result("SELECT * FROM test_null");;
+ DIE_UNLESS((int) nData == rc);
+
+ /* Fetch results */
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&nData; /* this buffer won't be altered */
+ my_bind[0].length= 0;
+ my_bind[1]= my_bind[0];
+ my_bind[0].is_null= &is_null[0];
+ my_bind[1].is_null= &is_null[1];
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_null");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= 0;
+ is_null[0]= is_null[1]= 0;
+ while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
+ {
+ DIE_UNLESS(is_null[0]);
+ DIE_UNLESS(is_null[1]);
+ rc++;
+ is_null[0]= is_null[1]= 0;
+ }
+ DIE_UNLESS(rc == (int) nData);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test for NULL as PS parameter (BUG#3367, BUG#3371) */
+
+static void test_ps_null_param()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+
+ MYSQL_BIND in_bind;
+ my_bool in_is_null;
+ long int in_long;
+
+ MYSQL_BIND out_bind;
+ ulong out_length;
+ my_bool out_is_null;
+ char out_str_data[20];
+
+ const char *queries[]= {"select ?", "select ?+1",
+ "select col1 from test_ps_nulls where col1 <=> ?",
+ NULL
+ };
+ const char **cur_query= queries;
+
+ myheader("test_null_ps_param_in_result");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ps_nulls");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_ps_nulls(col1 int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_ps_nulls values (1), (null)");
+ myquery(rc);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) &in_bind, sizeof(in_bind));
+ bzero((char*) &out_bind, sizeof(out_bind));
+
+ in_bind.buffer_type= MYSQL_TYPE_LONG;
+ in_bind.is_null= &in_is_null;
+ in_bind.length= 0;
+ in_bind.buffer= (void *)&in_long;
+ in_is_null= 1;
+ in_long= 1;
+
+ out_bind.buffer_type= MYSQL_TYPE_STRING;
+ out_bind.is_null= &out_is_null;
+ out_bind.length= &out_length;
+ out_bind.buffer= out_str_data;
+ out_bind.buffer_length= array_elements(out_str_data);
+
+ /* Execute several queries, all returning NULL in result. */
+ for(cur_query= queries; *cur_query; cur_query++)
+ {
+ char query[MAX_TEST_QUERY_LENGTH];
+ strmov(query, *cur_query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc= mysql_stmt_bind_param(stmt, &in_bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_bind_result(stmt, &out_bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc != MYSQL_NO_DATA);
+ DIE_UNLESS(out_is_null);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ mysql_stmt_close(stmt);
+ }
+}
+
+
+/* Test fetch null */
+
+static void test_fetch_null()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ int i, nData;
+ MYSQL_BIND my_bind[11];
+ ulong length[11];
+ my_bool is_null[11];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_fetch_null");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_fetch_null");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_fetch_null("
+ " col1 tinyint, col2 smallint, "
+ " col3 int, col4 bigint, "
+ " col5 float, col6 double, "
+ " col7 date, col8 time, "
+ " col9 varbinary(10), "
+ " col10 varchar(50), "
+ " col11 char(20))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_fetch_null (col11) "
+ "VALUES (1000), (88), (389789)");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* fetch */
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[i].is_null= &is_null[i];
+ my_bind[i].length= &length[i];
+ }
+ my_bind[i-1].buffer= (void *)&nData; /* Last column is not null */
+
+ strmov((char *)query , "SELECT * FROM test_fetch_null");
+
+ rc= my_stmt_result(query);
+ DIE_UNLESS(rc == 3);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= 0;
+ while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
+ {
+ rc++;
+ for (i= 0; i < 10; i++)
+ {
+ if (!opt_silent)
+ fprintf(stdout, "\n data[%d] : %s", i,
+ is_null[i] ? "NULL" : "NOT NULL");
+ DIE_UNLESS(is_null[i]);
+ }
+ if (!opt_silent)
+ fprintf(stdout, "\n data[%d]: %d", i, nData);
+ DIE_UNLESS(nData == 1000 || nData == 88 || nData == 389789);
+ DIE_UNLESS(is_null[i] == 0);
+ DIE_UNLESS(length[i] == 4);
+ }
+ DIE_UNLESS(rc == 3);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple select */
+
+static void test_select_version()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+
+ myheader("test_select_version");
+
+ stmt= mysql_simple_prepare(mysql, "SELECT @@version");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 0);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_process_stmt_result(stmt);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple show */
+
+static void test_select_show_table()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+
+ myheader("test_select_show_table");
+
+ stmt= mysql_simple_prepare(mysql, "SHOW TABLES FROM mysql");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 0);
+
+ for (i= 1; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+
+ my_process_stmt_result(stmt);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple select to debug */
+
+static void test_select_direct()
+{
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_select_direct");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_select(id int, id1 tinyint, "
+ " id2 float, "
+ " id3 double, "
+ " name varchar(50))");
+ myquery(rc);
+
+ /* insert a row and commit the transaction */
+ rc= mysql_query(mysql, "INSERT INTO test_select VALUES(10, 5, 2.3, 4.5, 'venu')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SELECT * FROM test_select");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+}
+
+
+/* Test simple select with prepare */
+
+static void test_select_prepare()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+
+ myheader("test_select_prepare");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_select(id int, name varchar(50))");
+ myquery(rc);
+
+ /* insert a row and commit the transaction */
+ rc= mysql_query(mysql, "INSERT INTO test_select VALUES(10, 'venu')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_select");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE test_select");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_select(id tinyint, id1 int, "
+ " id2 float, id3 float, "
+ " name varchar(50))");
+ myquery(rc);
+
+ /* insert a row and commit the transaction */
+ rc= mysql_query(mysql, "INSERT INTO test_select(id, id1, id2, name) VALUES(10, 5, 2.3, 'venu')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_select");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple select */
+
+static void test_select()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char szData[25];
+ int nData= 1;
+ MYSQL_BIND my_bind[2];
+ ulong length[2];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_select");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_select(id int, name varchar(50))");
+ myquery(rc);
+
+ /* insert a row and commit the transaction */
+ rc= mysql_query(mysql, "INSERT INTO test_select VALUES(10, 'venu')");
+ myquery(rc);
+
+ /* now insert the second row, and roll back the transaction */
+ rc= mysql_query(mysql, "INSERT INTO test_select VALUES(20, 'mysql')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ strmov(query, "SELECT * FROM test_select WHERE id= ? "
+ "AND CONVERT(name USING utf8) =?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /* string data */
+ nData= 10;
+ strmov(szData, (char *)"venu");
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)szData;
+ my_bind[1].buffer_length= 4;
+ my_bind[1].length= &length[1];
+ length[1]= 4;
+
+ my_bind[0].buffer= (void *)&nData;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/*
+ Test for BUG#3420 ("select id1, value1 from t where id= ? or value= ?"
+ returns all rows in the table)
+*/
+
+static void test_ps_conj_select()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_BIND my_bind[2];
+ int32 int_data;
+ char str_data[32];
+ unsigned long str_length;
+ char query[MAX_TEST_QUERY_LENGTH];
+ myheader("test_ps_conj_select");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (id1 int(11) NOT NULL default '0', "
+ "value2 varchar(100), value1 varchar(100))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 values (1, 'hh', 'hh'), "
+ "(2, 'hh', 'hh'), (1, 'ii', 'ii'), (2, 'ii', 'ii')");
+ myquery(rc);
+
+ strmov(query, "SELECT id1, value1 from t1 where id1= ? or "
+ "CONVERT(value1 USING utf8)= ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&int_data;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+ my_bind[1].buffer= (void *)str_data;
+ my_bind[1].buffer_length= array_elements(str_data);
+ my_bind[1].length= &str_length;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ int_data= 1;
+ strmov(str_data, "hh");
+ str_length= strlen(str_data);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 3);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* reads Qcache_hits from server and returns its value */
+static uint query_cache_hits(MYSQL *conn)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ int rc;
+ uint result;
+
+ rc= mysql_query(conn, "show status like 'qcache_hits'");
+ myquery(rc);
+ res= mysql_use_result(conn);
+ DIE_UNLESS(res);
+
+ row= mysql_fetch_row(res);
+ DIE_UNLESS(row);
+
+ result= atoi(row[1]);
+ mysql_free_result(res);
+ return result;
+}
+
+
+/*
+ utility for the next test; expects 3 rows in the result from a SELECT,
+ compares each row/field with an expected value.
+ */
+#define test_ps_query_cache_result(i1,s1,l1,i2,s2,l2,i3,s3,l3) \
+ r_metadata= mysql_stmt_result_metadata(stmt); \
+ DIE_UNLESS(r_metadata != NULL); \
+ rc= mysql_stmt_fetch(stmt); \
+ check_execute(stmt, rc); \
+ if (!opt_silent) \
+ fprintf(stdout, "\n row 1: %d, %s(%lu)", r_int_data, \
+ r_str_data, r_str_length); \
+ DIE_UNLESS((r_int_data == i1) && (r_str_length == l1) && \
+ (strcmp(r_str_data, s1) == 0)); \
+ rc= mysql_stmt_fetch(stmt); \
+ check_execute(stmt, rc); \
+ if (!opt_silent) \
+ fprintf(stdout, "\n row 2: %d, %s(%lu)", r_int_data, \
+ r_str_data, r_str_length); \
+ DIE_UNLESS((r_int_data == i2) && (r_str_length == l2) && \
+ (strcmp(r_str_data, s2) == 0)); \
+ rc= mysql_stmt_fetch(stmt); \
+ check_execute(stmt, rc); \
+ if (!opt_silent) \
+ fprintf(stdout, "\n row 3: %d, %s(%lu)", r_int_data, \
+ r_str_data, r_str_length); \
+ DIE_UNLESS((r_int_data == i3) && (r_str_length == l3) && \
+ (strcmp(r_str_data, s3) == 0)); \
+ rc= mysql_stmt_fetch(stmt); \
+ DIE_UNLESS(rc == MYSQL_NO_DATA); \
+ mysql_free_result(r_metadata);
+
+
+/*
+ Check that query cache is available in server.
+*/
+static my_bool is_query_cache_available()
+{
+ int rc;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ int res= -1;
+
+ rc= mysql_query(mysql, "SHOW VARIABLES LIKE 'have_query_cache'");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ DIE_UNLESS(result);
+
+ row= mysql_fetch_row(result);
+ DIE_UNLESS(row != NULL);
+ if (strcmp(row[1], "YES") == 0)
+ res= 1;
+ else if (strcmp(row[1], "NO") == 0)
+ res= 0;
+ mysql_free_result(result);
+
+ DIE_UNLESS(res == 0 || res == 1);
+ return res;
+}
+
+/*
+ Test that prepared statements make use of the query cache just as normal
+ statements (BUG#735).
+*/
+static void test_ps_query_cache()
+{
+ MYSQL *lmysql= mysql;
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_BIND p_bind[2],r_bind[2]; /* p: param bind; r: result bind */
+ int32 p_int_data, r_int_data;
+ char p_str_data[32], r_str_data[32];
+ unsigned long p_str_length, r_str_length;
+ MYSQL_RES *r_metadata;
+ char query[MAX_TEST_QUERY_LENGTH];
+ uint hits1, hits2;
+ enum enum_test_ps_query_cache
+ {
+ /*
+ We iterate the same prepare/executes block, but have iterations where
+ we vary the query cache conditions.
+ */
+ /* the query cache is enabled for the duration of prep&execs: */
+ TEST_QCACHE_ON= 0,
+ /*
+ same but using a new connection (to see if qcache serves results from
+ the previous connection as it should):
+ */
+ TEST_QCACHE_ON_WITH_OTHER_CONN,
+ /*
+ First border case: disables the query cache before prepare and
+ re-enables it before execution (to test if we have no bug then):
+ */
+ TEST_QCACHE_OFF_ON,
+ /*
+ Second border case: enables the query cache before prepare and
+ disables it before execution:
+ */
+ TEST_QCACHE_ON_OFF
+ };
+ enum enum_test_ps_query_cache iteration;
+
+ myheader("test_ps_query_cache");
+
+ if (! is_query_cache_available())
+ {
+ fprintf(stdout, "Skipping test_ps_query_cache: Query cache not available.\n");
+ return;
+ }
+
+ rc= mysql_set_character_set(mysql, "utf8");
+ myquery(rc);
+
+ /* prepare the table */
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (id1 int(11) NOT NULL default '0', "
+ "value2 varchar(100), value1 varchar(100))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 values (1, 'hh', 'hh'), "
+ "(2, 'hh', 'hh'), (1, 'ii', 'ii'), (2, 'ii', 'ii')");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "set @save_query_cache_type="
+ "@@global.query_cache_type,"
+ "@save_query_cache_size="
+ "@@global.query_cache_size");
+ myquery(rc);
+ rc= mysql_query(lmysql, "set global query_cache_type=ON");
+ myquery(rc);
+ rc= mysql_query(lmysql, "set local query_cache_type=ON");
+ myquery(rc);
+
+ for (iteration= TEST_QCACHE_ON; iteration <= TEST_QCACHE_ON_OFF; iteration++)
+ {
+
+ switch (iteration) {
+ case TEST_QCACHE_ON:
+ case TEST_QCACHE_ON_OFF:
+ rc= mysql_query(lmysql, "set global query_cache_size=1000000");
+ myquery(rc);
+ break;
+ case TEST_QCACHE_OFF_ON:
+ rc= mysql_query(lmysql, "set global query_cache_size=0");
+ myquery(rc);
+ break;
+ case TEST_QCACHE_ON_WITH_OTHER_CONN:
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a test connection ...");
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ printf("mysql_client_init() failed");
+ DIE_UNLESS(0);
+ }
+ if (!(mysql_real_connect(lmysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ printf("connection failed");
+ mysql_close(lmysql);
+ DIE_UNLESS(0);
+ }
+ rc= mysql_query(lmysql, "SET SQL_MODE=''");
+ myquery(rc);
+ rc= mysql_set_character_set(lmysql, "utf8");
+ myquery(rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+ break;
+ }
+
+ strmov(query, "select id1, value1 from t1 where id1= ? or "
+ "CONVERT(value1 USING utf8)= ?");
+ stmt= mysql_simple_prepare(lmysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ switch (iteration) {
+ case TEST_QCACHE_OFF_ON:
+ rc= mysql_query(lmysql, "set global query_cache_size=1000000");
+ myquery(rc);
+ break;
+ case TEST_QCACHE_ON_OFF:
+ rc= mysql_query(lmysql, "set global query_cache_size=0");
+ myquery(rc);
+ default:
+ break;
+ }
+
+ bzero((char*) p_bind, sizeof(p_bind));
+ p_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ p_bind[0].buffer= (void *)&p_int_data;
+ p_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+ p_bind[1].buffer= (void *)p_str_data;
+ p_bind[1].buffer_length= array_elements(p_str_data);
+ p_bind[1].length= &p_str_length;
+
+ rc= mysql_stmt_bind_param(stmt, p_bind);
+ check_execute(stmt, rc);
+
+ p_int_data= 1;
+ strmov(p_str_data, "hh");
+ p_str_length= strlen(p_str_data);
+
+ bzero((char*) r_bind, sizeof(r_bind));
+ r_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ r_bind[0].buffer= (void *)&r_int_data;
+ r_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+ r_bind[1].buffer= (void *)r_str_data;
+ r_bind[1].buffer_length= array_elements(r_str_data);
+ r_bind[1].length= &r_str_length;
+
+ rc= mysql_stmt_bind_result(stmt, r_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ test_ps_query_cache_result(1, "hh", 2, 2, "hh", 2, 1, "ii", 2);
+
+ /* now retry with the same parameter values and see qcache hits */
+ hits1= query_cache_hits(lmysql);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ test_ps_query_cache_result(1, "hh", 2, 2, "hh", 2, 1, "ii", 2);
+ hits2= query_cache_hits(lmysql);
+ switch(iteration) {
+ case TEST_QCACHE_ON_WITH_OTHER_CONN:
+ case TEST_QCACHE_ON: /* should have hit */
+ DIE_UNLESS(hits2-hits1 == 1);
+ break;
+ case TEST_QCACHE_OFF_ON:
+ case TEST_QCACHE_ON_OFF: /* should not have hit */
+ DIE_UNLESS(hits2-hits1 == 0);
+ break;
+ }
+
+ /* now modify parameter values and see qcache hits */
+ strmov(p_str_data, "ii");
+ p_str_length= strlen(p_str_data);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ test_ps_query_cache_result(1, "hh", 2, 1, "ii", 2, 2, "ii", 2);
+ hits1= query_cache_hits(lmysql);
+
+ switch(iteration) {
+ case TEST_QCACHE_ON:
+ case TEST_QCACHE_OFF_ON:
+ case TEST_QCACHE_ON_OFF: /* should not have hit */
+ DIE_UNLESS(hits2-hits1 == 0);
+ break;
+ case TEST_QCACHE_ON_WITH_OTHER_CONN: /* should have hit */
+ DIE_UNLESS(hits1-hits2 == 1);
+ break;
+ }
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ test_ps_query_cache_result(1, "hh", 2, 1, "ii", 2, 2, "ii", 2);
+ hits2= query_cache_hits(lmysql);
+
+ mysql_stmt_close(stmt);
+
+ switch(iteration) {
+ case TEST_QCACHE_ON: /* should have hit */
+ DIE_UNLESS(hits2-hits1 == 1);
+ break;
+ case TEST_QCACHE_OFF_ON:
+ case TEST_QCACHE_ON_OFF: /* should not have hit */
+ DIE_UNLESS(hits2-hits1 == 0);
+ break;
+ case TEST_QCACHE_ON_WITH_OTHER_CONN: /* should have hit */
+ DIE_UNLESS(hits2-hits1 == 1);
+ break;
+ }
+
+ } /* for(iteration=...) */
+
+ if (lmysql != mysql)
+ mysql_close(lmysql);
+
+ rc= mysql_query(mysql, "set global query_cache_size=@save_query_cache_size");
+ myquery(rc);
+ rc= mysql_query(mysql, "set global query_cache_type=@save_query_cache_type");
+ myquery(rc);
+}
+
+
+/* Test BUG#1115 (incorrect string parameter value allocation) */
+
+static void test_bug1115()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_BIND my_bind[1];
+ ulong length[1];
+ char szData[11];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_bug1115");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_select(\
+session_id char(9) NOT NULL, \
+ a int(8) unsigned NOT NULL, \
+ b int(5) NOT NULL, \
+ c int(5) NOT NULL, \
+ d datetime NOT NULL)");
+ myquery(rc);
+ rc= mysql_query(mysql, "INSERT INTO test_select VALUES "
+ "(\"abc\", 1, 2, 3, 2003-08-30), "
+ "(\"abd\", 1, 2, 3, 2003-08-30), "
+ "(\"abf\", 1, 2, 3, 2003-08-30), "
+ "(\"abg\", 1, 2, 3, 2003-08-30), "
+ "(\"abh\", 1, 2, 3, 2003-08-30), "
+ "(\"abj\", 1, 2, 3, 2003-08-30), "
+ "(\"abk\", 1, 2, 3, 2003-08-30), "
+ "(\"abl\", 1, 2, 3, 2003-08-30), "
+ "(\"abq\", 1, 2, 3, 2003-08-30) ");
+ myquery(rc);
+ rc= mysql_query(mysql, "INSERT INTO test_select VALUES "
+ "(\"abw\", 1, 2, 3, 2003-08-30), "
+ "(\"abe\", 1, 2, 3, 2003-08-30), "
+ "(\"abr\", 1, 2, 3, 2003-08-30), "
+ "(\"abt\", 1, 2, 3, 2003-08-30), "
+ "(\"aby\", 1, 2, 3, 2003-08-30), "
+ "(\"abu\", 1, 2, 3, 2003-08-30), "
+ "(\"abi\", 1, 2, 3, 2003-08-30), "
+ "(\"abo\", 1, 2, 3, 2003-08-30), "
+ "(\"abp\", 1, 2, 3, 2003-08-30), "
+ "(\"abz\", 1, 2, 3, 2003-08-30), "
+ "(\"abx\", 1, 2, 3, 2003-08-30)");
+ myquery(rc);
+
+ strmov(query, "SELECT * FROM test_select WHERE "
+ "CONVERT(session_id USING utf8)= ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 1);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ strmov(szData, (char *)"abc");
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)szData;
+ my_bind[0].buffer_length= 10;
+ my_bind[0].length= &length[0];
+ length[0]= 3;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ strmov(szData, (char *)"venu");
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)szData;
+ my_bind[0].buffer_length= 10;
+ my_bind[0].length= &length[0];
+ length[0]= 4;
+ my_bind[0].is_null= 0;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 0);
+
+ strmov(szData, (char *)"abc");
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)szData;
+ my_bind[0].buffer_length= 10;
+ my_bind[0].length= &length[0];
+ length[0]= 3;
+ my_bind[0].is_null= 0;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test BUG#1180 (optimized away part of WHERE clause) */
+
+static void test_bug1180()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_BIND my_bind[1];
+ ulong length[1];
+ char szData[11];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_select_bug");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_select(session_id char(9) NOT NULL)");
+ myquery(rc);
+ rc= mysql_query(mysql, "INSERT INTO test_select VALUES (\"abc\")");
+ myquery(rc);
+
+ strmov(query, "SELECT * FROM test_select WHERE ?= \"1111\" and "
+ "session_id= \"abc\"");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 1);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ strmov(szData, (char *)"abc");
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)szData;
+ my_bind[0].buffer_length= 10;
+ my_bind[0].length= &length[0];
+ length[0]= 3;
+ my_bind[0].is_null= 0;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 0);
+
+ strmov(szData, (char *)"1111");
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)szData;
+ my_bind[0].buffer_length= 10;
+ my_bind[0].length= &length[0];
+ length[0]= 4;
+ my_bind[0].is_null= 0;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ strmov(szData, (char *)"abc");
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)szData;
+ my_bind[0].buffer_length= 10;
+ my_bind[0].length= &length[0];
+ length[0]= 3;
+ my_bind[0].is_null= 0;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 0);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/*
+ Test BUG#1644 (Insertion of more than 3 NULL columns with parameter
+ binding fails)
+*/
+
+static void test_bug1644()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ MYSQL_BIND my_bind[4];
+ int num;
+ my_bool isnull;
+ int rc, i;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_bug1644");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS foo_dfr");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE TABLE foo_dfr(col1 int, col2 int, col3 int, col4 int);");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO foo_dfr VALUES (?, ?, ?, ? )");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 4);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ num= 22;
+ isnull= 0;
+ for (i= 0 ; i < 4 ; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[i].buffer= (void *)&num;
+ my_bind[i].is_null= &isnull;
+ }
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ isnull= 1;
+ for (i= 0 ; i < 4 ; i++)
+ my_bind[i].is_null= &isnull;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ isnull= 0;
+ num= 88;
+ for (i= 0 ; i < 4 ; i++)
+ my_bind[i].is_null= &isnull;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "SELECT * FROM foo_dfr");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 3);
+
+ mysql_data_seek(result, 0);
+
+ row= mysql_fetch_row(result);
+ mytest(row);
+ for (i= 0 ; i < 4 ; i++)
+ {
+ DIE_UNLESS(strcmp(row[i], "22") == 0);
+ }
+ row= mysql_fetch_row(result);
+ mytest(row);
+ for (i= 0 ; i < 4 ; i++)
+ {
+ DIE_UNLESS(row[i] == 0);
+ }
+ row= mysql_fetch_row(result);
+ mytest(row);
+ for (i= 0 ; i < 4 ; i++)
+ {
+ DIE_UNLESS(strcmp(row[i], "88") == 0);
+ }
+ row= mysql_fetch_row(result);
+ mytest_r(row);
+
+ mysql_free_result(result);
+}
+
+
+/* Test simple select show */
+
+static void test_select_show()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_select_show");
+
+ mysql_autocommit(mysql, TRUE);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_show");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_show(id int(4) NOT NULL primary "
+ " key, name char(2))");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "show columns from test_show");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 0);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_process_stmt_result(stmt);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "show tables from mysql like ?");
+ check_stmt_r(stmt);
+
+ strxmov(query, "show tables from ", current_db, " like \'test_show\'", NullS);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_process_stmt_result(stmt);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "describe test_show");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_process_stmt_result(stmt);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "show keys from test_show");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple update */
+
+static void test_simple_update()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char szData[25];
+ int nData= 1;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[2];
+ ulong length[2];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_simple_update");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_update(col1 int, "
+ " col2 varchar(50), col3 int )");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_update VALUES(1, 'MySQL', 100)");
+ myquery(rc);
+
+ verify_affected_rows(1);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* insert by prepare */
+ strmov(query, "UPDATE test_update SET col2= ? WHERE col1= ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ nData= 1;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= szData; /* string data */
+ my_bind[0].buffer_length= sizeof(szData);
+ my_bind[0].length= &length[0];
+ length[0]= sprintf(szData, "updated-data");
+
+ my_bind[1].buffer= (void *) &nData;
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ verify_affected_rows(1);
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM test_update");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+}
+
+
+/* Test simple long data handling */
+
+static void test_long_data()
+{
+ MYSQL_STMT *stmt;
+ int rc, int_data;
+ char *data= NullS;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[3];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_long_data");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_long_data(col1 int, "
+ " col2 long varchar, col3 long varbinary)");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_long_data(col1, col2) VALUES(?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt_r(stmt);
+
+ strmov(query, "INSERT INTO test_long_data(col1, col2, col3) VALUES(?, ?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 3);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= (void *)&int_data;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+
+ my_bind[2]= my_bind[1];
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ int_data= 999;
+ data= (char *)"Michael";
+
+ /* supply data in pieces */
+ rc= mysql_stmt_send_long_data(stmt, 1, data, strlen(data));
+ data= (char *)" 'Monty' Widenius";
+ rc= mysql_stmt_send_long_data(stmt, 1, data, strlen(data));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_send_long_data(stmt, 2, "Venu (venu@mysql.com)", 4);
+ check_execute(stmt, rc);
+
+ /* execute */
+ rc= mysql_stmt_execute(stmt);
+ if (!opt_silent)
+ fprintf(stdout, " mysql_stmt_execute() returned %d\n", rc);
+ check_execute(stmt, rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* now fetch the results ..*/
+ rc= mysql_query(mysql, "SELECT * FROM test_long_data");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+
+ verify_col_data("test_long_data", "col1", "999");
+ verify_col_data("test_long_data", "col2", "Michael 'Monty' Widenius");
+ verify_col_data("test_long_data", "col3", "Venu");
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test long data (string) handling */
+
+static void test_long_data_str()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ char data[255];
+ long length;
+ ulong length1;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[2];
+ my_bool is_null[2];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_long_data_str");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_str");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_long_data_str(id int, longstr long varchar)");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_long_data_str VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= (void *)&length;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].is_null= &is_null[0];
+ is_null[0]= 0;
+ length= 0;
+
+ my_bind[1].buffer= data; /* string data */
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].length= &length1;
+ my_bind[1].is_null= &is_null[1];
+ is_null[1]= 0;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ length= 40;
+ strmov(data, "MySQL AB");
+
+ /* supply data in pieces */
+ for(i= 0; i < 4; i++)
+ {
+ rc= mysql_stmt_send_long_data(stmt, 1, (char *)data, 5);
+ check_execute(stmt, rc);
+ }
+ /* execute */
+ rc= mysql_stmt_execute(stmt);
+ if (!opt_silent)
+ fprintf(stdout, " mysql_stmt_execute() returned %d\n", rc);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* now fetch the results ..*/
+ rc= mysql_query(mysql, "SELECT LENGTH(longstr), longstr FROM test_long_data_str");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+
+ sprintf(data, "%d", i*5);
+ verify_col_data("test_long_data_str", "LENGTH(longstr)", data);
+ data[0]= '\0';
+ while (i--)
+ strxmov(data, data, "MySQL", NullS);
+ verify_col_data("test_long_data_str", "longstr", data);
+
+ rc= mysql_query(mysql, "DROP TABLE test_long_data_str");
+ myquery(rc);
+}
+
+
+/* Test long data (string) handling */
+
+static void test_long_data_str1()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ char data[255];
+ long length;
+ ulong max_blob_length, blob_length= 0, length1;
+ my_bool true_value;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[2];
+ MYSQL_FIELD *field;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_long_data_str1");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_str");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_long_data_str(longstr long varchar, blb long varbinary)");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_long_data_str VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= data; /* string data */
+ my_bind[0].buffer_length= sizeof(data);
+ my_bind[0].length= &length1;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ length1= 0;
+
+ my_bind[1]= my_bind[0];
+ my_bind[1].buffer_type= MYSQL_TYPE_BLOB;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+ length= sprintf(data, "MySQL AB");
+
+ /* supply data in pieces */
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_send_long_data(stmt, 0, data, length);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_send_long_data(stmt, 1, data, 2);
+ check_execute(stmt, rc);
+ }
+
+ /* execute */
+ rc= mysql_stmt_execute(stmt);
+ if (!opt_silent)
+ fprintf(stdout, " mysql_stmt_execute() returned %d\n", rc);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* now fetch the results ..*/
+ rc= mysql_query(mysql, "SELECT LENGTH(longstr), longstr, LENGTH(blb), blb FROM test_long_data_str");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+
+ mysql_field_seek(result, 1);
+ field= mysql_fetch_field(result);
+ max_blob_length= field->max_length;
+
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+
+ sprintf(data, "%ld", (long)i*length);
+ verify_col_data("test_long_data_str", "length(longstr)", data);
+
+ sprintf(data, "%d", i*2);
+ verify_col_data("test_long_data_str", "length(blb)", data);
+
+ /* Test length of field->max_length */
+ stmt= mysql_simple_prepare(mysql, "SELECT * from test_long_data_str");
+ check_stmt(stmt);
+ verify_param_count(stmt, 0);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ result= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_fields(result);
+
+ /* First test what happens if STMT_ATTR_UPDATE_MAX_LENGTH is not used */
+ DIE_UNLESS(field->max_length == 0);
+ mysql_free_result(result);
+
+ /* Enable updating of field->max_length */
+ true_value= 1;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &true_value);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ result= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_fields(result);
+
+ DIE_UNLESS(field->max_length == max_blob_length);
+
+ /* Fetch results into a data buffer that is smaller than data */
+ bzero((char*) my_bind, sizeof(*my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_BLOB;
+ my_bind[0].buffer= (void *) &data; /* this buffer won't be altered */
+ my_bind[0].buffer_length= 16;
+ my_bind[0].length= &blob_length;
+ my_bind[0].error= &my_bind[0].error_value;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ data[16]= 0;
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(my_bind[0].error_value);
+ DIE_UNLESS(strlen(data) == 16);
+ DIE_UNLESS(blob_length == max_blob_length);
+
+ /* Fetch all data */
+ bzero((char*) (my_bind+1), sizeof(*my_bind));
+ my_bind[1].buffer_type= MYSQL_TYPE_BLOB;
+ my_bind[1].buffer= (void *) &data; /* this buffer won't be altered */
+ my_bind[1].buffer_length= sizeof(data);
+ my_bind[1].length= &blob_length;
+ bzero(data, sizeof(data));
+ mysql_stmt_fetch_column(stmt, my_bind+1, 0, 0);
+ DIE_UNLESS(strlen(data) == max_blob_length);
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+
+ /* Drop created table */
+ rc= mysql_query(mysql, "DROP TABLE test_long_data_str");
+ myquery(rc);
+}
+
+
+/* Test long data (binary) handling */
+
+static void test_long_data_bin()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char data[255];
+ long length;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[2];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+
+ myheader("test_long_data_bin");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_bin");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_long_data_bin(id int, longbin long varbinary)");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_long_data_bin VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= (void *)&length;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ length= 0;
+
+ my_bind[1].buffer= data; /* string data */
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG_BLOB;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ length= 10;
+ strmov(data, "MySQL AB");
+
+ /* supply data in pieces */
+ {
+ int i;
+ for (i= 0; i < 100; i++)
+ {
+ rc= mysql_stmt_send_long_data(stmt, 1, (char *)data, 4);
+ check_execute(stmt, rc);
+ }
+ }
+ /* execute */
+ rc= mysql_stmt_execute(stmt);
+ if (!opt_silent)
+ fprintf(stdout, " mysql_stmt_execute() returned %d\n", rc);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* now fetch the results ..*/
+ rc= mysql_query(mysql, "SELECT LENGTH(longbin), longbin FROM test_long_data_bin");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+}
+
+
+/* Test simple delete */
+
+static void test_simple_delete()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char szData[30]= {0};
+ int nData= 1;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[2];
+ ulong length[2];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_simple_delete");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_simple_delete");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_simple_delete(col1 int, \
+ col2 varchar(50), col3 int )");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_simple_delete VALUES(1, 'MySQL', 100)");
+ myquery(rc);
+
+ verify_affected_rows(1);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* insert by prepare */
+ strmov(query, "DELETE FROM test_simple_delete WHERE col1= ? AND "
+ "CONVERT(col2 USING utf8)= ? AND col3= 100");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ nData= 1;
+ strmov(szData, "MySQL");
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= szData; /* string data */
+ my_bind[1].buffer_length= sizeof(szData);
+ my_bind[1].length= &length[1];
+ length[1]= 5;
+
+ my_bind[0].buffer= (void *)&nData;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_affected_rows(1);
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM test_simple_delete");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+ mysql_free_result(result);
+}
+
+
+/* Test simple update */
+
+static void test_update()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char szData[25];
+ int nData= 1;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[2];
+ ulong length[2];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_update");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_update("
+ "col1 int primary key auto_increment, "
+ "col2 varchar(50), col3 int )");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_update(col2, col3) VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /* string data */
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= szData;
+ my_bind[0].buffer_length= sizeof(szData);
+ my_bind[0].length= &length[0];
+ length[0]= sprintf(szData, "inserted-data");
+
+ my_bind[1].buffer= (void *)&nData;
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ nData= 100;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_affected_rows(1);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "UPDATE test_update SET col2= ? WHERE col3= ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+ nData= 100;
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= szData;
+ my_bind[0].buffer_length= sizeof(szData);
+ my_bind[0].length= &length[0];
+ length[0]= sprintf(szData, "updated-data");
+
+ my_bind[1].buffer= (void *)&nData;
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ verify_affected_rows(1);
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM test_update");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+}
+
+
+/* Test prepare without parameters */
+
+static void test_prepare_noparam()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_RES *result;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_prepare_noparam");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_prepare");
+ myquery(rc);
+
+
+ rc= mysql_query(mysql, "CREATE TABLE my_prepare(col1 int, col2 varchar(50))");
+ myquery(rc);
+
+ /* insert by prepare */
+ strmov(query, "INSERT INTO my_prepare VALUES(10, 'venu')");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 0);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM my_prepare");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+}
+
+
+/* Test simple bind result */
+
+static void test_bind_result()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ int nData;
+ ulong length1;
+ char szData[100];
+ MYSQL_BIND my_bind[2];
+ my_bool is_null[2];
+
+ myheader("test_bind_result");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_result(col1 int , col2 varchar(50))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_bind_result VALUES(10, 'venu')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_bind_result VALUES(20, 'MySQL')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_bind_result(col2) VALUES('monty')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* fetch */
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *) &nData; /* integer data */
+ my_bind[0].is_null= &is_null[0];
+
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= szData; /* string data */
+ my_bind[1].buffer_length= sizeof(szData);
+ my_bind[1].length= &length1;
+ my_bind[1].is_null= &is_null[1];
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_bind_result");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %d, %s(%lu)", nData, szData, length1);
+ DIE_UNLESS(nData == 10);
+ DIE_UNLESS(strcmp(szData, "venu") == 0);
+ DIE_UNLESS(length1 == 4);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 2: %d, %s(%lu)", nData, szData, length1);
+ DIE_UNLESS(nData == 20);
+ DIE_UNLESS(strcmp(szData, "MySQL") == 0);
+ DIE_UNLESS(length1 == 5);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent && is_null[0])
+ fprintf(stdout, "\n row 3: NULL, %s(%lu)", szData, length1);
+ DIE_UNLESS(is_null[0]);
+ DIE_UNLESS(strcmp(szData, "monty") == 0);
+ DIE_UNLESS(length1 == 5);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test ext bind result */
+
+static void test_bind_result_ext()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ uchar t_data;
+ short s_data;
+ int i_data;
+ longlong b_data;
+ float f_data;
+ double d_data;
+ char szData[20], bData[20];
+ ulong szLength, bLength;
+ MYSQL_BIND my_bind[8];
+ ulong length[8];
+ my_bool is_null[8];
+ char llbuf[22];
+ myheader("test_bind_result_ext");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_result(c1 tinyint, "
+ " c2 smallint, "
+ " c3 int, c4 bigint, "
+ " c5 float, c6 double, "
+ " c7 varbinary(10), "
+ " c8 varchar(50))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_bind_result "
+ "VALUES (19, 2999, 3999, 4999999, "
+ " 2345.6, 5678.89563, 'venu', 'mysql')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].length= &length[i];
+ my_bind[i].is_null= &is_null[i];
+ }
+
+ my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[0].buffer= (void *)&t_data;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[2].buffer_type= MYSQL_TYPE_LONG;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[1].buffer= (void *)&s_data;
+
+ my_bind[2].buffer= (void *)&i_data;
+ my_bind[3].buffer= (void *)&b_data;
+
+ my_bind[4].buffer_type= MYSQL_TYPE_FLOAT;
+ my_bind[4].buffer= (void *)&f_data;
+
+ my_bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
+ my_bind[5].buffer= (void *)&d_data;
+
+ my_bind[6].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[6].buffer= (void *)szData;
+ my_bind[6].buffer_length= sizeof(szData);
+ my_bind[6].length= &szLength;
+
+ my_bind[7].buffer_type= MYSQL_TYPE_TINY_BLOB;
+ my_bind[7].buffer= (void *)&bData;
+ my_bind[7].length= &bLength;
+ my_bind[7].buffer_length= sizeof(bData);
+
+ stmt= mysql_simple_prepare(mysql, "select * from test_bind_result");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n data (tiny) : %d", t_data);
+ fprintf(stdout, "\n data (short) : %d", s_data);
+ fprintf(stdout, "\n data (int) : %d", i_data);
+ fprintf(stdout, "\n data (big) : %s", llstr(b_data, llbuf));
+
+ fprintf(stdout, "\n data (float) : %f", f_data);
+ fprintf(stdout, "\n data (double) : %f", d_data);
+
+ fprintf(stdout, "\n data (str) : %s(%lu)", szData, szLength);
+
+ bData[bLength]= '\0'; /* bData is binary */
+ fprintf(stdout, "\n data (bin) : %s(%lu)", bData, bLength);
+ }
+
+ DIE_UNLESS(t_data == 19);
+ DIE_UNLESS(s_data == 2999);
+ DIE_UNLESS(i_data == 3999);
+ DIE_UNLESS(b_data == 4999999);
+ /*DIE_UNLESS(f_data == 2345.60);*/
+ /*DIE_UNLESS(d_data == 5678.89563);*/
+ DIE_UNLESS(strcmp(szData, "venu") == 0);
+ DIE_UNLESS(strncmp(bData, "mysql", 5) == 0);
+ DIE_UNLESS(szLength == 4);
+ DIE_UNLESS(bLength == 5);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test ext bind result */
+
+static void test_bind_result_ext1()
+{
+ MYSQL_STMT *stmt;
+ uint i;
+ int rc;
+ char t_data[20];
+ float s_data;
+ short i_data;
+ uchar b_data;
+ int f_data;
+ long bData;
+ char d_data[20];
+ double szData;
+ MYSQL_BIND my_bind[8];
+ ulong length[8];
+ my_bool is_null[8];
+ myheader("test_bind_result_ext1");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_result(c1 tinyint, c2 smallint, \
+ c3 int, c4 bigint, \
+ c5 float, c6 double, \
+ c7 varbinary(10), \
+ c8 varchar(10))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_bind_result VALUES(120, 2999, 3999, 54, \
+ 2.6, 58.89, \
+ '206', '6.7')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *) t_data;
+ my_bind[0].buffer_length= sizeof(t_data);
+ my_bind[0].error= &my_bind[0].error_value;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_FLOAT;
+ my_bind[1].buffer= (void *)&s_data;
+ my_bind[1].buffer_length= 0;
+ my_bind[1].error= &my_bind[1].error_value;
+
+ my_bind[2].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[2].buffer= (void *)&i_data;
+ my_bind[2].buffer_length= 0;
+ my_bind[2].error= &my_bind[2].error_value;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[3].buffer= (void *)&b_data;
+ my_bind[3].buffer_length= 0;
+ my_bind[3].error= &my_bind[3].error_value;
+
+ my_bind[4].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[4].buffer= (void *)&f_data;
+ my_bind[4].buffer_length= 0;
+ my_bind[4].error= &my_bind[4].error_value;
+
+ my_bind[5].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[5].buffer= (void *)d_data;
+ my_bind[5].buffer_length= sizeof(d_data);
+ my_bind[5].error= &my_bind[5].error_value;
+
+ my_bind[6].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[6].buffer= (void *)&bData;
+ my_bind[6].buffer_length= 0;
+ my_bind[6].error= &my_bind[6].error_value;
+
+ my_bind[7].buffer_type= MYSQL_TYPE_DOUBLE;
+ my_bind[7].buffer= (void *)&szData;
+ my_bind[7].buffer_length= 0;
+ my_bind[7].error= &my_bind[7].error_value;
+
+ for (i= 0; i < array_elements(my_bind); i++)
+ {
+ my_bind[i].is_null= &is_null[i];
+ my_bind[i].length= &length[i];
+ }
+
+ stmt= mysql_simple_prepare(mysql, "select * from test_bind_result");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ printf("rc=%d\n", rc);
+ DIE_UNLESS(rc == 0);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n data (tiny) : %s(%lu)", t_data, length[0]);
+ fprintf(stdout, "\n data (short) : %f(%lu)", s_data, length[1]);
+ fprintf(stdout, "\n data (int) : %d(%lu)", i_data, length[2]);
+ fprintf(stdout, "\n data (big) : %d(%lu)", b_data, length[3]);
+
+ fprintf(stdout, "\n data (float) : %d(%lu)", f_data, length[4]);
+ fprintf(stdout, "\n data (double) : %s(%lu)", d_data, length[5]);
+
+ fprintf(stdout, "\n data (bin) : %ld(%lu)", bData, length[6]);
+ fprintf(stdout, "\n data (str) : %g(%lu)", szData, length[7]);
+ }
+
+ DIE_UNLESS(strcmp(t_data, "120") == 0);
+ DIE_UNLESS(i_data == 3999);
+ DIE_UNLESS(f_data == 2);
+ DIE_UNLESS(strcmp(d_data, "58.89") == 0);
+ DIE_UNLESS(b_data == 54);
+
+ DIE_UNLESS(length[0] == 3);
+ DIE_UNLESS(length[1] == 4);
+ DIE_UNLESS(length[2] == 2);
+ DIE_UNLESS(length[3] == 1);
+ DIE_UNLESS(length[4] == 4);
+ DIE_UNLESS(length[5] == 5);
+ DIE_UNLESS(length[6] == 4);
+ DIE_UNLESS(length[7] == 8);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Generalized fetch conversion routine for all basic types */
+
+static void bind_fetch(int row_count)
+{
+ MYSQL_STMT *stmt;
+ int rc, i, count= row_count;
+ int32 data[10];
+ int8 i8_data;
+ int16 i16_data;
+ int32 i32_data;
+ longlong i64_data;
+ float f_data;
+ double d_data;
+ char s_data[10];
+ ulong length[10];
+ MYSQL_BIND my_bind[7];
+ my_bool is_null[7];
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO test_bind_fetch VALUES "
+ "(?, ?, ?, ?, ?, ?, ?)");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 7);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[i].buffer= (void *) &data[i];
+ }
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ while (count--)
+ {
+ rc= 10+count;
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ data[i]= rc+i;
+ rc+= 12;
+ }
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= my_stmt_result("SELECT * FROM test_bind_fetch");
+ DIE_UNLESS(row_count == rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_bind_fetch");
+ check_stmt(stmt);
+
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].buffer= (void *) &data[i];
+ my_bind[i].length= &length[i];
+ my_bind[i].is_null= &is_null[i];
+ }
+
+ my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[0].buffer= (void *)&i8_data;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[1].buffer= (void *)&i16_data;
+
+ my_bind[2].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[2].buffer= (void *)&i32_data;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[3].buffer= (void *)&i64_data;
+
+ my_bind[4].buffer_type= MYSQL_TYPE_FLOAT;
+ my_bind[4].buffer= (void *)&f_data;
+
+ my_bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
+ my_bind[5].buffer= (void *)&d_data;
+
+ my_bind[6].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[6].buffer= (void *)&s_data;
+ my_bind[6].buffer_length= sizeof(s_data);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ while (row_count--)
+ {
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n");
+ fprintf(stdout, "\n tiny : %ld(%lu)", (ulong) i8_data, length[0]);
+ fprintf(stdout, "\n short : %ld(%lu)", (ulong) i16_data, length[1]);
+ fprintf(stdout, "\n int : %ld(%lu)", (ulong) i32_data, length[2]);
+ fprintf(stdout, "\n longlong : %ld(%lu)", (ulong) i64_data, length[3]);
+ fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]);
+ fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]);
+ fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]);
+ }
+ rc= 10+row_count;
+
+ /* TINY */
+ DIE_UNLESS((int) i8_data == rc);
+ DIE_UNLESS(length[0] == 1);
+ rc+= 13;
+
+ /* SHORT */
+ DIE_UNLESS((int) i16_data == rc);
+ DIE_UNLESS(length[1] == 2);
+ rc+= 13;
+
+ /* LONG */
+ DIE_UNLESS((int) i32_data == rc);
+ DIE_UNLESS(length[2] == 4);
+ rc+= 13;
+
+ /* LONGLONG */
+ DIE_UNLESS((int) i64_data == rc);
+ DIE_UNLESS(length[3] == 8);
+ rc+= 13;
+
+ /* FLOAT */
+ DIE_UNLESS((int)f_data == rc);
+ DIE_UNLESS(length[4] == 4);
+ rc+= 13;
+
+ /* DOUBLE */
+ DIE_UNLESS((int)d_data == rc);
+ DIE_UNLESS(length[5] == 8);
+ rc+= 13;
+
+ /* CHAR */
+ {
+ char buff[20];
+ long len= sprintf(buff, "%d", rc);
+ DIE_UNLESS(strcmp(s_data, buff) == 0);
+ DIE_UNLESS(length[6] == (ulong) len);
+ }
+ }
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test fetching of date, time and ts */
+
+static void test_fetch_date()
+{
+ MYSQL_STMT *stmt;
+ uint i;
+ int rc, year;
+ char date[25], my_time[25], ts[25], ts_4[25], ts_6[20], dt[20];
+ ulong d_length, t_length, ts_length, ts4_length, ts6_length,
+ dt_length, y_length;
+ MYSQL_BIND my_bind[8];
+ my_bool is_null[8];
+ ulong length[8];
+
+ myheader("test_fetch_date");
+
+ /* Will not work if sql_mode is NO_ZERO_DATE (implicit if TRADITIONAL) */
+ rc= mysql_query(mysql, "SET SQL_MODE=''");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_result(c1 date, c2 time, \
+ c3 timestamp, \
+ c4 year, \
+ c5 datetime, \
+ c6 timestamp, \
+ c7 timestamp)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SET SQL_MODE=''");
+ rc= mysql_query(mysql, "INSERT INTO test_bind_result VALUES('2002-01-02', \
+ '12:49:00', \
+ '2002-01-02 17:46:59', \
+ 2010, \
+ '2010-07-10', \
+ '2020', '1999-12-29')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < array_elements(my_bind); i++)
+ {
+ my_bind[i].is_null= &is_null[i];
+ my_bind[i].length= &length[i];
+ }
+
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1]= my_bind[2]= my_bind[0];
+
+ my_bind[0].buffer= (void *)&date;
+ my_bind[0].buffer_length= sizeof(date);
+ my_bind[0].length= &d_length;
+
+ my_bind[1].buffer= (void *)&my_time;
+ my_bind[1].buffer_length= sizeof(my_time);
+ my_bind[1].length= &t_length;
+
+ my_bind[2].buffer= (void *)&ts;
+ my_bind[2].buffer_length= sizeof(ts);
+ my_bind[2].length= &ts_length;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[3].buffer= (void *)&year;
+ my_bind[3].length= &y_length;
+
+ my_bind[4].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[4].buffer= (void *)&dt;
+ my_bind[4].buffer_length= sizeof(dt);
+ my_bind[4].length= &dt_length;
+
+ my_bind[5].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[5].buffer= (void *)&ts_4;
+ my_bind[5].buffer_length= sizeof(ts_4);
+ my_bind[5].length= &ts4_length;
+
+ my_bind[6].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[6].buffer= (void *)&ts_6;
+ my_bind[6].buffer_length= sizeof(ts_6);
+ my_bind[6].length= &ts6_length;
+
+ rc= my_stmt_result("SELECT * FROM test_bind_result");
+ DIE_UNLESS(rc == 1);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_bind_result");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ ts_4[0]= '\0';
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n date : %s(%lu)", date, d_length);
+ fprintf(stdout, "\n time : %s(%lu)", my_time, t_length);
+ fprintf(stdout, "\n ts : %s(%lu)", ts, ts_length);
+ fprintf(stdout, "\n year : %d(%lu)", year, y_length);
+ fprintf(stdout, "\n dt : %s(%lu)", dt, dt_length);
+ fprintf(stdout, "\n ts(4) : %s(%lu)", ts_4, ts4_length);
+ fprintf(stdout, "\n ts(6) : %s(%lu)", ts_6, ts6_length);
+ }
+
+ DIE_UNLESS(strcmp(date, "2002-01-02") == 0);
+ DIE_UNLESS(d_length == 10);
+
+ DIE_UNLESS(strcmp(my_time, "12:49:00") == 0);
+ DIE_UNLESS(t_length == 8);
+
+ DIE_UNLESS(strcmp(ts, "2002-01-02 17:46:59") == 0);
+ DIE_UNLESS(ts_length == 19);
+
+ DIE_UNLESS(year == 2010);
+ DIE_UNLESS(y_length == 4);
+
+ DIE_UNLESS(strcmp(dt, "2010-07-10 00:00:00") == 0);
+ DIE_UNLESS(dt_length == 19);
+
+ DIE_UNLESS(strcmp(ts_4, "0000-00-00 00:00:00") == 0);
+ DIE_UNLESS(ts4_length == strlen("0000-00-00 00:00:00"));
+
+ DIE_UNLESS(strcmp(ts_6, "1999-12-29 00:00:00") == 0);
+ DIE_UNLESS(ts6_length == 19);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test fetching of str to all types */
+
+static void test_fetch_str()
+{
+ int rc;
+
+ myheader("test_fetch_str");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_fetch(c1 char(10), \
+ c2 char(10), \
+ c3 char(20), \
+ c4 char(20), \
+ c5 char(30), \
+ c6 char(40), \
+ c7 char(20))");
+ myquery(rc);
+
+ bind_fetch(3);
+}
+
+
+/* Test fetching of long to all types */
+
+static void test_fetch_long()
+{
+ int rc;
+
+ myheader("test_fetch_long");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_fetch(c1 int unsigned, \
+ c2 int unsigned, \
+ c3 int, \
+ c4 int, \
+ c5 int, \
+ c6 int unsigned, \
+ c7 int)");
+ myquery(rc);
+
+ bind_fetch(4);
+}
+
+
+/* Test fetching of short to all types */
+
+static void test_fetch_short()
+{
+ int rc;
+
+ myheader("test_fetch_short");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_fetch(c1 smallint unsigned, \
+ c2 smallint, \
+ c3 smallint unsigned, \
+ c4 smallint, \
+ c5 smallint, \
+ c6 smallint, \
+ c7 smallint unsigned)");
+ myquery(rc);
+
+ bind_fetch(5);
+}
+
+
+/* Test fetching of tiny to all types */
+
+static void test_fetch_tiny()
+{
+ int rc;
+
+ myheader("test_fetch_tiny");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_fetch(c1 tinyint unsigned, \
+ c2 tinyint, \
+ c3 tinyint unsigned, \
+ c4 tinyint, \
+ c5 tinyint, \
+ c6 tinyint, \
+ c7 tinyint unsigned)");
+ myquery(rc);
+
+ bind_fetch(3);
+
+}
+
+
+/* Test fetching of longlong to all types */
+
+static void test_fetch_bigint()
+{
+ int rc;
+
+ myheader("test_fetch_bigint");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_fetch(c1 bigint, \
+ c2 bigint, \
+ c3 bigint unsigned, \
+ c4 bigint unsigned, \
+ c5 bigint unsigned, \
+ c6 bigint unsigned, \
+ c7 bigint unsigned)");
+ myquery(rc);
+
+ bind_fetch(2);
+
+}
+
+
+/* Test fetching of float to all types */
+
+static void test_fetch_float()
+{
+ int rc;
+
+ myheader("test_fetch_float");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_fetch(c1 float(3), \
+ c2 float, \
+ c3 float unsigned, \
+ c4 float, \
+ c5 float, \
+ c6 float, \
+ c7 float(10) unsigned)");
+ myquery(rc);
+
+ bind_fetch(2);
+
+}
+
+
+/* Test fetching of double to all types */
+
+static void test_fetch_double()
+{
+ int rc;
+
+ myheader("test_fetch_double");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bind_fetch(c1 double(5, 2), "
+ "c2 double unsigned, c3 double unsigned, "
+ "c4 double unsigned, c5 double unsigned, "
+ "c6 double unsigned, c7 double unsigned)");
+ myquery(rc);
+
+ bind_fetch(3);
+
+}
+
+
+/* Test simple prepare with all possible types */
+
+static void test_prepare_ext()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char *sql;
+ int nData= 1;
+ char tData= 1;
+ short sData= 10;
+ longlong bData= 20;
+ MYSQL_BIND my_bind[6];
+ char query[MAX_TEST_QUERY_LENGTH];
+ myheader("test_prepare_ext");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_ext");
+ myquery(rc);
+
+ sql= (char *)"CREATE TABLE test_prepare_ext"
+ "("
+ " c1 tinyint,"
+ " c2 smallint,"
+ " c3 mediumint,"
+ " c4 int,"
+ " c5 integer,"
+ " c6 bigint,"
+ " c7 float,"
+ " c8 double,"
+ " c9 double precision,"
+ " c10 real,"
+ " c11 decimal(7, 4),"
+ " c12 numeric(8, 4),"
+ " c13 date,"
+ " c14 datetime,"
+ " c15 timestamp,"
+ " c16 time,"
+ " c17 year,"
+ " c18 bit,"
+ " c19 bool,"
+ " c20 char,"
+ " c21 char(10),"
+ " c22 varchar(30),"
+ " c23 tinyblob,"
+ " c24 tinytext,"
+ " c25 blob,"
+ " c26 text,"
+ " c27 mediumblob,"
+ " c28 mediumtext,"
+ " c29 longblob,"
+ " c30 longtext,"
+ " c31 enum('one', 'two', 'three'),"
+ " c32 set('monday', 'tuesday', 'wednesday'))";
+
+ rc= mysql_query(mysql, sql);
+ myquery(rc);
+
+ /* insert by prepare - all integers */
+ strmov(query, (char *)"INSERT INTO test_prepare_ext(c1, c2, c3, c4, c5, c6) VALUES(?, ?, ?, ?, ?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 6);
+
+ /* Always bzero all members of bind parameter */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /*tinyint*/
+ my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[0].buffer= (void *)&tData;
+
+ /*smallint*/
+ my_bind[1].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[1].buffer= (void *)&sData;
+
+ /*mediumint*/
+ my_bind[2].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[2].buffer= (void *)&nData;
+
+ /*int*/
+ my_bind[3].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[3].buffer= (void *)&nData;
+
+ /*integer*/
+ my_bind[4].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[4].buffer= (void *)&nData;
+
+ /*bigint*/
+ my_bind[5].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[5].buffer= (void *)&bData;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /*
+ * integer to integer
+ */
+ for (nData= 0; nData<10; nData++, tData++, sData++, bData++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+
+ stmt= mysql_simple_prepare(mysql, "SELECT c1, c2, c3, c4, c5, c6 "
+ "FROM test_prepare_ext");
+ check_stmt(stmt);
+
+ /* get the result */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(nData == rc);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test real and alias names */
+
+static void test_field_names()
+{
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_field_names");
+
+ if (!opt_silent)
+ fprintf(stdout, "\n %d, %d, %d", MYSQL_TYPE_DECIMAL, MYSQL_TYPE_NEWDATE, MYSQL_TYPE_ENUM);
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_names1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_names2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_field_names1(id int, name varchar(50))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_field_names2(id int, name varchar(50))");
+ myquery(rc);
+
+ /* with table name included with TRUE column name */
+ rc= mysql_query(mysql, "SELECT id as 'id-alias' FROM test_field_names1");
+ myquery(rc);
+
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+ mysql_free_result(result);
+
+ /* with table name included with TRUE column name */
+ rc= mysql_query(mysql, "SELECT t1.id as 'id-alias', test_field_names2.name FROM test_field_names1 t1, test_field_names2");
+ myquery(rc);
+
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+ mysql_free_result(result);
+}
+
+
+/* Test warnings */
+
+static void test_warnings()
+{
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_warnings");
+
+ mysql_query(mysql, "DROP TABLE if exists test_non_exists");
+
+ rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists");
+ myquery(rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total warnings: %d", mysql_warning_count(mysql));
+ rc= mysql_query(mysql, "SHOW WARNINGS");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+}
+
+
+/* Test errors */
+
+static void test_errors()
+{
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_errors");
+
+ mysql_query(mysql, "DROP TABLE if exists test_non_exists");
+
+ rc= mysql_query(mysql, "DROP TABLE test_non_exists");
+ myquery_r(rc);
+
+ rc= mysql_query(mysql, "SHOW ERRORS");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+}
+
+
+/* Test simple prepare-insert */
+
+static void test_insert()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char str_data[50];
+ char tiny_data;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[2];
+ ulong length;
+
+ myheader("test_insert");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prep_insert");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_prep_insert(col1 tinyint, \
+ col2 varchar(50))");
+ myquery(rc);
+
+ /* insert by prepare */
+ stmt= mysql_simple_prepare(mysql,
+ "INSERT INTO test_prep_insert VALUES(?, ?)");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 2);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /* tinyint */
+ my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[0].buffer= (void *)&tiny_data;
+
+ /* string */
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= str_data;
+ my_bind[1].buffer_length= sizeof(str_data);;
+ my_bind[1].length= &length;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /* now, execute the prepared statement to insert 10 records.. */
+ for (tiny_data= 0; tiny_data < 3; tiny_data++)
+ {
+ length= sprintf(str_data, "MySQL%d", tiny_data);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+
+ mysql_stmt_close(stmt);
+
+ /* now fetch the results ..*/
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* test the results now, only one row should exist */
+ rc= mysql_query(mysql, "SELECT * FROM test_prep_insert");
+ myquery(rc);
+
+ /* get the result */
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS((int) tiny_data == rc);
+ mysql_free_result(result);
+
+}
+
+
+/* Test simple prepare-resultset info */
+
+static void test_prepare_resultset()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_prepare_resultset");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_resultset");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_prepare_resultset(id int, \
+ name varchar(50), extra double)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_prepare_resultset");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 0);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+ my_print_result_metadata(result);
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test field flags (verify .NET provider) */
+
+static void test_field_flags()
+{
+ int rc;
+ MYSQL_RES *result;
+ MYSQL_FIELD *field;
+ unsigned int i;
+
+
+ myheader("test_field_flags");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_flags");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_field_flags(id int NOT NULL AUTO_INCREMENT PRIMARY KEY, \
+ id1 int NOT NULL, \
+ id2 int UNIQUE, \
+ id3 int, \
+ id4 int NOT NULL, \
+ id5 int, \
+ KEY(id3, id4))");
+ myquery(rc);
+
+ /* with table name included with TRUE column name */
+ rc= mysql_query(mysql, "SELECT * FROM test_field_flags");
+ myquery(rc);
+
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ mysql_field_seek(result, 0);
+ if (!opt_silent)
+ fputc('\n', stdout);
+
+ for(i= 0; i< mysql_num_fields(result); i++)
+ {
+ field= mysql_fetch_field(result);
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n field:%d", i);
+ if (field->flags & NOT_NULL_FLAG)
+ fprintf(stdout, "\n NOT_NULL_FLAG");
+ if (field->flags & PRI_KEY_FLAG)
+ fprintf(stdout, "\n PRI_KEY_FLAG");
+ if (field->flags & UNIQUE_KEY_FLAG)
+ fprintf(stdout, "\n UNIQUE_KEY_FLAG");
+ if (field->flags & MULTIPLE_KEY_FLAG)
+ fprintf(stdout, "\n MULTIPLE_KEY_FLAG");
+ if (field->flags & AUTO_INCREMENT_FLAG)
+ fprintf(stdout, "\n AUTO_INCREMENT_FLAG");
+
+ }
+ }
+ mysql_free_result(result);
+}
+
+
+/* Test mysql_stmt_close for open stmts */
+
+static void test_stmt_close()
+{
+ MYSQL *lmysql;
+ MYSQL_STMT *stmt1, *stmt2, *stmt3, *stmt_x;
+ MYSQL_BIND my_bind[1];
+ MYSQL_RES *result;
+ unsigned int count;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_stmt_close");
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a test connection ...");
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+ if (!(mysql_real_connect(lmysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ exit(1);
+ }
+ mysql_options(lmysql, MYSQL_OPT_RECONNECT, &my_true);
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+
+
+ /* set AUTOCOMMIT to ON*/
+ mysql_autocommit(lmysql, TRUE);
+
+ rc= mysql_query(lmysql, "SET SQL_MODE = ''");
+ myquery(rc);
+
+ rc= mysql_query(lmysql, "DROP TABLE IF EXISTS test_stmt_close");
+ myquery(rc);
+
+ rc= mysql_query(lmysql, "CREATE TABLE test_stmt_close(id int)");
+ myquery(rc);
+
+ strmov(query, "DO \"nothing\"");
+ stmt1= mysql_simple_prepare(lmysql, query);
+ check_stmt(stmt1);
+
+ verify_param_count(stmt1, 0);
+
+ strmov(query, "INSERT INTO test_stmt_close(id) VALUES(?)");
+ stmt_x= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_x);
+
+ verify_param_count(stmt_x, 1);
+
+ strmov(query, "UPDATE test_stmt_close SET id= ? WHERE id= ?");
+ stmt3= mysql_simple_prepare(lmysql, query);
+ check_stmt(stmt3);
+
+ verify_param_count(stmt3, 2);
+
+ strmov(query, "SELECT * FROM test_stmt_close WHERE id= ?");
+ stmt2= mysql_simple_prepare(lmysql, query);
+ check_stmt(stmt2);
+
+ verify_param_count(stmt2, 1);
+
+ rc= mysql_stmt_close(stmt1);
+ if (!opt_silent)
+ fprintf(stdout, "\n mysql_close_stmt(1) returned: %d", rc);
+ DIE_UNLESS(rc == 0);
+
+ /*
+ Originally we were going to close all statements automatically in
+ mysql_close(). This proved to not work well - users weren't able to
+ close statements by hand once mysql_close() had been called.
+ Now mysql_close() doesn't free any statements, so this test doesn't
+ serve its original designation any more.
+ Here we free stmt2 and stmt3 by hand to avoid memory leaks.
+ */
+ mysql_stmt_close(stmt2);
+ mysql_stmt_close(stmt3);
+ mysql_close(lmysql);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= (void *)&count;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ count= 100;
+
+ rc= mysql_stmt_bind_param(stmt_x, my_bind);
+ check_execute(stmt_x, rc);
+
+ rc= mysql_stmt_execute(stmt_x);
+ check_execute(stmt_x, rc);
+
+ verify_st_affected_rows(stmt_x, 1);
+
+ rc= mysql_stmt_close(stmt_x);
+ if (!opt_silent)
+ fprintf(stdout, "\n mysql_close_stmt(x) returned: %d", rc);
+ DIE_UNLESS( rc == 0);
+
+ rc= mysql_query(mysql, "SELECT id FROM test_stmt_close");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+}
+
+
+/* Test simple set variable prepare */
+
+static void test_set_variable()
+{
+ MYSQL_STMT *stmt, *stmt1;
+ int rc;
+ int set_count, def_count, get_count;
+ ulong length;
+ char var[NAME_LEN+1];
+ MYSQL_BIND set_bind[1], get_bind[2];
+
+ myheader("test_set_variable");
+
+ mysql_autocommit(mysql, TRUE);
+
+ stmt1= mysql_simple_prepare(mysql, "show variables like 'max_error_count'");
+ check_stmt(stmt1);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) get_bind, sizeof(get_bind));
+
+ get_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ get_bind[0].buffer= (void *)var;
+ get_bind[0].length= &length;
+ get_bind[0].buffer_length= (int)NAME_LEN;
+ length= NAME_LEN;
+
+ get_bind[1].buffer_type= MYSQL_TYPE_LONG;
+ get_bind[1].buffer= (void *)&get_count;
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_bind_result(stmt1, get_bind);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n max_error_count(default): %d", get_count);
+ def_count= get_count;
+
+ DIE_UNLESS(strcmp(var, "max_error_count") == 0);
+ rc= mysql_stmt_fetch(stmt1);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ stmt= mysql_simple_prepare(mysql, "set max_error_count= ?");
+ check_stmt(stmt);
+
+ bzero((char*) set_bind, sizeof(set_bind));
+
+ set_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ set_bind[0].buffer= (void *)&set_count;
+
+ rc= mysql_stmt_bind_param(stmt, set_bind);
+ check_execute(stmt, rc);
+
+ set_count= 31;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_commit(mysql);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n max_error_count : %d", get_count);
+ DIE_UNLESS(get_count == set_count);
+
+ rc= mysql_stmt_fetch(stmt1);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ /* restore back to default */
+ set_count= def_count;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n max_error_count(default): %d", get_count);
+ DIE_UNLESS(get_count == set_count);
+
+ rc= mysql_stmt_fetch(stmt1);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+ mysql_stmt_close(stmt1);
+}
+
+/* Test FUNCTION field info / DATE_FORMAT() table_name . */
+
+static void test_func_fields()
+{
+ int rc;
+ MYSQL_RES *result;
+ MYSQL_FIELD *field;
+
+ myheader("test_func_fields");
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_dateformat");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_dateformat(id int, \
+ ts timestamp)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_dateformat(id) values(10)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SELECT ts FROM test_dateformat");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ field= mysql_fetch_field(result);
+ mytest(field);
+ if (!opt_silent)
+ fprintf(stdout, "\n table name: `%s` (expected: `%s`)", field->table,
+ "test_dateformat");
+ DIE_UNLESS(strcmp(field->table, "test_dateformat") == 0);
+
+ field= mysql_fetch_field(result);
+ mytest_r(field); /* no more fields */
+
+ mysql_free_result(result);
+
+ /* DATE_FORMAT */
+ rc= mysql_query(mysql, "SELECT DATE_FORMAT(ts, '%Y') AS 'venu' FROM test_dateformat");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ field= mysql_fetch_field(result);
+ mytest(field);
+ if (!opt_silent)
+ fprintf(stdout, "\n table name: `%s` (expected: `%s`)", field->table, "");
+ DIE_UNLESS(field->table[0] == '\0');
+
+ field= mysql_fetch_field(result);
+ mytest_r(field); /* no more fields */
+
+ mysql_free_result(result);
+
+ /* FIELD ALIAS TEST */
+ rc= mysql_query(mysql, "SELECT DATE_FORMAT(ts, '%Y') AS 'YEAR' FROM test_dateformat");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ field= mysql_fetch_field(result);
+ mytest(field);
+ if (!opt_silent)
+ {
+ printf("\n field name: `%s` (expected: `%s`)", field->name, "YEAR");
+ printf("\n field org name: `%s` (expected: `%s`)", field->org_name, "");
+ }
+ DIE_UNLESS(strcmp(field->name, "YEAR") == 0);
+ DIE_UNLESS(field->org_name[0] == '\0');
+
+ field= mysql_fetch_field(result);
+ mytest_r(field); /* no more fields */
+
+ mysql_free_result(result);
+}
+
+
+/* Multiple stmts .. */
+
+static void test_multi_stmt()
+{
+
+ MYSQL_STMT *stmt, *stmt1, *stmt2;
+ int rc;
+ uint32 id;
+ char name[50];
+ MYSQL_BIND my_bind[2];
+ ulong length[2];
+ my_bool is_null[2];
+ myheader("test_multi_stmt");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_multi_table");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_multi_table(id int, name char(20))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_multi_table values(10, 'mysql')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_multi_table "
+ "WHERE id= ?");
+ check_stmt(stmt);
+
+ stmt2= mysql_simple_prepare(mysql, "UPDATE test_multi_table "
+ "SET name='updated' WHERE id=10");
+ check_stmt(stmt2);
+
+ verify_param_count(stmt, 1);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&id;
+ my_bind[0].is_null= &is_null[0];
+ my_bind[0].length= &length[0];
+ is_null[0]= 0;
+ length[0]= 0;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)name;
+ my_bind[1].buffer_length= sizeof(name);
+ my_bind[1].length= &length[1];
+ my_bind[1].is_null= &is_null[1];
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ id= 10;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ id= 999;
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n int_data: %lu(%lu)", (ulong) id, length[0]);
+ fprintf(stdout, "\n str_data: %s(%lu)", name, length[1]);
+ }
+ DIE_UNLESS(id == 10);
+ DIE_UNLESS(strcmp(name, "mysql") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ /* alter the table schema now */
+ stmt1= mysql_simple_prepare(mysql, "DELETE FROM test_multi_table "
+ "WHERE id= ? AND "
+ "CONVERT(name USING utf8)=?");
+ check_stmt(stmt1);
+
+ verify_param_count(stmt1, 2);
+
+ rc= mysql_stmt_bind_param(stmt1, my_bind);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_execute(stmt2);
+ check_execute(stmt2, rc);
+
+ verify_st_affected_rows(stmt2, 1);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n int_data: %lu(%lu)", (ulong) id, length[0]);
+ fprintf(stdout, "\n str_data: %s(%lu)", name, length[1]);
+ }
+ DIE_UNLESS(id == 10);
+ DIE_UNLESS(strcmp(name, "updated") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ verify_st_affected_rows(stmt1, 1);
+
+ mysql_stmt_close(stmt1);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= my_stmt_result("SELECT * FROM test_multi_table");
+ DIE_UNLESS(rc == 0);
+
+ mysql_stmt_close(stmt);
+ mysql_stmt_close(stmt2);
+
+}
+
+
+/* Test simple sample - manual */
+
+static void test_manual_sample()
+{
+ unsigned int param_count;
+ MYSQL_STMT *stmt;
+ short small_data= 1;
+ int int_data= 2;
+ int rc;
+ char str_data[50]= "std_data";
+ ulonglong affected_rows;
+ MYSQL_BIND my_bind[3];
+ my_bool is_null;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_manual_sample");
+
+ /*
+ Sample which is incorporated directly in the manual under Prepared
+ statements section (Example from mysql_stmt_execute()
+ */
+
+ mysql_autocommit(mysql, 1);
+ if (mysql_query(mysql, "DROP TABLE IF EXISTS test_table"))
+ {
+ fprintf(stderr, "\n drop table failed");
+ fprintf(stderr, "\n %s", mysql_error(mysql));
+ exit(1);
+ }
+ if (mysql_query(mysql, "CREATE TABLE test_table(col1 int, col2 varchar(50), \
+ col3 smallint, \
+ col4 timestamp)"))
+ {
+ fprintf(stderr, "\n create table failed");
+ fprintf(stderr, "\n %s", mysql_error(mysql));
+ exit(1);
+ }
+
+ /* Prepare a insert query with 3 parameters */
+ strmov(query, "INSERT INTO test_table(col1, col2, col3) values(?, ?, ?)");
+ if (!(stmt= mysql_simple_prepare(mysql, query)))
+ {
+ fprintf(stderr, "\n prepare, insert failed");
+ fprintf(stderr, "\n %s", mysql_error(mysql));
+ exit(1);
+ }
+ if (!opt_silent)
+ fprintf(stdout, "\n prepare, insert successful");
+
+ /* Get the parameter count from the statement */
+ param_count= mysql_stmt_param_count(stmt);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total parameters in insert: %d", param_count);
+ if (param_count != 3) /* validate parameter count */
+ {
+ fprintf(stderr, "\n invalid parameter count returned by MySQL");
+ exit(1);
+ }
+
+ /* Bind the data for the parameters */
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /* INTEGER PART */
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&int_data;
+
+ /* STRING PART */
+ my_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+ my_bind[1].buffer= (void *)str_data;
+ my_bind[1].buffer_length= sizeof(str_data);
+
+ /* SMALLINT PART */
+ my_bind[2].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[2].buffer= (void *)&small_data;
+ my_bind[2].is_null= &is_null;
+ is_null= 0;
+
+ /* Bind the buffers */
+ if (mysql_stmt_bind_param(stmt, my_bind))
+ {
+ fprintf(stderr, "\n param bind failed");
+ fprintf(stderr, "\n %s", mysql_stmt_error(stmt));
+ exit(1);
+ }
+
+ /* Specify the data */
+ int_data= 10; /* integer */
+ strmov(str_data, "MySQL"); /* string */
+
+ /* INSERT SMALLINT data as NULL */
+ is_null= 1;
+
+ /* Execute the insert statement - 1*/
+ if (mysql_stmt_execute(stmt))
+ {
+ fprintf(stderr, "\n execute 1 failed");
+ fprintf(stderr, "\n %s", mysql_stmt_error(stmt));
+ exit(1);
+ }
+
+ /* Get the total rows affected */
+ affected_rows= mysql_stmt_affected_rows(stmt);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total affected rows: %ld", (ulong) affected_rows);
+ if (affected_rows != 1) /* validate affected rows */
+ {
+ fprintf(stderr, "\n invalid affected rows by MySQL");
+ exit(1);
+ }
+
+ /* Re-execute the insert, by changing the values */
+ int_data= 1000;
+ strmov(str_data, "The most popular open source database");
+ small_data= 1000; /* smallint */
+ is_null= 0; /* reset */
+
+ /* Execute the insert statement - 2*/
+ if (mysql_stmt_execute(stmt))
+ {
+ fprintf(stderr, "\n execute 2 failed");
+ fprintf(stderr, "\n %s", mysql_stmt_error(stmt));
+ exit(1);
+ }
+
+ /* Get the total rows affected */
+ affected_rows= mysql_stmt_affected_rows(stmt);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total affected rows: %ld", (ulong) affected_rows);
+ if (affected_rows != 1) /* validate affected rows */
+ {
+ fprintf(stderr, "\n invalid affected rows by MySQL");
+ exit(1);
+ }
+
+ /* Close the statement */
+ if (mysql_stmt_close(stmt))
+ {
+ fprintf(stderr, "\n failed while closing the statement");
+ fprintf(stderr, "\n %s", mysql_stmt_error(stmt));
+ exit(1);
+ }
+ rc= my_stmt_result("SELECT * FROM test_table");
+ DIE_UNLESS(rc == 2);
+
+ /* DROP THE TABLE */
+ if (mysql_query(mysql, "DROP TABLE test_table"))
+ {
+ fprintf(stderr, "\n drop table failed");
+ fprintf(stderr, "\n %s", mysql_error(mysql));
+ exit(1);
+ }
+ if (!opt_silent)
+ fprintf(stdout, "Success !!!");
+}
+
+
+/* Test alter table scenario in the middle of prepare */
+
+static void test_prepare_alter()
+{
+ MYSQL_STMT *stmt;
+ int rc, id;
+ MYSQL_BIND my_bind[1];
+ my_bool is_null;
+
+ myheader("test_prepare_alter");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prep_alter");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_prep_alter(id int, name char(20))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_prep_alter values(10, 'venu'), (20, 'mysql')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO test_prep_alter VALUES(?, 'monty')");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 1);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ is_null= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[0].buffer= (void *)&id;
+ my_bind[0].is_null= &is_null;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ id= 30;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ if (thread_query("ALTER TABLE test_prep_alter change id id_new varchar(20)"))
+ exit(1);
+
+ is_null= 1;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_stmt_result("SELECT * FROM test_prep_alter");
+ DIE_UNLESS(rc == 4);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test the support of multi-statement executions */
+
+static void test_multi_statements()
+{
+ MYSQL *mysql_local;
+ MYSQL_RES *result;
+ int rc;
+
+ const char *query= "\
+DROP TABLE IF EXISTS test_multi_tab;\
+CREATE TABLE test_multi_tab(id int, name char(20));\
+INSERT INTO test_multi_tab(id) VALUES(10), (20);\
+INSERT INTO test_multi_tab VALUES(20, 'insert;comma');\
+SELECT * FROM test_multi_tab;\
+UPDATE test_multi_tab SET name='new;name' WHERE id=20;\
+DELETE FROM test_multi_tab WHERE name='new;name';\
+SELECT * FROM test_multi_tab;\
+DELETE FROM test_multi_tab WHERE id=10;\
+SELECT * FROM test_multi_tab;\
+DROP TABLE test_multi_tab;\
+select 1;\
+DROP TABLE IF EXISTS test_multi_tab";
+ uint count, exp_value;
+ uint rows[]= {0, 0, 2, 1, 3, 2, 2, 1, 1, 0, 0, 1, 0};
+
+ myheader("test_multi_statements");
+
+ /*
+ First test that we get an error for multi statements
+ (Because default connection is not opened with CLIENT_MULTI_STATEMENTS)
+ */
+ rc= mysql_query(mysql, query); /* syntax error */
+ myquery_r(rc);
+
+ rc= mysql_next_result(mysql);
+ DIE_UNLESS(rc == -1);
+ rc= mysql_more_results(mysql);
+ DIE_UNLESS(rc == 0);
+
+ if (!(mysql_local= mysql_client_init(NULL)))
+ {
+ fprintf(stdout, "\n mysql_client_init() failed");
+ exit(1);
+ }
+
+ /* Create connection that supports multi statements */
+ if (!(mysql_real_connect(mysql_local, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, CLIENT_MULTI_STATEMENTS)))
+ {
+ fprintf(stdout, "\n connection failed(%s)", mysql_error(mysql_local));
+ exit(1);
+ }
+ mysql_options(mysql_local, MYSQL_OPT_RECONNECT, &my_true);
+
+ rc= mysql_query(mysql_local, query);
+ myquery(rc);
+
+ for (count= 0 ; count < array_elements(rows) ; count++)
+ {
+ if (!opt_silent)
+ fprintf(stdout, "\n Query %d: ", count);
+ if ((result= mysql_store_result(mysql_local)))
+ {
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+ }
+ else if (!opt_silent)
+ fprintf(stdout, "OK, %ld row(s) affected, %ld warning(s)\n",
+ (ulong) mysql_affected_rows(mysql_local),
+ (ulong) mysql_warning_count(mysql_local));
+
+ exp_value= (uint) mysql_affected_rows(mysql_local);
+ if (rows[count] != exp_value)
+ {
+ fprintf(stderr, "row %d had affected rows: %d, should be %d\n",
+ count, exp_value, rows[count]);
+ exit(1);
+ }
+ if (count != array_elements(rows) -1)
+ {
+ if (!(rc= mysql_more_results(mysql_local)))
+ {
+ fprintf(stdout,
+ "mysql_more_result returned wrong value: %d for row %d\n",
+ rc, count);
+ exit(1);
+ }
+ if ((rc= mysql_next_result(mysql_local)))
+ {
+ exp_value= mysql_errno(mysql_local);
+
+ exit(1);
+ }
+ }
+ else
+ {
+ rc= mysql_more_results(mysql_local);
+ DIE_UNLESS(rc == 0);
+ rc= mysql_next_result(mysql_local);
+ DIE_UNLESS(rc == -1);
+ }
+ }
+
+ /* check that errors abort multi statements */
+
+ rc= mysql_query(mysql_local, "select 1+1+a;select 1+1");
+ myquery_r(rc);
+ rc= mysql_more_results(mysql_local);
+ DIE_UNLESS(rc == 0);
+ rc= mysql_next_result(mysql_local);
+ DIE_UNLESS(rc == -1);
+
+ rc= mysql_query(mysql_local, "select 1+1;select 1+1+a;select 1");
+ myquery(rc);
+ result= mysql_store_result(mysql_local);
+ mytest(result);
+ mysql_free_result(result);
+ rc= mysql_more_results(mysql_local);
+ DIE_UNLESS(rc == 1);
+ rc= mysql_next_result(mysql_local);
+ DIE_UNLESS(rc > 0);
+
+ /*
+ Ensure that we can now do a simple query (this checks that the server is
+ not trying to send us the results for the last 'select 1'
+ */
+ rc= mysql_query(mysql_local, "select 1+1+1");
+ myquery(rc);
+ result= mysql_store_result(mysql_local);
+ mytest(result);
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+
+ /*
+ Check if errors in one of the queries handled properly.
+ */
+ rc= mysql_query(mysql_local, "select 1; select * from not_existing_table");
+ myquery(rc);
+ result= mysql_store_result(mysql_local);
+ mysql_free_result(result);
+
+ rc= mysql_next_result(mysql_local);
+ DIE_UNLESS(rc > 0);
+
+ rc= mysql_next_result(mysql_local);
+ DIE_UNLESS(rc < 0);
+
+ mysql_close(mysql_local);
+}
+
+
+/*
+ Check that Prepared statement cannot contain several
+ SQL statements
+*/
+
+static void test_prepare_multi_statements()
+{
+ MYSQL *mysql_local;
+ MYSQL_STMT *stmt;
+ char query[MAX_TEST_QUERY_LENGTH];
+ myheader("test_prepare_multi_statements");
+
+ if (!(mysql_local= mysql_client_init(NULL)))
+ {
+ fprintf(stderr, "\n mysql_client_init() failed");
+ exit(1);
+ }
+
+ if (!(mysql_real_connect(mysql_local, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, CLIENT_MULTI_STATEMENTS)))
+ {
+ fprintf(stderr, "\n connection failed(%s)", mysql_error(mysql_local));
+ exit(1);
+ }
+ mysql_options(mysql_local, MYSQL_OPT_RECONNECT, &my_true);
+ strmov(query, "select 1; select 'another value'");
+ stmt= mysql_simple_prepare(mysql_local, query);
+ check_stmt_r(stmt);
+ mysql_close(mysql_local);
+}
+
+
+/* Test simple bind store result */
+
+static void test_store_result()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ int32 nData;
+ char szData[100];
+ MYSQL_BIND my_bind[2];
+ ulong length, length1;
+ my_bool is_null[2];
+
+ myheader("test_store_result");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_store_result(col1 int , col2 varchar(50))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_store_result VALUES(10, 'venu'), (20, 'mysql')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_store_result(col2) VALUES('monty')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* fetch */
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *) &nData; /* integer data */
+ my_bind[0].length= &length;
+ my_bind[0].is_null= &is_null[0];
+
+ length= 0;
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= szData; /* string data */
+ my_bind[1].buffer_length= sizeof(szData);
+ my_bind[1].length= &length1;
+ my_bind[1].is_null= &is_null[1];
+ length1= 0;
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_store_result");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %ld, %s(%lu)", (long) nData, szData, length1);
+ DIE_UNLESS(nData == 10);
+ DIE_UNLESS(strcmp(szData, "venu") == 0);
+ DIE_UNLESS(length1 == 4);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 2: %ld, %s(%lu)", (long) nData, szData, length1);
+ DIE_UNLESS(nData == 20);
+ DIE_UNLESS(strcmp(szData, "mysql") == 0);
+ DIE_UNLESS(length1 == 5);
+
+ length= 99;
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent && is_null[0])
+ fprintf(stdout, "\n row 3: NULL, %s(%lu)", szData, length1);
+ DIE_UNLESS(is_null[0]);
+ DIE_UNLESS(strcmp(szData, "monty") == 0);
+ DIE_UNLESS(length1 == 5);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %ld, %s(%lu)", (long) nData, szData, length1);
+ DIE_UNLESS(nData == 10);
+ DIE_UNLESS(strcmp(szData, "venu") == 0);
+ DIE_UNLESS(length1 == 4);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 2: %ld, %s(%lu)", (long) nData, szData, length1);
+ DIE_UNLESS(nData == 20);
+ DIE_UNLESS(strcmp(szData, "mysql") == 0);
+ DIE_UNLESS(length1 == 5);
+
+ length= 99;
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent && is_null[0])
+ fprintf(stdout, "\n row 3: NULL, %s(%lu)", szData, length1);
+ DIE_UNLESS(is_null[0]);
+ DIE_UNLESS(strcmp(szData, "monty") == 0);
+ DIE_UNLESS(length1 == 5);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple bind store result */
+
+static void test_store_result1()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+
+ myheader("test_store_result1");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_store_result(col1 int , col2 varchar(50))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_store_result VALUES(10, 'venu'), (20, 'mysql')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_store_result(col2) VALUES('monty')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_store_result");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= 0;
+ while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
+ rc++;
+ if (!opt_silent)
+ fprintf(stdout, "\n total rows: %d", rc);
+ DIE_UNLESS(rc == 3);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= 0;
+ while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
+ rc++;
+ if (!opt_silent)
+ fprintf(stdout, "\n total rows: %d", rc);
+ DIE_UNLESS(rc == 3);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Another test for bind and store result */
+
+static void test_store_result2()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ int nData;
+ ulong length;
+ MYSQL_BIND my_bind[1];
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_store_result2");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_store_result(col1 int , col2 varchar(50))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_store_result VALUES(10, 'venu'), (20, 'mysql')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_store_result(col2) VALUES('monty')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *) &nData; /* integer data */
+ my_bind[0].length= &length;
+ my_bind[0].is_null= 0;
+
+ strmov((char *)query , "SELECT col1 FROM test_store_result where col1= ?");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ nData= 10; length= 0;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ nData= 0;
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %d", nData);
+ DIE_UNLESS(nData == 10);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ nData= 20;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ nData= 0;
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %d", nData);
+ DIE_UNLESS(nData == 20);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test simple subselect prepare */
+
+static void test_subselect()
+{
+
+ MYSQL_STMT *stmt;
+ int rc, id;
+ MYSQL_BIND my_bind[1];
+ DBUG_ENTER("test_subselect");
+
+ myheader("test_subselect");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_sub1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_sub2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_sub1(id int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_sub2(id int, id1 int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_sub1 values(2)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_sub2 VALUES(1, 7), (2, 7)");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ /* fetch */
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *) &id;
+ my_bind[0].length= 0;
+ my_bind[0].is_null= 0;
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO test_sub2(id) SELECT * FROM test_sub1 WHERE id= ?");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ id= 2;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_st_affected_rows(stmt, 1);
+
+ id= 9;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_st_affected_rows(stmt, 0);
+
+ mysql_stmt_close(stmt);
+
+ rc= my_stmt_result("SELECT * FROM test_sub2");
+ DIE_UNLESS(rc == 3);
+
+ rc= my_stmt_result("SELECT ROW(1, 7) IN (select id, id1 "
+ "from test_sub2 WHERE id1= 8)");
+ DIE_UNLESS(rc == 1);
+ rc= my_stmt_result("SELECT ROW(1, 7) IN (select id, id1 "
+ "from test_sub2 WHERE id1= 7)");
+ DIE_UNLESS(rc == 1);
+
+ stmt= mysql_simple_prepare(mysql, ("SELECT ROW(1, 7) IN (select id, id1 "
+ "from test_sub2 WHERE id1= ?)"));
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ id= 7;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %d", id);
+ DIE_UNLESS(id == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ id= 8;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %d", id);
+ DIE_UNLESS(id == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Generalized conversion routine to handle DATE, TIME and DATETIME
+ conversion using MYSQL_TIME structure
+*/
+
+static void test_bind_date_conv(uint row_count)
+{
+ MYSQL_STMT *stmt= 0;
+ uint rc, i, count= row_count;
+ ulong length[4];
+ MYSQL_BIND my_bind[4];
+ my_bool is_null[4]= {0};
+ MYSQL_TIME tm[4];
+ ulong second_part;
+ uint year, month, day, hour, minute, sec;
+ uint now_year= 1990, now_month= 3, now_day= 13;
+
+ rc= mysql_query(mysql, "SET timestamp=UNIX_TIMESTAMP('1990-03-13')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO test_date VALUES(?, ?, ?, ?)");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 4);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_TIMESTAMP;
+ my_bind[1].buffer_type= MYSQL_TYPE_TIME;
+ my_bind[2].buffer_type= MYSQL_TYPE_DATETIME;
+ my_bind[3].buffer_type= MYSQL_TYPE_DATE;
+
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].buffer= (void *) &tm[i];
+ my_bind[i].is_null= &is_null[i];
+ my_bind[i].length= &length[i];
+ my_bind[i].buffer_length= 30;
+ length[i]= 20;
+ }
+
+ second_part= 0;
+
+ year= 2000;
+ month= 01;
+ day= 10;
+
+ hour= 11;
+ minute= 16;
+ sec= 20;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ for (count= 0; count < row_count; count++)
+ {
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ tm[i].neg= 0;
+ tm[i].second_part= second_part+count;
+ if (my_bind[i].buffer_type != MYSQL_TYPE_TIME)
+ {
+ tm[i].year= year+count;
+ tm[i].month= month+count;
+ tm[i].day= day+count;
+ }
+ else
+ tm[i].year= tm[i].month= tm[i].day= 0;
+ if (my_bind[i].buffer_type != MYSQL_TYPE_DATE)
+ {
+ tm[i].hour= hour+count;
+ tm[i].minute= minute+count;
+ tm[i].second= sec+count;
+ }
+ else
+ tm[i].hour= tm[i].minute= tm[i].second= 0;
+ }
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= my_stmt_result("SELECT * FROM test_date");
+ DIE_UNLESS(row_count == rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_date");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ for (count= 0; count < row_count; count++)
+ {
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0 || rc == MYSQL_DATA_TRUNCATED);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n");
+ for (i= 0; i < array_elements(my_bind); i++)
+ {
+ if (!opt_silent)
+ fprintf(stdout, "\ntime[%d]: %02d-%02d-%02d %02d:%02d:%02d.%02lu",
+ i, tm[i].year, tm[i].month, tm[i].day,
+ tm[i].hour, tm[i].minute, tm[i].second,
+ tm[i].second_part);
+ DIE_UNLESS(tm[i].year == 0 || tm[i].year == year + count ||
+ (tm[i].year == now_year &&
+ my_bind[i].buffer_type == MYSQL_TYPE_TIME));
+ DIE_UNLESS(tm[i].month == 0 || tm[i].month == month + count ||
+ (tm[i].month == now_month &&
+ my_bind[i].buffer_type == MYSQL_TYPE_TIME));
+ DIE_UNLESS(tm[i].day == 0 || tm[i].day == day + count ||
+ (tm[i].day == now_day &&
+ my_bind[i].buffer_type == MYSQL_TYPE_TIME));
+
+ DIE_UNLESS(tm[i].hour == 0 || tm[i].hour == hour+count);
+ DIE_UNLESS(tm[i].minute == 0 || tm[i].minute == minute+count);
+ DIE_UNLESS(tm[i].second == 0 || tm[i].second == sec+count);
+ DIE_UNLESS(tm[i].second_part == 0 ||
+ tm[i].second_part == second_part+count);
+ }
+ }
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test DATE, TIME, DATETIME and TS with MYSQL_TIME conversion */
+
+static void test_date()
+{
+ int rc;
+
+ myheader("test_date");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_date(c1 TIMESTAMP, \
+ c2 TIME, \
+ c3 DATETIME, \
+ c4 DATE)");
+
+ myquery(rc);
+
+ test_bind_date_conv(5);
+}
+
+
+/* Test all time types to DATE and DATE to all types */
+
+static void test_date_date()
+{
+ int rc;
+
+ myheader("test_date_date");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_date(c1 DATE, \
+ c2 DATE, \
+ c3 DATE, \
+ c4 DATE)");
+
+ myquery(rc);
+
+ test_bind_date_conv(3);
+}
+
+
+/* Test all time types to TIME and TIME to all types */
+
+static void test_date_time()
+{
+ int rc;
+
+ myheader("test_date_time");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_date(c1 TIME, \
+ c2 TIME, \
+ c3 TIME, \
+ c4 TIME)");
+
+ myquery(rc);
+
+ test_bind_date_conv(3);
+}
+
+
+/* Test all time types to TIMESTAMP and TIMESTAMP to all types */
+
+static void test_date_ts()
+{
+ int rc;
+
+ myheader("test_date_ts");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_date(c1 TIMESTAMP, \
+ c2 TIMESTAMP, \
+ c3 TIMESTAMP, \
+ c4 TIMESTAMP)");
+
+ myquery(rc);
+
+ test_bind_date_conv(2);
+}
+
+
+/* Test all time types to DATETIME and DATETIME to all types */
+
+static void test_date_dt()
+{
+ int rc;
+
+ myheader("test_date_dt");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_date(c1 datetime, "
+ " c2 datetime, c3 datetime, c4 date)");
+ myquery(rc);
+
+ test_bind_date_conv(2);
+}
+
+static void test_simple_temporal() {
+
+ MYSQL_STMT *stmt = NULL;
+ uint rc;
+ ulong length = 0;
+ MYSQL_BIND my_bind[4], my_bind2;
+ my_bool is_null = FALSE;
+ MYSQL_TIME tm;
+ char string[100];
+ MYSQL_RES *rs;
+ MYSQL_FIELD *field;
+
+ myheader("test_simple_temporal");
+
+ /* Initialize param/fetch buffers for data, null flags, lengths */
+ memset(&my_bind, 0, sizeof(my_bind));
+ memset(&my_bind2, 0, sizeof(my_bind2));
+
+ /* Initialize the first input parameter */
+ my_bind[0].buffer_type = MYSQL_TYPE_DATETIME;
+ my_bind[0].buffer = &tm;
+ my_bind[0].is_null = &is_null;
+ my_bind[0].length = &length;
+ my_bind[0].buffer_length = sizeof(tm);
+
+ /* Clone the other input parameters */
+ my_bind[3] = my_bind[2] = my_bind[1] = my_bind[0];
+
+ my_bind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
+ my_bind[2].buffer_type = MYSQL_TYPE_DATE;
+ my_bind[3].buffer_type = MYSQL_TYPE_TIME;
+
+ /* Initialize fetch parameter */
+ my_bind2.buffer_type = MYSQL_TYPE_STRING;
+ my_bind2.length = &length;
+ my_bind2.is_null = &is_null;
+ my_bind2.buffer_length = sizeof(string);
+ my_bind2.buffer = string;
+
+ /* Prepare and bind simple SELECT with DATETIME parameter */
+ stmt = mysql_simple_prepare(mysql, "SELECT ?");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc = mysql_stmt_bind_param(stmt, &my_bind[0]);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_bind_result(stmt, &my_bind2);
+ check_execute(stmt, rc);
+
+ /* Initialize DATETIME value */
+ tm.neg = FALSE;
+ tm.time_type = MYSQL_TIMESTAMP_DATETIME;
+ tm.year = 2001;
+ tm.month = 10;
+ tm.day = 20;
+ tm.hour = 10;
+ tm.minute = 10;
+ tm.second = 59;
+ tm.second_part = 500000;
+
+ /* Execute and fetch */
+ rc = mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rs = mysql_stmt_result_metadata(stmt);
+ field = mysql_fetch_fields(rs);
+
+ rc = mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(field->type == MYSQL_TYPE_DATETIME);
+ DIE_UNLESS(strcmp(string, "2001-10-20 10:10:59.500000") == 0);
+
+ mysql_free_result(rs);
+
+ mysql_stmt_close(stmt);
+
+ /* Same test with explicit CAST */
+ stmt = mysql_simple_prepare(mysql, "SELECT CAST(? AS DATETIME(6))");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc = mysql_stmt_bind_param(stmt, &my_bind[0]);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_bind_result(stmt, &my_bind2);
+ check_execute(stmt, rc);
+
+ /* Execute and fetch */
+ rc = mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rs = mysql_stmt_result_metadata(stmt);
+ field = mysql_fetch_fields(rs);
+
+ rc = mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(field->type == MYSQL_TYPE_DATETIME);
+ DIE_UNLESS(strcmp(string, "2001-10-20 10:10:59.500000") == 0);
+
+ mysql_free_result(rs);
+
+ mysql_stmt_close(stmt);
+
+ /* Prepare and bind simple SELECT with TIMESTAMP parameter */
+ stmt = mysql_simple_prepare(mysql, "SELECT ?");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc = mysql_stmt_bind_param(stmt, &my_bind[1]);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_bind_result(stmt, &my_bind2);
+ check_execute(stmt, rc);
+
+ /* Initialize TIMESTAMP value */
+ tm.neg = FALSE;
+ tm.time_type = MYSQL_TIMESTAMP_DATETIME;
+ tm.year = 2001;
+ tm.month = 10;
+ tm.day = 20;
+ tm.hour = 10;
+ tm.minute = 10;
+ tm.second = 59;
+ tm.second_part = 500000;
+
+ /* Execute and fetch */
+ rc = mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rs = mysql_stmt_result_metadata(stmt);
+ field = mysql_fetch_fields(rs);
+
+ rc = mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(field->type == MYSQL_TYPE_TIMESTAMP);
+ DIE_UNLESS(strcmp(string, "2001-10-20 10:10:59.500000") == 0);
+
+ mysql_free_result(rs);
+
+ mysql_stmt_close(stmt);
+
+ /* Prepare and bind simple SELECT with DATE parameter */
+ stmt = mysql_simple_prepare(mysql, "SELECT ?");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc = mysql_stmt_bind_param(stmt, &my_bind[2]);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_bind_result(stmt, &my_bind2);
+ check_execute(stmt, rc);
+
+ /* Initialize DATE value */
+ tm.neg = FALSE;
+ tm.time_type = MYSQL_TIMESTAMP_DATE;
+ tm.year = 2001;
+ tm.month = 10;
+ tm.day = 20;
+ tm.hour = 0;
+ tm.minute = 0;
+ tm.second = 0;
+ tm.second_part = 0;
+
+ /* Execute and fetch */
+ rc = mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rs = mysql_stmt_result_metadata(stmt);
+ field = mysql_fetch_fields(rs);
+
+ rc = mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(field->type == MYSQL_TYPE_DATE);
+ DIE_UNLESS(strcmp(string, "2001-10-20") == 0);
+
+ mysql_free_result(rs);
+
+ mysql_stmt_close(stmt);
+
+ /* Same test with explicit CAST */
+ stmt = mysql_simple_prepare(mysql, "SELECT CAST(? AS DATE)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc = mysql_stmt_bind_param(stmt, &my_bind[2]);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_bind_result(stmt, &my_bind2);
+ check_execute(stmt, rc);
+
+ /* Execute and fetch */
+ rc = mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rs = mysql_stmt_result_metadata(stmt);
+ field = mysql_fetch_fields(rs);
+
+ rc = mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(field->type == MYSQL_TYPE_DATE);
+ DIE_UNLESS(strcmp(string, "2001-10-20") == 0);
+
+ mysql_free_result(rs);
+
+ mysql_stmt_close(stmt);
+
+ /* Prepare and bind simple SELECT with TIME parameter */
+ stmt = mysql_simple_prepare(mysql, "SELECT ?");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc = mysql_stmt_bind_param(stmt, &my_bind[3]);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_bind_result(stmt, &my_bind2);
+ check_execute(stmt, rc);
+
+ /* Initialize TIME value */
+ tm.neg = FALSE;
+ tm.time_type = MYSQL_TIMESTAMP_TIME;
+ tm.year = 0;
+ tm.month = 0;
+ tm.day = 0;
+ tm.hour = 10;
+ tm.minute = 10;
+ tm.second = 59;
+ tm.second_part = 500000;
+
+ /* Execute and fetch */
+ rc = mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rs = mysql_stmt_result_metadata(stmt);
+ field = mysql_fetch_fields(rs);
+
+ rc = mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(field->type == MYSQL_TYPE_TIME);
+ DIE_UNLESS(strcmp(string, "10:10:59.500000") == 0);
+
+ mysql_free_result(rs);
+
+ mysql_stmt_close(stmt);
+
+ /* Same test with explicit CAST */
+ stmt = mysql_simple_prepare(mysql, "SELECT CAST(? AS TIME(6))");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc = mysql_stmt_bind_param(stmt, &my_bind[3]);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_bind_result(stmt, &my_bind2);
+ check_execute(stmt, rc);
+
+ /* Initialize TIME value */
+ tm.neg = FALSE;
+ tm.time_type = MYSQL_TIMESTAMP_TIME;
+ tm.year = 0;
+ tm.month = 0;
+ tm.day = 0;
+ tm.hour = 10;
+ tm.minute = 10;
+ tm.second = 59;
+ tm.second_part = 500000;
+
+ /* Execute and fetch */
+ rc = mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rs = mysql_stmt_result_metadata(stmt);
+ field = mysql_fetch_fields(rs);
+
+ rc = mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc = mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(field->type == MYSQL_TYPE_TIME);
+ DIE_UNLESS(strcmp(string, "10:10:59.500000") == 0);
+
+ mysql_free_result(rs);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Misc tests to keep pure coverage happy */
+
+static void test_pure_coverage()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ int rc;
+ ulong length;
+
+ myheader("test_pure_coverage");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_pure");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_pure(c1 int, c2 varchar(20))");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "insert into test_pure(c67788) values(10)");
+ check_stmt_r(stmt);
+
+ /* Query without params and result should allow to bind 0 arrays */
+ stmt= mysql_simple_prepare(mysql, "insert into test_pure(c2) values(10)");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_param(stmt, (MYSQL_BIND*)0);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, (MYSQL_BIND*)0);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "insert into test_pure(c2) values(?)");
+ check_stmt(stmt);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].length= &length;
+ my_bind[0].is_null= 0;
+ my_bind[0].buffer_length= 0;
+
+ my_bind[0].buffer_type= MYSQL_TYPE_GEOMETRY;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute_r(stmt, rc); /* unsupported buffer type */
+
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "select * from test_pure");
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_bind[0].buffer_type= MYSQL_TYPE_GEOMETRY;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc); /* MariaDB C/C converts geometry to string */
+
+ rc= mysql_stmt_store_result(stmt);
+ DIE_IF(rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ DIE_UNLESS(rc); /* Old error must be reset first */
+
+ mysql_stmt_close(stmt);
+
+ mysql_query(mysql, "DROP TABLE test_pure");
+}
+
+
+/* Test for string buffer fetch */
+
+static void test_buffers()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ int rc;
+ ulong length;
+ my_bool is_null;
+ char buffer[20];
+
+ myheader("test_buffers");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_buffer");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_buffer(str varchar(20))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into test_buffer values('MySQL')\
+ , ('Database'), ('Open-Source'), ('Popular')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select str from test_buffer");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero(buffer, sizeof(buffer)); /* Avoid overruns in printf() */
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].length= &length;
+ my_bind[0].is_null= &is_null;
+ my_bind[0].buffer_length= 1;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)buffer;
+ my_bind[0].error= &my_bind[0].error_value;
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ buffer[1]= 'X';
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(my_bind[0].error_value);
+ if (!opt_silent)
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ DIE_UNLESS(buffer[0] == 'M');
+ DIE_UNLESS(buffer[1] == 'X');
+ DIE_UNLESS(length == 5);
+
+ my_bind[0].buffer_length= 8;
+ rc= mysql_stmt_bind_result(stmt, my_bind);/* re-bind */
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ DIE_UNLESS(strncmp(buffer, "Database", 8) == 0);
+ DIE_UNLESS(length == 8);
+
+ my_bind[0].buffer_length= 12;
+ rc= mysql_stmt_bind_result(stmt, my_bind);/* re-bind */
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ DIE_UNLESS(strcmp(buffer, "Open-Source") == 0);
+ DIE_UNLESS(length == 11);
+
+ my_bind[0].buffer_length= 6;
+ rc= mysql_stmt_bind_result(stmt, my_bind);/* re-bind */
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(my_bind[0].error_value);
+ if (!opt_silent)
+ fprintf(stdout, "\n data: %s (%lu)", buffer, length);
+ DIE_UNLESS(strncmp(buffer, "Popula", 6) == 0);
+ DIE_UNLESS(length == 7);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test the direct query execution in the middle of open stmts */
+
+static void test_open_direct()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ int rc;
+
+ myheader("test_open_direct");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_open_direct");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_open_direct(id int, name char(6))");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO test_open_direct values(10, 'mysql')");
+ check_stmt(stmt);
+
+ rc= mysql_query(mysql, "SELECT * FROM test_open_direct");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+ mysql_free_result(result);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_st_affected_rows(stmt, 1);
+
+ rc= mysql_query(mysql, "SELECT * FROM test_open_direct");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_st_affected_rows(stmt, 1);
+
+ rc= mysql_query(mysql, "SELECT * FROM test_open_direct");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 2);
+ mysql_free_result(result);
+
+ mysql_stmt_close(stmt);
+
+ /* run a direct query in the middle of a fetch */
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_open_direct");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_open_direct(id) VALUES(20)");
+ myquery_r(rc);
+
+ rc= mysql_stmt_close(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_open_direct(id) VALUES(20)");
+ myquery(rc);
+
+ /* run a direct query with store result */
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_open_direct");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "drop table test_open_direct");
+ myquery(rc);
+
+ rc= mysql_stmt_close(stmt);
+ check_execute(stmt, rc);
+}
+
+
+/* Test fetch without prior bound buffers */
+
+static void test_fetch_nobuffs()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[4];
+ char str[4][50];
+ int rc;
+
+ myheader("test_fetch_nobuffs");
+
+ stmt= mysql_simple_prepare(mysql, "SELECT DATABASE(), CURRENT_USER(), \
+ CURRENT_DATE(), CURRENT_TIME()");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= 0;
+ while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
+ rc++;
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total rows : %d", rc);
+ DIE_UNLESS(rc == 1);
+
+ bzero((char*) my_bind, sizeof(MYSQL_BIND));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)str[0];
+ my_bind[0].buffer_length= sizeof(str[0]);
+ my_bind[1]= my_bind[2]= my_bind[3]= my_bind[0];
+ my_bind[1].buffer= (void *)str[1];
+ my_bind[2].buffer= (void *)str[2];
+ my_bind[3].buffer= (void *)str[3];
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= 0;
+ while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
+ {
+ rc++;
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n CURRENT_DATABASE(): %s", str[0]);
+ fprintf(stdout, "\n CURRENT_USER() : %s", str[1]);
+ fprintf(stdout, "\n CURRENT_DATE() : %s", str[2]);
+ fprintf(stdout, "\n CURRENT_TIME() : %s", str[3]);
+ }
+ }
+ if (!opt_silent)
+ fprintf(stdout, "\n total rows : %d", rc);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test a misc bug */
+
+static void test_ushort_bug()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[4];
+ ushort short_value;
+ uint32 long_value;
+ ulong s_length, l_length, ll_length, t_length;
+ ulonglong longlong_value;
+ int rc;
+ uchar tiny_value;
+ char llbuf[22];
+ myheader("test_ushort_bug");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ushort");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_ushort(a smallint unsigned, \
+ b smallint unsigned, \
+ c smallint unsigned, \
+ d smallint unsigned)");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "INSERT INTO test_ushort VALUES(35999, 35999, 35999, 200)");
+ myquery(rc);
+
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_ushort");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[0].buffer= (void *)&short_value;
+ my_bind[0].is_unsigned= TRUE;
+ my_bind[0].length= &s_length;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[1].buffer= (void *)&long_value;
+ my_bind[1].length= &l_length;
+
+ my_bind[2].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[2].buffer= (void *)&longlong_value;
+ my_bind[2].length= &ll_length;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[3].buffer= (void *)&tiny_value;
+ my_bind[3].is_unsigned= TRUE;
+ my_bind[3].length= &t_length;
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n ushort : %d (%ld)", short_value, s_length);
+ fprintf(stdout, "\n ulong : %lu (%ld)", (ulong) long_value, l_length);
+ fprintf(stdout, "\n longlong : %s (%ld)", llstr(longlong_value, llbuf),
+ ll_length);
+ fprintf(stdout, "\n tinyint : %d (%ld)", tiny_value, t_length);
+ }
+
+ DIE_UNLESS(short_value == 35999);
+ DIE_UNLESS(s_length == 2);
+
+ DIE_UNLESS(long_value == 35999);
+ DIE_UNLESS(l_length == 4);
+
+ DIE_UNLESS(longlong_value == 35999);
+ DIE_UNLESS(ll_length == 8);
+
+ DIE_UNLESS(tiny_value == 200);
+ DIE_UNLESS(t_length == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test a misc smallint-signed conversion bug */
+
+static void test_sshort_bug()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[4];
+ short short_value;
+ int32 long_value;
+ ulong s_length, l_length, ll_length, t_length;
+ ulonglong longlong_value;
+ int rc;
+ uchar tiny_value;
+ char llbuf[22];
+
+ myheader("test_sshort_bug");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_sshort");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_sshort(a smallint signed, \
+ b smallint signed, \
+ c smallint unsigned, \
+ d smallint unsigned)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_sshort VALUES(-5999, -5999, 35999, 200)");
+ myquery(rc);
+
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_sshort");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[0].buffer= (void *)&short_value;
+ my_bind[0].length= &s_length;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[1].buffer= (void *)&long_value;
+ my_bind[1].length= &l_length;
+
+ my_bind[2].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[2].buffer= (void *)&longlong_value;
+ my_bind[2].length= &ll_length;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[3].buffer= (void *)&tiny_value;
+ my_bind[3].is_unsigned= TRUE;
+ my_bind[3].length= &t_length;
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n sshort : %d (%ld)", short_value, s_length);
+ fprintf(stdout, "\n slong : %ld (%ld)", (long) long_value, l_length);
+ fprintf(stdout, "\n longlong : %s (%ld)", llstr(longlong_value, llbuf),
+ ll_length);
+ fprintf(stdout, "\n tinyint : %d (%ld)", tiny_value, t_length);
+ }
+
+ DIE_UNLESS(short_value == -5999);
+ DIE_UNLESS(s_length == 2);
+
+ DIE_UNLESS(long_value == -5999);
+ DIE_UNLESS(l_length == 4);
+
+ DIE_UNLESS(longlong_value == 35999);
+ DIE_UNLESS(ll_length == 8);
+
+ DIE_UNLESS(tiny_value == 200);
+ DIE_UNLESS(t_length == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test a misc tinyint-signed conversion bug */
+
+static void test_stiny_bug()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[4];
+ short short_value;
+ int32 long_value;
+ ulong s_length, l_length, ll_length, t_length;
+ ulonglong longlong_value;
+ int rc;
+ uchar tiny_value;
+ char llbuf[22];
+
+ myheader("test_stiny_bug");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_stiny");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_stiny(a tinyint signed, \
+ b tinyint signed, \
+ c tinyint unsigned, \
+ d tinyint unsigned)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_stiny VALUES(-128, -127, 255, 0)");
+ myquery(rc);
+
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_stiny");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[0].buffer= (void *)&short_value;
+ my_bind[0].length= &s_length;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[1].buffer= (void *)&long_value;
+ my_bind[1].length= &l_length;
+
+ my_bind[2].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[2].buffer= (void *)&longlong_value;
+ my_bind[2].length= &ll_length;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[3].buffer= (void *)&tiny_value;
+ my_bind[3].length= &t_length;
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "\n sshort : %d (%ld)", short_value, s_length);
+ fprintf(stdout, "\n slong : %ld (%ld)", (long) long_value, l_length);
+ fprintf(stdout, "\n longlong : %s (%ld)", llstr(longlong_value, llbuf),
+ ll_length);
+ fprintf(stdout, "\n tinyint : %d (%ld)", tiny_value, t_length);
+ }
+
+ DIE_UNLESS(short_value == -128);
+ DIE_UNLESS(s_length == 2);
+
+ DIE_UNLESS(long_value == -127);
+ DIE_UNLESS(l_length == 4);
+
+ DIE_UNLESS(longlong_value == 255);
+ DIE_UNLESS(ll_length == 8);
+
+ DIE_UNLESS(tiny_value == 0);
+ DIE_UNLESS(t_length == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test misc field information, bug: #74 */
+
+static void test_field_misc()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ int rc;
+
+ myheader("test_field_misc");
+
+ rc= mysql_query(mysql, "SELECT @@autocommit");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+
+ verify_prepare_field(result, 0,
+ "@@autocommit", "", /* field and its org name */
+ MYSQL_TYPE_LONGLONG, /* field type */
+ "", "", /* table and its org name */
+ "", 1, 0); /* db name, length(its bool flag)*/
+
+ mysql_free_result(result);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT @@autocommit");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ verify_prepare_field(result, 0,
+ "@@autocommit", "", /* field and its org name */
+ MYSQL_TYPE_LONGLONG, /* field type */
+ "", "", /* table and its org name */
+ "", 1, 0); /* db name, length(its bool flag)*/
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT @@max_error_count");
+ check_stmt(stmt);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ verify_prepare_field(result, 0,
+ "@@max_error_count", "", /* field and its org name */
+ MYSQL_TYPE_LONGLONG, /* field type */
+ "", "", /* table and its org name */
+ /* db name, length */
+ "", MY_INT64_NUM_DECIMAL_DIGITS , 0);
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT @@max_allowed_packet");
+ check_stmt(stmt);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(1 == my_process_stmt_result(stmt));
+
+ verify_prepare_field(result, 0,
+ "@@max_allowed_packet", "", /* field and its org name */
+ MYSQL_TYPE_LONGLONG, /* field type */
+ "", "", /* table and its org name */
+ /* db name, length */
+ "", MY_INT64_NUM_DECIMAL_DIGITS, 0);
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT @@sql_warnings");
+ check_stmt(stmt);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ verify_prepare_field(result, 0,
+ "@@sql_warnings", "", /* field and its org name */
+ MYSQL_TYPE_LONGLONG, /* field type */
+ "", "", /* table and its org name */
+ "", 1, 0); /* db name, length */
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+}
+
+
+/*
+ Test SET OPTION feature with prepare stmts
+ bug #85 (reported by mark@mysql.com)
+*/
+
+static void test_set_option()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ int rc;
+
+ myheader("test_set_option");
+
+ mysql_autocommit(mysql, TRUE);
+
+ /* LIMIT the rows count to 2 */
+ rc= mysql_query(mysql, "SET SQL_SELECT_LIMIT= 2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_limit");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_limit(a tinyint)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_limit VALUES(10), (20), (30), (40)");
+ myquery(rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n with SQL_SELECT_LIMIT= 2 (direct)");
+ rc= mysql_query(mysql, "SELECT * FROM test_limit");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 2);
+
+ mysql_free_result(result);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n with SQL_SELECT_LIMIT=2 (prepare)");
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_limit");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 2);
+
+ mysql_stmt_close(stmt);
+
+ /* RESET the LIMIT the rows count to 0 */
+ if (!opt_silent)
+ fprintf(stdout, "\n with SQL_SELECT_LIMIT=DEFAULT (prepare)");
+ rc= mysql_query(mysql, "SET SQL_SELECT_LIMIT=DEFAULT");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_limit");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 4);
+
+ mysql_stmt_close(stmt);
+}
+
+#ifdef EMBEDDED_LIBRARY
+static void test_embedded_start_stop()
+{
+ MYSQL *mysql_emb=NULL;
+ int i, j;
+ int argc= original_argc; // Start with the original args
+ char **argv, **my_argv;
+ char test_name[]= "test_embedded_start_stop";
+#define EMBEDDED_RESTARTS 64
+
+ myheader("test_embedded_start_stop");
+
+ /* Must stop the main embedded server, since we use the same config. */
+ client_disconnect(mysql); /* disconnect from server */
+ free_defaults(defaults_argv);
+ mysql_server_end();
+ /* Free everything allocated by my_once_alloc */
+ my_end(0);
+
+ /*
+ Use a copy of the original arguments.
+ The arguments will be altered when reading the configs and parsing
+ options.
+ */
+ my_argv= malloc((argc + 1) * sizeof(char*));
+ if (!my_argv)
+ exit(1);
+
+ /* Test restarting the embedded library many times. */
+ for (i= 1; i <= EMBEDDED_RESTARTS; i++)
+ {
+ argv= my_argv;
+ argv[0]= test_name;
+ for (j= 1; j < argc; j++)
+ argv[j]= original_argv[j];
+
+ /* Initialize everything again. */
+ MY_INIT(argv[0]);
+
+ /* Load the client defaults from the .cnf file[s]. */
+ load_defaults_or_exit("my", client_test_load_default_groups, &argc, &argv);
+
+ /* Parse the options (including the ones given from defaults files). */
+ get_options(&argc, &argv);
+
+ /* mysql_library_init is the same as mysql_server_init. */
+ if (mysql_library_init(embedded_server_arg_count,
+ embedded_server_args,
+ (char**) embedded_server_groups))
+ {
+ myerror("mysql_library_init failed");
+ exit(1);
+ }
+
+ /* Create a client connection. */
+ if (!(mysql_emb= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init failed");
+ exit(1);
+ }
+
+ /* Connect it and see if we can use the database. */
+ if (!(mysql_real_connect(mysql_emb, opt_host, opt_user,
+ opt_password, current_db, 0,
+ NULL, 0)))
+ {
+ myerror("mysql_real_connect failed");
+ }
+
+ /* Close the client connection */
+ mysql_close(mysql_emb);
+ mysql_emb = NULL;
+ /* Free arguments allocated for defaults files. */
+ free_defaults(defaults_argv);
+ /* mysql_library_end is a define for mysql_server_end. */
+ mysql_library_end();
+ /* Free everything allocated by my_once_alloc */
+ my_end(0);
+ }
+
+ argc= original_argc;
+ argv= my_argv;
+ argv[0]= test_name;
+ for (j= 1; j < argc; j++)
+ argv[j]= original_argv[j];
+
+ MY_INIT(argv[0]);
+
+ load_defaults_or_exit("my", client_test_load_default_groups, &argc, &argv);
+ get_options(&argc, &argv);
+
+ /* Must start the main embedded server again after the test. */
+ if (mysql_server_init(embedded_server_arg_count,
+ embedded_server_args,
+ (char**) embedded_server_groups))
+ DIE("Can't initialize MariaDB server");
+
+ /* connect to server with no flags, default protocol, auto reconnect true */
+ mysql= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 1);
+ free(my_argv);
+}
+#endif /* EMBEDDED_LIBRARY */
+
+
+/*
+ Test a misc GRANT option
+ bug #89 (reported by mark@mysql.com)
+*/
+
+#ifndef EMBEDDED_LIBRARY
+static void test_prepare_grant()
+{
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_prepare_grant");
+
+ mysql_autocommit(mysql, TRUE);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_grant");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_grant(a tinyint primary key auto_increment)");
+ myquery(rc);
+
+ strxmov(query, "GRANT INSERT, UPDATE, SELECT ON ", current_db,
+ ".test_grant TO 'test_grant'@",
+ opt_host ? opt_host : "'localhost'", NullS);
+
+ if (mysql_query(mysql, query))
+ {
+ myerror("GRANT failed");
+
+ /*
+ If server started with --skip-grant-tables, skip this test, else
+ exit to indicate an error
+
+ ER_UNKNOWN_COM_ERROR= 1047
+ */
+ if (mysql_errno(mysql) != 1047)
+ exit(1);
+ }
+ else
+ {
+ MYSQL *org_mysql= mysql, *lmysql;
+ MYSQL_STMT *stmt;
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a test connection ...");
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+ if (!(mysql_real_connect(lmysql, opt_host, "test_grant",
+ "", current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ mysql_close(lmysql);
+ exit(1);
+ }
+ mysql_options(lmysql, MYSQL_OPT_RECONNECT, &my_true);
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+
+ mysql= lmysql;
+ rc= mysql_query(mysql, "INSERT INTO test_grant VALUES(NULL)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_grant(a) VALUES(NULL)");
+ myquery(rc);
+
+ execute_prepare_query("INSERT INTO test_grant(a) VALUES(NULL)", 1);
+ execute_prepare_query("INSERT INTO test_grant VALUES(NULL)", 1);
+ execute_prepare_query("UPDATE test_grant SET a=9 WHERE a=1", 1);
+ rc= my_stmt_result("SELECT a FROM test_grant");
+ DIE_UNLESS(rc == 4);
+
+ /* Both DELETE expected to fail as user does not have DELETE privs */
+
+ rc= mysql_query(mysql, "DELETE FROM test_grant");
+ myquery_r(rc);
+
+ stmt= mysql_simple_prepare(mysql, "DELETE FROM test_grant");
+ check_stmt_r(stmt);
+
+ rc= my_stmt_result("SELECT * FROM test_grant");
+ DIE_UNLESS(rc == 4);
+
+ mysql_close(lmysql);
+ mysql= org_mysql;
+
+ rc= mysql_query(mysql, "delete from mysql.user where User='test_grant'");
+ myquery(rc);
+ DIE_UNLESS(1 == mysql_affected_rows(mysql));
+
+ rc= mysql_query(mysql, "delete from mysql.tables_priv where User='test_grant'");
+ myquery(rc);
+ DIE_UNLESS(1 == mysql_affected_rows(mysql));
+
+ }
+}
+#endif /* EMBEDDED_LIBRARY */
+
+/*
+ Test a crash when invalid/corrupted .frm is used in the
+ SHOW TABLE STATUS
+ bug #93 (reported by serg@mysql.com).
+*/
+
+static void test_frm_bug()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ FILE *test_file;
+ char data_dir[FN_REFLEN];
+ char test_frm[FN_REFLEN];
+ int rc;
+
+ myheader("test_frm_bug");
+
+ mysql_autocommit(mysql, TRUE);
+
+ rc= mysql_query(mysql, "drop table if exists test_frm_bug");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "flush tables");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "show variables like 'datadir'");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= data_dir;
+ my_bind[0].buffer_length= FN_REFLEN;
+ my_bind[1]= my_bind[0];
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n data directory: %s", data_dir);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ strxmov(test_frm, data_dir, "/", current_db, "/", "test_frm_bug.frm", NullS);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n test_frm: %s", test_frm);
+
+ if (!(test_file= my_fopen(test_frm, (int) (O_RDWR | O_CREAT), MYF(MY_WME))))
+ {
+ fprintf(stdout, "\n ERROR: my_fopen failed for '%s'", test_frm);
+ fprintf(stdout, "\n test cancelled");
+ exit(1);
+ }
+ if (!opt_silent)
+ fprintf(test_file, "this is a junk file for test");
+
+ rc= mysql_query(mysql, "SHOW TABLE STATUS like 'test_frm_bug'");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);/* It can't be NULL */
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+
+ mysql_data_seek(result, 0);
+
+ row= mysql_fetch_row(result);
+ mytest(row);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Comment: %s", row[17]);
+ DIE_UNLESS(row[17] != 0);
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+
+ my_fclose(test_file, MYF(0));
+ mysql_query(mysql, "drop table if exists test_frm_bug");
+}
+
+
+/* Test DECIMAL conversion */
+
+static void test_decimal_bug()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char data[30];
+ int rc;
+ my_bool is_null;
+
+ myheader("test_decimal_bug");
+
+ mysql_autocommit(mysql, TRUE);
+
+ rc= mysql_query(mysql, "drop table if exists test_decimal_bug");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table test_decimal_bug(c1 decimal(10, 2))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into test_decimal_bug value(8), (10.22), (5.61)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select c1 from test_decimal_bug where c1= ?");
+ check_stmt(stmt);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_NEWDECIMAL;
+ my_bind[0].buffer= (void *)data;
+ my_bind[0].buffer_length= 25;
+ my_bind[0].is_null= &is_null;
+
+ is_null= 0;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ memset(data, 0, sizeof data);
+ strmov(data, "8.0");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ data[0]= 0;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n data: %s", data);
+ DIE_UNLESS(strcmp(data, "8.00") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ strmov(data, "5.61");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ data[0]= 0;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n data: %s", data);
+ DIE_UNLESS(strcmp(data, "5.61") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ is_null= 1;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ strmov(data, "10.22"); is_null= 0;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ data[0]= 0;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n data: %s", data);
+ DIE_UNLESS(strcmp(data, "10.22") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test EXPLAIN bug (#115, reported by mark@mysql.com & georg@php.net). */
+
+static void test_explain_bug()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ int rc;
+
+ myheader("test_explain_bug");
+
+ mysql_autocommit(mysql, TRUE);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_explain");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_explain(id int, name char(2))");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "explain test_explain");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 2);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total fields in the result: %d",
+ mysql_num_fields(result));
+ DIE_UNLESS(6 == mysql_num_fields(result));
+
+ verify_prepare_field(result, 0, "Field", "COLUMN_NAME",
+ mysql_get_server_version(mysql) <= 50000 ?
+ MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING,
+ 0, 0, "information_schema", 64, 0);
+
+ verify_prepare_field(result, 1, "Type", "COLUMN_TYPE", MYSQL_TYPE_BLOB,
+ 0, 0, "information_schema", 0, 0);
+
+ verify_prepare_field(result, 2, "Null", "IS_NULLABLE",
+ mysql_get_server_version(mysql) <= 50000 ?
+ MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING,
+ 0, 0, "information_schema", 3, 0);
+
+ verify_prepare_field(result, 3, "Key", "COLUMN_KEY",
+ mysql_get_server_version(mysql) <= 50000 ?
+ MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING,
+ 0, 0, "information_schema", 3, 0);
+
+ if ( mysql_get_server_version(mysql) >= 50027 )
+ {
+ /* The patch for bug#23037 changes column type of DEAULT to blob */
+ verify_prepare_field(result, 4, "Default", "COLUMN_DEFAULT",
+ MYSQL_TYPE_BLOB, 0, 0, "information_schema", 0, 0);
+ }
+ else
+ {
+ verify_prepare_field(result, 4, "Default", "COLUMN_DEFAULT",
+ mysql_get_server_version(mysql) >= 50027 ?
+ MYSQL_TYPE_BLOB :
+ mysql_get_server_version(mysql) <= 50000 ?
+ MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING,
+ 0, 0, "information_schema",
+ mysql_get_server_version(mysql) >= 50027 ? 0 :64, 0);
+ }
+
+ verify_prepare_field(result, 5, "Extra", "EXTRA",
+ mysql_get_server_version(mysql) <= 50000 ?
+ MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING,
+ 0, 0, "information_schema", 80, 0);
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "explain select id, name FROM test_explain");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ result= mysql_stmt_result_metadata(stmt);
+ mytest(result);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total fields in the result: %d",
+ mysql_num_fields(result));
+ DIE_UNLESS(10 == mysql_num_fields(result));
+
+ verify_prepare_field(result, 0, "id", "", MYSQL_TYPE_LONGLONG,
+ "", "", "", 3, 0);
+
+ verify_prepare_field(result, 1, "select_type", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", 19, 0);
+
+ verify_prepare_field(result, 2, "table", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", NAME_CHAR_LEN, 0);
+
+ verify_prepare_field(result, 3, "type", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", 10, 0);
+
+ verify_prepare_field(result, 4, "possible_keys", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", NAME_CHAR_LEN*MAX_KEY, 0);
+
+ verify_prepare_field(result, 5, "key", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", NAME_CHAR_LEN, 0);
+
+ if (mysql_get_server_version(mysql) <= 50000)
+ {
+ verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_LONGLONG, "",
+ "", "", 3, 0);
+ }
+ else
+ {
+ verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_VAR_STRING, "",
+ "", "", NAME_CHAR_LEN*MAX_KEY, 0);
+ }
+
+ /* The length of this may verify between MariaDB versions (1024 / 2048) */
+ verify_prepare_field(result, 7, "ref", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", NAME_CHAR_LEN * HA_MAX_KEY_SEG, 0);
+
+ verify_prepare_field(result, 8, "rows", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", NAME_CHAR_LEN, 0);
+
+ verify_prepare_field(result, 9, "Extra", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", 255, 0);
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+}
+
+#ifdef NOT_YET_WORKING
+
+/*
+ Test math functions.
+ Bug #148 (reported by salle@mysql.com).
+*/
+
+#define myerrno(n) check_errcode(n)
+
+static void check_errcode(const unsigned int err)
+{
+ if (!opt_silent || mysql_errno(mysql) != err)
+ {
+ if (mysql->server_version)
+ fprintf(stdout, "\n [MySQL-%s]", mysql->server_version);
+ else
+ fprintf(stdout, "\n [MySQL]");
+ fprintf(stdout, "[%d] %s\n", mysql_errno(mysql), mysql_error(mysql));
+ }
+ DIE_UNLESS(mysql_errno(mysql) == err);
+}
+
+
+static void test_drop_temp()
+{
+ int rc;
+
+ myheader("test_drop_temp");
+
+ rc= mysql_query(mysql, "DROP DATABASE IF EXISTS test_drop_temp_db");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE DATABASE test_drop_temp_db");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_drop_temp_db.t1(c1 int, c2 char(1))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "delete from mysql.db where Db='test_drop_temp_db'");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "delete from mysql.db where Db='test_drop_temp_db'");
+ myquery(rc);
+
+ strxmov(query, "GRANT SELECT, USAGE, DROP ON test_drop_temp_db.* TO test_temp@",
+ opt_host ? opt_host : "localhost", NullS);
+
+ if (mysql_query(mysql, query))
+ {
+ myerror("GRANT failed");
+
+ /*
+ If server started with --skip-grant-tables, skip this test, else
+ exit to indicate an error
+
+ ER_UNKNOWN_COM_ERROR= 1047
+ */
+ if (mysql_errno(mysql) != 1047)
+ exit(1);
+ }
+ else
+ {
+ MYSQL *org_mysql= mysql, *lmysql;
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a test connection ...");
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+
+ rc= mysql_query(mysql, "flush privileges");
+ myquery(rc);
+
+ if (!(mysql_real_connect(lmysql, opt_host ? opt_host : "localhost", "test_temp",
+ "", "test_drop_temp_db", opt_port,
+ opt_unix_socket, 0)))
+ {
+ mysql= lmysql;
+ myerror("connection failed");
+ mysql_close(lmysql);
+ exit(1);
+ }
+ mysql_options(lmysql, MYSQL_OPT_RECONNECT, &my_true);
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+
+ mysql= lmysql;
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES(10, 'C')");
+ myerrno((uint)1142);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myerrno((uint)1142);
+
+ mysql= org_mysql;
+ rc= mysql_query(mysql, "CREATE TEMPORARY TABLE test_drop_temp_db.t1(c1 int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TEMPORARY TABLE test_drop_temp_db.t2 LIKE test_drop_temp_db.t1");
+ myquery(rc);
+
+ mysql= lmysql;
+
+ rc= mysql_query(mysql, "DROP TABLE t1, t2");
+ myquery_r(rc);
+
+ rc= mysql_query(mysql, "DROP TEMPORARY TABLE t1");
+ myquery_r(rc);
+
+ rc= mysql_query(mysql, "DROP TEMPORARY TABLE t2");
+ myquery_r(rc);
+
+ mysql_close(lmysql);
+ mysql= org_mysql;
+
+ rc= mysql_query(mysql, "drop database test_drop_temp_db");
+ myquery(rc);
+ DIE_UNLESS(1 == mysql_affected_rows(mysql));
+
+ rc= mysql_query(mysql, "delete from mysql.user where User='test_temp'");
+ myquery(rc);
+ DIE_UNLESS(1 == mysql_affected_rows(mysql));
+
+
+ rc= mysql_query(mysql, "delete from mysql.tables_priv where User='test_temp'");
+ myquery(rc);
+ DIE_UNLESS(1 == mysql_affected_rows(mysql));
+ }
+}
+#endif
+
+
+/* Test warnings for cuted rows */
+
+static void test_cuted_rows()
+{
+ int rc, count;
+ MYSQL_RES *result;
+
+ myheader("test_cuted_rows");
+
+ mysql_query(mysql, "DROP TABLE if exists t1");
+ mysql_query(mysql, "DROP TABLE if exists t2");
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(c1 tinyint)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t2(c1 int not null)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 values(10), (NULL), (NULL)");
+ myquery(rc);
+
+ count= mysql_warning_count(mysql);
+ if (!opt_silent)
+ fprintf(stdout, "\n total warnings: %d", count);
+ DIE_UNLESS(count == 0);
+
+ rc= mysql_query(mysql, "INSERT INTO t2 SELECT * FROM t1");
+ myquery(rc);
+
+ count= mysql_warning_count(mysql);
+ if (!opt_silent)
+ fprintf(stdout, "\n total warnings: %d", count);
+ DIE_UNLESS(count == 2);
+
+ rc= mysql_query(mysql, "SHOW WARNINGS");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 2);
+ mysql_free_result(result);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES('junk'), (876789)");
+ myquery(rc);
+
+ count= mysql_warning_count(mysql);
+ if (!opt_silent)
+ fprintf(stdout, "\n total warnings: %d", count);
+ DIE_UNLESS(count == 2);
+
+ rc= mysql_query(mysql, "SHOW WARNINGS");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 2);
+ mysql_free_result(result);
+}
+
+
+/* Test update/binary logs */
+
+static void test_logs()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ char data[255];
+ ulong length;
+ int rc;
+ short id;
+
+ myheader("test_logs");
+
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_logs");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_logs(id smallint, name varchar(20))");
+ myquery(rc);
+
+ strmov((char *)data, "INSERT INTO test_logs VALUES(?, ?)");
+ stmt= mysql_simple_prepare(mysql, data);
+ check_stmt(stmt);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[0].buffer= (void *)&id;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)&data;
+ my_bind[1].buffer_length= 255;
+ my_bind[1].length= &length;
+
+ id= 9876;
+ length= (ulong)(strmov((char *)data, "MySQL - Open Source Database")- data);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ strmov((char *)data, "'");
+ length= 1;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ strmov((char *)data, "\"");
+ length= 1;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ length= (ulong)(strmov((char *)data, "my\'sql\'")-data);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ length= (ulong)(strmov((char *)data, "my\"sql\"")-data);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ strmov((char *)data, "INSERT INTO test_logs VALUES(20, 'mysql')");
+ stmt= mysql_simple_prepare(mysql, data);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ strmov((char *)data, "SELECT * FROM test_logs WHERE id=?");
+ stmt= mysql_simple_prepare(mysql, data);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_bind[1].buffer_length= 255;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ {
+ fprintf(stdout, "id : %d\n", id);
+ fprintf(stdout, "name : %s(%ld)\n", data, length);
+ }
+
+ DIE_UNLESS(id == 9876);
+ DIE_UNLESS(length == 19 || length == 20); /* Due to VARCHAR(20) */
+ DIE_UNLESS(is_prefix(data, "MySQL - Open Source") == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n name : %s(%ld)", data, length);
+
+ DIE_UNLESS(length == 1);
+ DIE_UNLESS(strcmp(data, "'") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n name : %s(%ld)", data, length);
+
+ DIE_UNLESS(length == 1);
+ DIE_UNLESS(strcmp(data, "\"") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n name : %s(%ld)", data, length);
+
+ DIE_UNLESS(length == 7);
+ DIE_UNLESS(strcmp(data, "my\'sql\'") == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n name : %s(%ld)", data, length);
+
+ DIE_UNLESS(length == 7);
+ /*DIE_UNLESS(strcmp(data, "my\"sql\"") == 0); */
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE test_logs");
+ myquery(rc);
+}
+
+
+/* Test 'n' statements create and close */
+
+static void test_nstmts()
+{
+ MYSQL_STMT *stmt;
+ char query[255];
+ int rc;
+ static uint i, total_stmts= 2000;
+ MYSQL_BIND my_bind[1];
+
+ myheader("test_nstmts");
+
+ mysql_autocommit(mysql, TRUE);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_nstmts");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_nstmts(id int)");
+ myquery(rc);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= (void *)&i;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+
+ for (i= 0; i < total_stmts; i++)
+ {
+ if (!opt_silent)
+ fprintf(stdout, "\r stmt: %d", i);
+
+ strmov(query, "insert into test_nstmts values(?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+ }
+
+ stmt= mysql_simple_prepare(mysql, " select count(*) from test_nstmts");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ i= 0;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n total rows: %d", i);
+ DIE_UNLESS( i == total_stmts);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE test_nstmts");
+ myquery(rc);
+}
+
+
+/* Test stmt seek() functions */
+
+static void test_fetch_seek()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[3];
+ MYSQL_ROW_OFFSET row;
+ int rc;
+ int32 c1;
+ char c2[11], c3[20];
+
+ myheader("test_fetch_seek");
+ rc= mysql_query(mysql, "drop table if exists t1");
+
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1(c1 int primary key auto_increment, c2 char(10), c3 timestamp)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1(c2) values('venu'), ('mysql'), ('open'), ('source')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select * from t1");
+ check_stmt(stmt);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&c1;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)c2;
+ my_bind[1].buffer_length= sizeof(c2);
+
+ my_bind[2]= my_bind[1];
+ my_bind[2].buffer= (void *)c3;
+ my_bind[2].buffer_length= sizeof(c3);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 0: %ld, %s, %s", (long) c1, c2, c3);
+
+ row= mysql_stmt_row_tell(stmt);
+
+ row= mysql_stmt_row_seek(stmt, row);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 2: %ld, %s, %s", (long) c1, c2, c3);
+
+ row= mysql_stmt_row_seek(stmt, row);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 2: %ld, %s, %s", (long) c1, c2, c3);
+
+ mysql_stmt_data_seek(stmt, 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 0: %ld, %s, %s", (long) c1, c2, c3);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+/* Test mysql_stmt_fetch_column() with offset */
+
+static void test_fetch_offset()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char data[11];
+ ulong length;
+ int rc;
+ my_bool is_null;
+
+
+ myheader("test_fetch_offset");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1(a char(10))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 values('abcdefghij'), (null)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select * from t1");
+ check_stmt(stmt);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)data;
+ my_bind[0].buffer_length= 11;
+ my_bind[0].is_null= &is_null;
+ my_bind[0].length= &length;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute_r(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ data[0]= '\0';
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 1: %s (%ld)", data, length);
+ DIE_UNLESS(strncmp(data, "abcd", 4) == 0 && length == 10);
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 5);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 1: %s (%ld)", data, length);
+ DIE_UNLESS(strncmp(data, "fg", 2) == 0 && length == 10);
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 9);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 0: %s (%ld)", data, length);
+ DIE_UNLESS(strncmp(data, "j", 1) == 0 && length == 10);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ is_null= 0;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(is_null == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0);
+ check_execute_r(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+/* Test mysql_stmt_fetch_column() */
+
+static void test_fetch_column()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ char c2[20], bc2[20];
+ ulong l1, l2, bl1, bl2;
+ int rc, c1, bc1;
+
+ myheader("test_fetch_column");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1(c1 int primary key auto_increment, c2 char(10))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1(c2) values('venu'), ('mysql')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select * from t1 order by c2 desc");
+ check_stmt(stmt);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&bc1;
+ my_bind[0].buffer_length= 0;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &bl1;
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)bc2;
+ my_bind[1].buffer_length= 7;
+ my_bind[1].is_null= 0;
+ my_bind[1].length= &bl2;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0); /* No-op at this point */
+ check_execute_r(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 0: %d, %s", bc1, bc2);
+
+ c2[0]= '\0'; l2= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)c2;
+ my_bind[0].buffer_length= 7;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l2;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 1: %s(%ld)", c2, l2);
+ DIE_UNLESS(strncmp(c2, "venu", 4) == 0 && l2 == 4);
+
+ c2[0]= '\0'; l2= 0;
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 1: %s(%ld)", c2, l2);
+ DIE_UNLESS(strcmp(c2, "venu") == 0 && l2 == 4);
+
+ c1= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&c1;
+ my_bind[0].buffer_length= 0;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l1;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 0: %d(%ld)", c1, l1);
+ DIE_UNLESS(c1 == 1 && l1 == 4);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n row 1: %d, %s", bc1, bc2);
+
+ c2[0]= '\0'; l2= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)c2;
+ my_bind[0].buffer_length= 7;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l2;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 1: %s(%ld)", c2, l2);
+ DIE_UNLESS(strncmp(c2, "mysq", 4) == 0 && l2 == 5);
+
+ c2[0]= '\0'; l2= 0;
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 1: %si(%ld)", c2, l2);
+ DIE_UNLESS(strcmp(c2, "mysql") == 0 && l2 == 5);
+
+ c1= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&c1;
+ my_bind[0].buffer_length= 0;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l1;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 0: %d(%ld)", c1, l1);
+ DIE_UNLESS(c1 == 2 && l1 == 4);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0);
+ check_execute_r(stmt, rc);
+
+ mysql_stmt_close(stmt);
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+/* Test mysql_list_fields() */
+
+static void test_list_fields()
+{
+ MYSQL_RES *result;
+ int rc;
+ myheader("test_list_fields");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1(c1 int primary key auto_increment, c2 char(10) default 'mysql')");
+ myquery(rc);
+
+ result= mysql_list_fields(mysql, "t1", NULL);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+
+ verify_prepare_field(result, 0, "c1", "c1", MYSQL_TYPE_LONG,
+ "t1", "t1",
+ current_db, 11, "0");
+
+ verify_prepare_field(result, 1, "c2", "c2", MYSQL_TYPE_STRING,
+ "t1", "t1",
+ current_db, 10, "mysql");
+
+ mysql_free_result(result);
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+/* Test mysql_list_fields() with information_schema */
+
+static void test_list_information_schema_fields()
+{
+ MYSQL_RES *result;
+ int rc;
+ myheader("test_list_information_schema_fields");
+
+ rc= mysql_select_db(mysql, "information_schema");
+ myquery(rc);
+ result= mysql_list_fields(mysql, "all_plugins", NULL);
+ mytest(result);
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+ mysql_free_result(result);
+ rc= mysql_select_db(mysql, current_db);
+ myquery(rc);
+}
+
+
+static void test_list_fields_blob()
+{
+ MYSQL_RES *result;
+ int rc;
+ myheader("test_list_fields_blob");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1(c1 tinyblob, c2 blob, c3 mediumblob, c4 longblob)");
+ myquery(rc);
+
+ result= mysql_list_fields(mysql, "t1", NULL);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+
+ /*
+ All BLOB variant Fields are displayed as MYSQL_TYPE_BLOB in
+ the result set metadata. Note, some Items display the exact
+ BLOB type. This inconsistency should be fixed eventually.
+ */
+ verify_prepare_field(result, 0, "c1", "c1", MYSQL_TYPE_BLOB,
+ "t1", "t1",
+ current_db, 255, NULL);
+
+ verify_prepare_field(result, 1, "c2", "c2", MYSQL_TYPE_BLOB,
+ "t1", "t1",
+ current_db, 65535, NULL);
+
+ verify_prepare_field(result, 2, "c3", "c3", MYSQL_TYPE_BLOB,
+ "t1", "t1",
+ current_db, 16777215, NULL);
+
+ verify_prepare_field(result, 3, "c4", "c4", MYSQL_TYPE_BLOB,
+ "t1", "t1",
+ current_db, 4294967295ULL, NULL);
+
+ mysql_free_result(result);
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+static void test_list_fields_default()
+{
+ int rc, i;
+ myheader("test_list_fields_default");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE TABLE t1 ("
+ " i1 INT NOT NULL DEFAULT 0,"
+ " i3 BIGINT UNSIGNED NOT NULL DEFAULT 0xFFFFFFFFFFFFFFFF,"
+ " s1 VARCHAR(10) CHARACTER SET latin1 NOT NULL DEFAULT 's1def',"
+ " d1 DECIMAL(31,1) NOT NULL DEFAULT 111111111122222222223333333333.9,"
+ " t1 DATETIME(6) NOT NULL DEFAULT '2001-01-01 10:20:30.123456',"
+ " e1 ENUM('a','b') NOT NULL DEFAULT 'a'"
+ ")");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS v1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE VIEW v1 AS SELECT * FROM t1");
+ myquery(rc);
+
+ /*
+ Checking that mysql_list_fields() returns the same result
+ for a TABLE and a VIEW on the same table.
+ */
+ for (i= 0; i < 2; i++)
+ {
+ const char *table_name= i == 0 ? "t1" : "v1";
+ MYSQL_RES *result= mysql_list_fields(mysql, table_name, NULL);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+
+ verify_prepare_field(result, 0, "i1", "i1", MYSQL_TYPE_LONG,
+ table_name, table_name, current_db,
+ 11, "0");
+
+ verify_prepare_field(result, 1, "i3", "i3", MYSQL_TYPE_LONGLONG,
+ table_name, table_name, current_db,
+ 20, "18446744073709551615");
+
+ verify_prepare_field(result, 2, "s1", "s1", MYSQL_TYPE_VAR_STRING,
+ table_name, table_name, current_db,
+ 10, "s1def");
+
+ verify_prepare_field(result, 3, "d1", "d1", MYSQL_TYPE_NEWDECIMAL,
+ table_name, table_name, current_db,
+ 33, "111111111122222222223333333333.9");
+
+ verify_prepare_field(result, 4, "t1", "t1", MYSQL_TYPE_DATETIME,
+ table_name, table_name, current_db,
+ 26, "2001-01-01 10:20:30.123456");
+
+ verify_prepare_field(result, 5, "e1", "e1", MYSQL_TYPE_STRING,
+ table_name, table_name, current_db,
+ 1, "a");
+
+ mysql_free_result(result);
+ }
+
+ myquery(mysql_query(mysql, "DROP VIEW v1"));
+ myquery(mysql_query(mysql, "DROP TABLE t1"));
+}
+
+
+/**
+ Note, this test covers MDEV-18408 and MDEV-18685
+*/
+
+static void test_mdev18408()
+{
+ MYSQL_RES *result;
+ int rc;
+ myheader("test_mdev18408s");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS v1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (c1 TIMESTAMP NULL DEFAULT NULL)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE VIEW v1 AS SELECT c1 FROM t1");
+ myquery(rc);
+
+ result= mysql_list_fields(mysql, "v1", NULL);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+
+ verify_prepare_field(result, 0, "c1", "c1", MYSQL_TYPE_TIMESTAMP,
+ "v1", "v1", current_db, 19, 0);
+
+ mysql_free_result(result);
+ myquery(mysql_query(mysql, "DROP VIEW v1"));
+ myquery(mysql_query(mysql, "DROP TABLE t1"));
+}
+
+
+static void test_bug19671()
+{
+ MYSQL_RES *result;
+ int rc;
+ myheader("test_bug19671");
+
+ mysql_query(mysql, "set sql_mode=''");
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "drop view if exists v1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1(f1 int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create view v1 as select va.* from t1 va");
+ myquery(rc);
+
+ result= mysql_list_fields(mysql, "v1", NULL);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 0);
+
+ verify_prepare_field(result, 0, "f1", "f1", MYSQL_TYPE_LONG,
+ "v1", "v1", current_db, 11, NULL);
+
+ mysql_free_result(result);
+ myquery(mysql_query(mysql, "drop view v1"));
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+/* Test a memory ovverun bug */
+
+static void test_mem_overun()
+{
+ char buffer[10000], field[10];
+ MYSQL_STMT *stmt;
+ MYSQL_RES *field_res;
+ int rc, length;
+ unsigned i;
+
+ myheader("test_mem_overun");
+
+ /*
+ Test a memory ovverun bug when a table had 1000 fields with
+ a row of data
+ */
+ rc= mysql_query(mysql, "drop table if exists t_mem_overun");
+ myquery(rc);
+
+ strxmov(buffer, "create table t_mem_overun(", NullS);
+ for (i= 0; i < 1000; i++)
+ {
+ sprintf(field, "c%u int", i);
+ strxmov(buffer, buffer, field, ", ", NullS);
+ }
+ length= strlen(buffer);
+ buffer[length-2]= ')';
+ buffer[--length]= '\0';
+
+ rc= mysql_real_query(mysql, buffer, length);
+ myquery(rc);
+
+ strxmov(buffer, "insert into t_mem_overun values(", NullS);
+ for (i= 0; i < 1000; i++)
+ {
+ strxmov(buffer, buffer, "1, ", NullS);
+ }
+ length= strlen(buffer);
+ buffer[length-2]= ')';
+ buffer[--length]= '\0';
+
+ rc= mysql_real_query(mysql, buffer, length);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "select * from t_mem_overun");
+ myquery(rc);
+
+ rc= my_process_result(mysql);
+ DIE_UNLESS(rc == 1);
+
+ stmt= mysql_simple_prepare(mysql, "select * from t_mem_overun");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ field_res= mysql_stmt_result_metadata(stmt);
+ mytest(field_res);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total fields : %d", mysql_num_fields(field_res));
+ DIE_UNLESS( 1000 == mysql_num_fields(field_res));
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_free_result(field_res);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test mysql_stmt_free_result() */
+
+static void test_free_result()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char c2[5];
+ ulong bl1, l2;
+ int rc, c1, bc1;
+
+ myheader("test_free_result");
+
+ rc= mysql_query(mysql, "drop table if exists test_free_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table test_free_result("
+ "c1 int primary key auto_increment)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into test_free_result values(), (), ()");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select * from test_free_result");
+ check_stmt(stmt);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&bc1;
+ my_bind[0].length= &bl1;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ c2[0]= '\0'; l2= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)c2;
+ my_bind[0].buffer_length= 7;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l2;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 0: %s(%ld)", c2, l2);
+ DIE_UNLESS(strncmp(c2, "1", 1) == 0 && l2 == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ c1= 0, l2= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&c1;
+ my_bind[0].buffer_length= 0;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l2;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 0: %d(%ld)", c1, l2);
+ DIE_UNLESS(c1 == 2 && l2 == 4);
+
+ rc= mysql_query(mysql, "drop table test_free_result");
+ myquery_r(rc); /* error should be, COMMANDS OUT OF SYNC */
+
+ rc= mysql_stmt_free_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "drop table test_free_result");
+ myquery(rc); /* should be successful */
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test mysql_stmt_store_result() */
+
+static void test_free_store_result()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char c2[5];
+ ulong bl1, l2;
+ int rc, c1, bc1;
+
+ myheader("test_free_store_result");
+
+ rc= mysql_query(mysql, "drop table if exists test_free_result");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table test_free_result(c1 int primary key auto_increment)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into test_free_result values(), (), ()");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select * from test_free_result");
+ check_stmt(stmt);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&bc1;
+ my_bind[0].buffer_length= 0;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &bl1;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ c2[0]= '\0'; l2= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)c2;
+ my_bind[0].buffer_length= 7;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l2;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 1: %s(%ld)", c2, l2);
+ DIE_UNLESS(strncmp(c2, "1", 1) == 0 && l2 == 1);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ c1= 0, l2= 0;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&c1;
+ my_bind[0].buffer_length= 0;
+ my_bind[0].is_null= 0;
+ my_bind[0].length= &l2;
+
+ rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "\n col 0: %d(%ld)", c1, l2);
+ DIE_UNLESS(c1 == 2 && l2 == 4);
+
+ rc= mysql_stmt_free_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "drop table test_free_result");
+ myquery(rc);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test SQLmode */
+
+static void test_sqlmode()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ char c1[5], c2[5];
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_sqlmode");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_piping");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_piping(name varchar(10))");
+ myquery(rc);
+
+ /* PIPES_AS_CONCAT */
+ strmov(query, "SET SQL_MODE= \"PIPES_AS_CONCAT\"");
+ if (!opt_silent)
+ fprintf(stdout, "\n With %s", query);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_piping VALUES(?||?)");
+ if (!opt_silent)
+ fprintf(stdout, "\n query: %s", query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n total parameters: %ld", mysql_stmt_param_count(stmt));
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)c1;
+ my_bind[0].buffer_length= 2;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)c2;
+ my_bind[1].buffer_length= 3;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ strmov(c1, "My"); strmov(c2, "SQL");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+
+ verify_col_data("test_piping", "name", "MySQL");
+
+ rc= mysql_query(mysql, "DELETE FROM test_piping");
+ myquery(rc);
+
+ strmov(query, "SELECT connection_id ()");
+ if (!opt_silent)
+ fprintf(stdout, "\n query: %s", query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ mysql_stmt_close(stmt);
+
+ /* ANSI */
+ strmov(query, "SET SQL_MODE= \"ANSI\"");
+ if (!opt_silent)
+ fprintf(stdout, "\n With %s", query);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_piping VALUES(?||?)");
+ if (!opt_silent)
+ fprintf(stdout, "\n query: %s", query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ if (!opt_silent)
+ fprintf(stdout, "\n total parameters: %ld", mysql_stmt_param_count(stmt));
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ strmov(c1, "My"); strmov(c2, "SQL");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+ verify_col_data("test_piping", "name", "MySQL");
+
+ /* ANSI mode spaces ... */
+ strmov(query, "SELECT connection_id ()");
+ if (!opt_silent)
+ fprintf(stdout, "\n query: %s", query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ if (!opt_silent)
+ fprintf(stdout, "\n returned 1 row\n");
+
+ mysql_stmt_close(stmt);
+
+ /* IGNORE SPACE MODE */
+ strmov(query, "SET SQL_MODE= \"IGNORE_SPACE\"");
+ if (!opt_silent)
+ fprintf(stdout, "\n With %s", query);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ strmov(query, "SELECT connection_id ()");
+ if (!opt_silent)
+ fprintf(stdout, "\n query: %s", query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ if (!opt_silent)
+ fprintf(stdout, "\n returned 1 row");
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Test for timestamp handling */
+
+static void test_ts()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[6];
+ MYSQL_TIME ts;
+ MYSQL_RES *prep_res;
+ char strts[30];
+ ulong length;
+ int rc, field_count;
+ char name;
+ char query[MAX_TEST_QUERY_LENGTH];
+ const char *queries [3]= {"SELECT a, b, c FROM test_ts WHERE %c=?",
+ "SELECT a, b, c FROM test_ts WHERE %c=CAST(? AS TIME)",
+ "SELECT a, b, c FROM test_ts WHERE %c=CAST(? AS DATE)"};
+ myheader("test_ts");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ts");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_ts(a DATE, b TIME, c TIMESTAMP)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO test_ts VALUES(?, ?, ?), (?, ?, ?)");
+ check_stmt(stmt);
+
+ ts.year= 2003;
+ ts.month= 07;
+ ts.day= 12;
+ ts.hour= 21;
+ ts.minute= 07;
+ ts.second= 46;
+ ts.second_part= 0;
+ length= (long)(strmov(strts, "2003-07-12 21:07:46") - strts);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_TIMESTAMP;
+ my_bind[0].buffer= (void *)&ts;
+ my_bind[0].buffer_length= sizeof(ts);
+
+ my_bind[2]= my_bind[1]= my_bind[0];
+
+ my_bind[3].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[3].buffer= (void *)strts;
+ my_bind[3].buffer_length= sizeof(strts);
+ my_bind[3].length= &length;
+
+ my_bind[5]= my_bind[4]= my_bind[3];
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ verify_col_data("test_ts", "a", "2003-07-12");
+ verify_col_data("test_ts", "b", "21:07:46");
+ verify_col_data("test_ts", "c", "2003-07-12 21:07:46");
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM test_ts");
+ check_stmt(stmt);
+
+ prep_res= mysql_stmt_result_metadata(stmt);
+ mytest(prep_res);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 2);
+ field_count= mysql_num_fields(prep_res);
+
+ mysql_free_result(prep_res);
+ mysql_stmt_close(stmt);
+
+ for (name= 'a'; field_count--; name++)
+ {
+ int row_count= 0;
+
+ sprintf(query, queries[field_count], name);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n %s", query);
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ while (mysql_stmt_fetch(stmt) == 0)
+ row_count++;
+
+ if (!opt_silent)
+ fprintf(stdout, "\n returned '%d' rows", row_count);
+ DIE_UNLESS(row_count == 2);
+ mysql_stmt_close(stmt);
+ }
+}
+
+
+/* Test for bug #1500. */
+
+static void test_bug1500()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[3];
+ int rc;
+ int32 int_data[3]= {2, 3, 4};
+ const char *data;
+
+ myheader("test_bug1500");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bg1500");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bg1500 (i INT)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO test_bg1500 VALUES (1), (2)");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT i FROM test_bg1500 WHERE i IN (?, ?, ?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 3);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= (void *)int_data;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[2]= my_bind[1]= my_bind[0];
+ my_bind[1].buffer= (void *)(int_data + 1);
+ my_bind[2].buffer= (void *)(int_data + 2);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE test_bg1500");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bg1500 (s VARCHAR(25), FULLTEXT(s)) engine=MyISAM");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "INSERT INTO test_bg1500 VALUES ('Gravedigger'), ('Greed'), ('Hollow Dogs')");
+ myquery(rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql,
+ "SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (?)");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 1);
+
+ data= "Dogs";
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *) data;
+ my_bind[0].buffer_length= strlen(data);
+ my_bind[0].is_null= 0;
+ my_bind[0].length= 0;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+
+ /* This should work too */
+ stmt= mysql_simple_prepare(mysql,
+ "SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (CONCAT(?, 'digger'))");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 1);
+
+ data= "Grave";
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *) data;
+ my_bind[0].buffer_length= strlen(data);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug1946()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *query= "INSERT INTO prepare_command VALUES (?)";
+
+ myheader("test_bug1946");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS prepare_command");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE prepare_command(ID INT)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ rc= mysql_real_query(mysql, query, strlen(query));
+ DIE_UNLESS(rc != 0);
+ if (!opt_silent)
+ fprintf(stdout, "Got error (as expected):\n");
+ myerror(NULL);
+
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "DROP TABLE prepare_command");
+}
+
+
+static void test_parse_error_and_bad_length()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+
+ /* check that we get 4 syntax errors over the 4 calls */
+ myheader("test_parse_error_and_bad_length");
+
+ rc= mysql_query(mysql, "SHOW DATABAAAA");
+ DIE_UNLESS(rc);
+ if (!opt_silent)
+ fprintf(stdout, "Got error (as expected): '%s'\n", mysql_error(mysql));
+ rc= mysql_real_query(mysql, STRING_WITH_LEN("SHOW DATABASES\0AAAAAAAA"));
+ DIE_UNLESS(rc);
+ if (!opt_silent)
+ fprintf(stdout, "Got error (as expected): '%s'\n", mysql_error(mysql));
+
+ stmt= mysql_simple_prepare(mysql, "SHOW DATABAAAA");
+ DIE_UNLESS(!stmt);
+ if (!opt_silent)
+ fprintf(stdout, "Got error (as expected): '%s'\n", mysql_error(mysql));
+ stmt= mysql_stmt_init(mysql);
+ DIE_UNLESS(stmt);
+ rc= mysql_stmt_prepare(stmt, STRING_WITH_LEN("SHOW DATABASES\0AAAAAAA"));
+ DIE_UNLESS(rc != 0);
+ if (!opt_silent)
+ fprintf(stdout, "Got error (as expected): '%s'\n", mysql_stmt_error(stmt));
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug2247()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *res;
+ int rc;
+ int i;
+ const char *create= "CREATE TABLE bug2247(id INT UNIQUE AUTO_INCREMENT)";
+ const char *insert= "INSERT INTO bug2247 VALUES (NULL)";
+ const char *SELECT= "SELECT id FROM bug2247";
+ const char *update= "UPDATE bug2247 SET id=id+10";
+ const char *drop= "DROP TABLE IF EXISTS bug2247";
+ ulonglong exp_count;
+ enum { NUM_ROWS= 5 };
+
+ myheader("test_bug2247");
+
+ if (!opt_silent)
+ fprintf(stdout, "\nChecking if stmt_affected_rows is not affected by\n"
+ "mysql_query ... ");
+ /* create table and insert few rows */
+ rc= mysql_query(mysql, drop);
+ myquery(rc);
+
+ rc= mysql_query(mysql, create);
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, insert);
+ check_stmt(stmt);
+ for (i= 0; i < NUM_ROWS; ++i)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+ exp_count= mysql_stmt_affected_rows(stmt);
+ DIE_UNLESS(exp_count == 1);
+
+ rc= mysql_query(mysql, SELECT);
+ myquery(rc);
+ /*
+ mysql_store_result overwrites mysql->affected_rows. Check that
+ mysql_stmt_affected_rows() returns the same value, whereas
+ mysql_affected_rows() value is correct.
+ */
+ res= mysql_store_result(mysql);
+ mytest(res);
+
+ DIE_UNLESS(mysql_affected_rows(mysql) == NUM_ROWS);
+ DIE_UNLESS(exp_count == mysql_stmt_affected_rows(stmt));
+
+ rc= mysql_query(mysql, update);
+ myquery(rc);
+ DIE_UNLESS(mysql_affected_rows(mysql) == NUM_ROWS);
+ DIE_UNLESS(exp_count == mysql_stmt_affected_rows(stmt));
+
+ mysql_free_result(res);
+ mysql_stmt_close(stmt);
+
+ /* check that mysql_stmt_store_result modifies mysql_stmt_affected_rows */
+ stmt= mysql_simple_prepare(mysql, SELECT);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+ exp_count= mysql_stmt_affected_rows(stmt);
+ DIE_UNLESS(exp_count == NUM_ROWS);
+
+ rc= mysql_query(mysql, insert);
+ myquery(rc);
+ DIE_UNLESS(mysql_affected_rows(mysql) == 1);
+ DIE_UNLESS(mysql_stmt_affected_rows(stmt) == exp_count);
+
+ mysql_stmt_close(stmt);
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+}
+
+
+static void test_subqueries()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query= "SELECT (SELECT SUM(a+b) FROM t2 where t1.b=t2.b GROUP BY t1.a LIMIT 1) as scalar_s, exists (select 1 from t2 where t2.a/2=t1.a) as exists_s, a in (select a+3 from t2) as in_s, (a-1, b-1) in (select a, b from t2) as in_row_s FROM t1, (select a x, b y from t2) tt WHERE x=a";
+
+ myheader("test_subqueries");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int , b int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t2 select * from t1;");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 5);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1, t2");
+ myquery(rc);
+}
+
+
+static void test_bad_union()
+{
+ MYSQL_STMT *stmt;
+ const char *query= "SELECT 1, 2 union SELECT 1";
+
+ myheader("test_bad_union");
+
+ stmt= mysql_simple_prepare(mysql, query);
+ DIE_UNLESS(stmt == 0);
+ myerror(NULL);
+}
+
+
+static void test_distinct()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query=
+ "SELECT 2+count(distinct b), group_concat(a) FROM t1 group by a";
+
+ myheader("test_distinct");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int , b int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), \
+(1, 10), (2, 20), (3, 30), (4, 40), (5, 50);");
+ myquery(rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 5);
+ mysql_stmt_close(stmt);
+ }
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+/*
+ Test for bug#2248 "mysql_fetch without prior mysql_stmt_execute hangs"
+*/
+
+static void test_bug2248()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *query1= "SELECT DATABASE()";
+ const char *query2= "INSERT INTO test_bug2248 VALUES (10)";
+
+ myheader("test_bug2248");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bug2248");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_bug2248 (id int)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query1);
+ check_stmt(stmt);
+
+ /* This should not hang */
+ rc= mysql_stmt_fetch(stmt);
+ check_execute_r(stmt, rc);
+
+ /* And this too */
+ rc= mysql_stmt_store_result(stmt);
+ check_execute_r(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, query2);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* This too should not hang but should return proper error */
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 1);
+
+ /* This too should not hang but should not bark */
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ /* This should return proper error */
+ rc= mysql_stmt_fetch(stmt);
+ check_execute_r(stmt, rc);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE test_bug2248");
+ myquery(rc);
+}
+
+
+static void test_subqueries_ref()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query= "SELECT a as ccc from t1 outr where a+1=(SELECT 1+outr.a from t1 where outr.a+1=a+1 and a=1)";
+
+ myheader("test_subqueries_ref");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "insert into t1 values (1), (2), (3), (4), (5);");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_union()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+
+ myheader("test_union");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE TABLE t1 "
+ "(id INTEGER NOT NULL PRIMARY KEY, "
+ " name VARCHAR(20) NOT NULL)");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "INSERT INTO t1 (id, name) VALUES "
+ "(2, 'Ja'), (3, 'Ede'), "
+ "(4, 'Haag'), (5, 'Kabul'), "
+ "(6, 'Almere'), (7, 'Utrecht'), "
+ "(8, 'Qandahar'), (9, 'Amsterdam'), "
+ "(10, 'Amersfoort'), (11, 'Constantine')");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "CREATE TABLE t2 "
+ "(id INTEGER NOT NULL PRIMARY KEY, "
+ " name VARCHAR(20) NOT NULL)");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "INSERT INTO t2 (id, name) VALUES "
+ "(4, 'Guam'), (5, 'Aruba'), "
+ "(6, 'Angola'), (7, 'Albania'), "
+ "(8, 'Anguilla'), (9, 'Argentina'), "
+ "(10, 'Azerbaijan'), (11, 'Afghanistan'), "
+ "(12, 'Burkina Faso'), (13, 'Faroe Islands')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql,
+ "SELECT t1.name FROM t1 UNION "
+ "SELECT t2.name FROM t2");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 20);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1, t2");
+ myquery(rc);
+}
+
+
+static void test_bug3117()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND buffer;
+ longlong lii;
+ ulong length;
+ my_bool is_null;
+ int rc;
+
+ myheader("test_bug3117");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (id int auto_increment primary key)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT LAST_INSERT_ID()");
+ check_stmt(stmt);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES (NULL)");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) &buffer, sizeof(buffer));
+ buffer.buffer_type= MYSQL_TYPE_LONGLONG;
+ buffer.buffer_length= sizeof(lii);
+ buffer.buffer= (void *)&lii;
+ buffer.length= &length;
+ buffer.is_null= &is_null;
+
+ rc= mysql_stmt_bind_result(stmt, &buffer);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(is_null == 0 && lii == 1);
+ if (!opt_silent)
+ fprintf(stdout, "\n\tLAST_INSERT_ID()= 1 ok\n");
+
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES (NULL)");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(is_null == 0 && lii == 2);
+ if (!opt_silent)
+ fprintf(stdout, "\tLAST_INSERT_ID()= 2 ok\n");
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_join()
+{
+ MYSQL_STMT *stmt;
+ int rc, i, j;
+ const char *query[]= {"SELECT * FROM t2 join t1 on (t1.a=t2.a)",
+ "SELECT * FROM t2 natural join t1",
+ "SELECT * FROM t2 join t1 using(a)",
+ "SELECT * FROM t2 left join t1 on(t1.a=t2.a)",
+ "SELECT * FROM t2 natural left join t1",
+ "SELECT * FROM t2 left join t1 using(a)",
+ "SELECT * FROM t2 right join t1 on(t1.a=t2.a)",
+ "SELECT * FROM t2 natural right join t1",
+ "SELECT * FROM t2 right join t1 using(a)"};
+
+ myheader("test_join");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int , b int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t2 (a int , c int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "insert into t2 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);");
+ myquery(rc);
+
+ for (j= 0; j < 9; j++)
+ {
+ stmt= mysql_simple_prepare(mysql, query[j]);
+ check_stmt(stmt);
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 5);
+ }
+ mysql_stmt_close(stmt);
+ }
+
+ rc= mysql_query(mysql, "DROP TABLE t1, t2");
+ myquery(rc);
+}
+
+
+static void test_selecttmp()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query= "select a, (select count(distinct t1.b) as sum from t1, t2 where t1.a=t2.a and t2.b > 0 and t1.a <= t3.b group by t1.a order by sum limit 1) from t3";
+
+ myheader("test_select_tmp");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2, t3");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int , b int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t2 (a int, b int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t3 (a int, b int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "insert into t1 values (0, 100), (1, 2), (1, 3), (2, 2), (2, 7), \
+(2, -1), (3, 10);");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "insert into t2 values (0, 0), (1, 1), (2, 1), (3, 1), (4, 1);");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "insert into t3 values (3, 3), (2, 2), (1, 1);");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 3);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1, t2, t3");
+ myquery(rc);
+}
+
+
+static void test_create_drop()
+{
+ MYSQL_STMT *stmt_create, *stmt_drop, *stmt_select, *stmt_create_select;
+ char *query;
+ int rc, i;
+ myheader("test_table_manipulation");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t2 (a int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (a int);");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t2 values (3), (2), (1);");
+ myquery(rc);
+
+ query= (char*)"create table t1 (a int)";
+ stmt_create= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_create);
+
+ query= (char*)"drop table t1";
+ stmt_drop= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_drop);
+
+ query= (char*)"select a in (select a from t2) from t1";
+ stmt_select= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_select);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ query= (char*)"create table t1 select a from t2";
+ stmt_create_select= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_create_select);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt_create);
+ check_execute(stmt_create, rc);
+ if (!opt_silent)
+ fprintf(stdout, "created %i\n", i);
+
+ rc= mysql_stmt_execute(stmt_select);
+ check_execute(stmt_select, rc);
+ rc= my_process_stmt_result(stmt_select);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_execute(stmt_drop);
+ check_execute(stmt_drop, rc);
+ if (!opt_silent)
+ fprintf(stdout, "dropped %i\n", i);
+
+ rc= mysql_stmt_execute(stmt_create_select);
+ check_execute(stmt_create, rc);
+ if (!opt_silent)
+ fprintf(stdout, "created select %i\n", i);
+
+ rc= mysql_stmt_execute(stmt_select);
+ check_execute(stmt_select, rc);
+ rc= my_process_stmt_result(stmt_select);
+ DIE_UNLESS(rc == 3);
+
+ rc= mysql_stmt_execute(stmt_drop);
+ check_execute(stmt_drop, rc);
+ if (!opt_silent)
+ fprintf(stdout, "dropped %i\n", i);
+ }
+
+ mysql_stmt_close(stmt_create);
+ mysql_stmt_close(stmt_drop);
+ mysql_stmt_close(stmt_select);
+ mysql_stmt_close(stmt_create_select);
+
+ rc= mysql_query(mysql, "DROP TABLE t2");
+ myquery(rc);
+}
+
+
+static void test_rename()
+{
+ MYSQL_STMT *stmt;
+ const char *query= "rename table t1 to t2, t3 to t4";
+ int rc;
+ myheader("test_table_rename");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2, t3, t4");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rc= mysql_query(mysql, "create table t1 (a int)");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute_r(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "rename without t3\n");
+
+ rc= mysql_query(mysql, "create table t3 (a int)");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "rename with t3\n");
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute_r(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "rename renamed\n");
+
+ rc= mysql_query(mysql, "rename table t2 to t1, t4 to t3");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ fprintf(stdout, "rename reverted\n");
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t2, t4");
+ myquery(rc);
+}
+
+
+static void test_do_set()
+{
+ MYSQL_STMT *stmt_do, *stmt_set;
+ char *query;
+ int rc, i;
+ myheader("test_do_set");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (a int)");
+ myquery(rc);
+
+ query= (char*)"do @var:=(1 in (select * from t1))";
+ stmt_do= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_do);
+
+ query= (char*)"set @var=(1 in (select * from t1))";
+ stmt_set= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_set);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt_do);
+ check_execute(stmt_do, rc);
+ if (!opt_silent)
+ fprintf(stdout, "do %i\n", i);
+ rc= mysql_stmt_execute(stmt_set);
+ check_execute(stmt_set, rc);
+ if (!opt_silent)
+ fprintf(stdout, "set %i\n", i);
+ }
+
+ mysql_stmt_close(stmt_do);
+ mysql_stmt_close(stmt_set);
+}
+
+
+static void test_multi()
+{
+ MYSQL_STMT *stmt_delete, *stmt_update, *stmt_select1, *stmt_select2;
+ char *query;
+ MYSQL_BIND my_bind[1];
+ int rc, i;
+ int32 param= 1;
+ ulong length= 1;
+ myheader("test_multi");
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&param;
+ my_bind[0].length= &length;
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (a int, b int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t2 (a int, b int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 values (3, 3), (2, 2), (1, 1)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t2 values (3, 3), (2, 2), (1, 1)");
+ myquery(rc);
+
+ query= (char*)"delete t1, t2 from t1, t2 where t1.a=t2.a and t1.b=10";
+ stmt_delete= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_delete);
+
+ query= (char*)"update t1, t2 set t1.b=10, t2.b=10 where t1.a=t2.a and t1.b=?";
+ stmt_update= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_update);
+
+ query= (char*)"select * from t1";
+ stmt_select1= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_select1);
+
+ query= (char*)"select * from t2";
+ stmt_select2= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_select2);
+
+ for(i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_bind_param(stmt_update, my_bind);
+ check_execute(stmt_update, rc);
+
+ rc= mysql_stmt_execute(stmt_update);
+ check_execute(stmt_update, rc);
+ if (!opt_silent)
+ fprintf(stdout, "update %ld\n", (long) param);
+
+ rc= mysql_stmt_execute(stmt_delete);
+ check_execute(stmt_delete, rc);
+ if (!opt_silent)
+ fprintf(stdout, "delete %ld\n", (long) param);
+
+ rc= mysql_stmt_execute(stmt_select1);
+ check_execute(stmt_select1, rc);
+ rc= my_process_stmt_result(stmt_select1);
+ DIE_UNLESS(rc == 3-param);
+
+ rc= mysql_stmt_execute(stmt_select2);
+ check_execute(stmt_select2, rc);
+ rc= my_process_stmt_result(stmt_select2);
+ DIE_UNLESS(rc == 3-param);
+
+ param++;
+ }
+
+ mysql_stmt_close(stmt_delete);
+ mysql_stmt_close(stmt_update);
+ mysql_stmt_close(stmt_select1);
+ mysql_stmt_close(stmt_select2);
+ rc= mysql_query(mysql, "drop table t1, t2");
+ myquery(rc);
+}
+
+
+static void test_insert_select()
+{
+ MYSQL_STMT *stmt_insert, *stmt_select;
+ char *query;
+ int rc;
+ uint i;
+ myheader("test_insert_select");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (a int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t2 (a int)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t2 values (1)");
+ myquery(rc);
+
+ query= (char*)"insert into t1 select a from t2";
+ stmt_insert= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_insert);
+
+ query= (char*)"select * from t1";
+ stmt_select= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_select);
+
+ for(i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt_insert);
+ check_execute(stmt_insert, rc);
+ if (!opt_silent)
+ fprintf(stdout, "insert %u\n", i);
+
+ rc= mysql_stmt_execute(stmt_select);
+ check_execute(stmt_select, rc);
+ rc= my_process_stmt_result(stmt_select);
+ DIE_UNLESS(rc == (int)(i+1));
+ }
+
+ mysql_stmt_close(stmt_insert);
+ mysql_stmt_close(stmt_select);
+ rc= mysql_query(mysql, "drop table t1, t2");
+ myquery(rc);
+}
+
+
+static void test_bind_nagative()
+{
+ MYSQL_STMT *stmt_insert;
+ char *query;
+ int rc;
+ MYSQL_BIND my_bind[1];
+ int32 my_val= 0;
+ ulong my_length= 0L;
+ my_bool my_null= FALSE;
+ myheader("test_insert_select");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create temporary table t1 (c1 int unsigned)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1), (-1)");
+ myquery(rc);
+
+ query= (char*)"INSERT INTO t1 VALUES (?)";
+ stmt_insert= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt_insert);
+
+ /* bind parameters */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&my_val;
+ my_bind[0].length= &my_length;
+ my_bind[0].is_null= (char*)&my_null;
+
+ rc= mysql_stmt_bind_param(stmt_insert, my_bind);
+ check_execute(stmt_insert, rc);
+
+ my_val= -1;
+ rc= mysql_stmt_execute(stmt_insert);
+ check_execute(stmt_insert, rc);
+
+ mysql_stmt_close(stmt_insert);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+static void test_derived()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ MYSQL_BIND my_bind[1];
+ int32 my_val= 0;
+ ulong my_length= 0L;
+ my_bool my_null= FALSE;
+ const char *query=
+ "select count(1) from (select f.id from t1 f where f.id=?) as x";
+
+ myheader("test_derived");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (id int(8), primary key (id)) \
+ENGINE=InnoDB DEFAULT CHARSET=utf8");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 values (1)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&my_val;
+ my_bind[0].length= &my_length;
+ my_bind[0].is_null= (char*)&my_null;
+ my_val= 1;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_xjoin()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query=
+ "select t.id, p1.value, n1.value, p2.value, n2.value from t3 t LEFT JOIN t1 p1 ON (p1.id=t.param1_id) LEFT JOIN t2 p2 ON (p2.id=t.param2_id) LEFT JOIN t4 n1 ON (n1.id=p1.name_id) LEFT JOIN t4 n2 ON (n2.id=p2.name_id) where t.id=1";
+
+ myheader("test_xjoin");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2, t3, t4");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t3 (id int(8), param1_id int(8), param2_id int(8)) ENGINE=InnoDB DEFAULT CHARSET=utf8");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 ( id int(8), name_id int(8), value varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t2 (id int(8), name_id int(8), value varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t4(id int(8), value varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t3 values (1, 1, 1), (2, 2, null)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 values (1, 1, 'aaa'), (2, null, 'bbb')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t2 values (1, 2, 'ccc')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t4 values (1, 'Name1'), (2, null)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1, t2, t3, t4");
+ myquery(rc);
+}
+
+
+static void test_bug3035()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_BIND bind_array[12], *my_bind= bind_array, *bind_end= my_bind + 12;
+ int8 int8_val;
+ uint8 uint8_val;
+ int16 int16_val;
+ uint16 uint16_val;
+ int32 int32_val;
+ uint32 uint32_val;
+ longlong int64_val;
+ ulonglong uint64_val;
+ double double_val, udouble_val, double_tmp;
+ char longlong_as_string[22], ulonglong_as_string[22];
+
+ /* mins and maxes */
+ const int8 int8_min= -128;
+ const int8 int8_max= 127;
+ const uint8 uint8_min= 0;
+ const uint8 uint8_max= 255;
+
+ const int16 int16_min= -32768;
+ const int16 int16_max= 32767;
+ const uint16 uint16_min= 0;
+ const uint16 uint16_max= 65535;
+
+ const int32 int32_max= 2147483647L;
+ const int32 int32_min= -int32_max - 1;
+ const uint32 uint32_min= 0;
+ const uint32 uint32_max= 4294967295U;
+
+ /* it might not work okay everyplace */
+ const longlong int64_max= 9223372036854775807LL;
+ const longlong int64_min= -int64_max - 1;
+
+ const ulonglong uint64_min= 0U;
+ const ulonglong uint64_max= 18446744073709551615ULL;
+
+ const char *stmt_text;
+
+ myheader("test_bug3035");
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "CREATE TABLE t1 (i8 TINYINT, ui8 TINYINT UNSIGNED, "
+ "i16 SMALLINT, ui16 SMALLINT UNSIGNED, "
+ "i32 INT, ui32 INT UNSIGNED, "
+ "i64 BIGINT, ui64 BIGINT UNSIGNED, "
+ "id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ bzero((char*) bind_array, sizeof(bind_array));
+
+ for (my_bind= bind_array; my_bind < bind_end; my_bind++)
+ my_bind->error= &my_bind->error_value;
+
+ bind_array[0].buffer_type= MYSQL_TYPE_TINY;
+ bind_array[0].buffer= (void *) &int8_val;
+
+ bind_array[1].buffer_type= MYSQL_TYPE_TINY;
+ bind_array[1].buffer= (void *) &uint8_val;
+ bind_array[1].is_unsigned= 1;
+
+ bind_array[2].buffer_type= MYSQL_TYPE_SHORT;
+ bind_array[2].buffer= (void *) &int16_val;
+
+ bind_array[3].buffer_type= MYSQL_TYPE_SHORT;
+ bind_array[3].buffer= (void *) &uint16_val;
+ bind_array[3].is_unsigned= 1;
+
+ bind_array[4].buffer_type= MYSQL_TYPE_LONG;
+ bind_array[4].buffer= (void *) &int32_val;
+
+ bind_array[5].buffer_type= MYSQL_TYPE_LONG;
+ bind_array[5].buffer= (void *) &uint32_val;
+ bind_array[5].is_unsigned= 1;
+
+ bind_array[6].buffer_type= MYSQL_TYPE_LONGLONG;
+ bind_array[6].buffer= (void *) &int64_val;
+
+ bind_array[7].buffer_type= MYSQL_TYPE_LONGLONG;
+ bind_array[7].buffer= (void *) &uint64_val;
+ bind_array[7].is_unsigned= 1;
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ stmt_text= "INSERT INTO t1 (i8, ui8, i16, ui16, i32, ui32, i64, ui64) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ mysql_stmt_bind_param(stmt, bind_array);
+
+ int8_val= int8_min;
+ uint8_val= uint8_min;
+ int16_val= int16_min;
+ uint16_val= uint16_min;
+ int32_val= int32_min;
+ uint32_val= uint32_min;
+ int64_val= int64_min;
+ uint64_val= uint64_min;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ int8_val= int8_max;
+ uint8_val= uint8_max;
+ int16_val= int16_max;
+ uint16_val= uint16_max;
+ int32_val= int32_max;
+ uint32_val= uint32_max;
+ int64_val= int64_max;
+ uint64_val= uint64_max;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ stmt_text= "SELECT i8, ui8, i16, ui16, i32, ui32, i64, ui64, ui64, "
+ "cast(ui64 as signed), ui64, cast(ui64 as signed)"
+ "FROM t1 ORDER BY id ASC";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bind_array[8].buffer_type= MYSQL_TYPE_DOUBLE;
+ bind_array[8].buffer= (void *) &udouble_val;
+
+ bind_array[9].buffer_type= MYSQL_TYPE_DOUBLE;
+ bind_array[9].buffer= (void *) &double_val;
+
+ bind_array[10].buffer_type= MYSQL_TYPE_STRING;
+ bind_array[10].buffer= (void *) &ulonglong_as_string;
+ bind_array[10].buffer_length= sizeof(ulonglong_as_string);
+
+ bind_array[11].buffer_type= MYSQL_TYPE_STRING;
+ bind_array[11].buffer= (void *) &longlong_as_string;
+ bind_array[11].buffer_length= sizeof(longlong_as_string);
+
+ mysql_stmt_bind_result(stmt, bind_array);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(int8_val == int8_min);
+ DIE_UNLESS(uint8_val == uint8_min);
+ DIE_UNLESS(int16_val == int16_min);
+ DIE_UNLESS(uint16_val == uint16_min);
+ DIE_UNLESS(int32_val == int32_min);
+ DIE_UNLESS(uint32_val == uint32_min);
+ DIE_UNLESS(int64_val == int64_min);
+ DIE_UNLESS(uint64_val == uint64_min);
+ DIE_UNLESS(double_val == (longlong) uint64_min);
+ double_tmp= ulonglong2double(uint64_val);
+ DIE_UNLESS(cmp_double(&udouble_val, &double_tmp));
+ DIE_UNLESS(!strcmp(longlong_as_string, "0"));
+ DIE_UNLESS(!strcmp(ulonglong_as_string, "0"));
+
+ rc= mysql_stmt_fetch(stmt);
+
+ if (!opt_silent)
+ {
+ printf("Truncation mask: ");
+ for (my_bind= bind_array; my_bind < bind_end; my_bind++)
+ printf("%d", (int) my_bind->error_value);
+ printf("\n");
+ }
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED || rc == 0);
+
+ DIE_UNLESS(int8_val == int8_max);
+ DIE_UNLESS(uint8_val == uint8_max);
+ DIE_UNLESS(int16_val == int16_max);
+ DIE_UNLESS(uint16_val == uint16_max);
+ DIE_UNLESS(int32_val == int32_max);
+ DIE_UNLESS(uint32_val == uint32_max);
+ DIE_UNLESS(int64_val == int64_max);
+ DIE_UNLESS(uint64_val == uint64_max);
+ DIE_UNLESS(double_val == (longlong) uint64_val);
+ double_tmp= ulonglong2double(uint64_val);
+ DIE_UNLESS(cmp_double(&udouble_val, &double_tmp));
+ DIE_UNLESS(!strcmp(longlong_as_string, "-1"));
+ DIE_UNLESS(!strcmp(ulonglong_as_string, "18446744073709551615"));
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+ stmt_text= "DROP TABLE t1";
+ mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+}
+
+
+static void test_union2()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+
+ myheader("test_union2");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(col1 INT, \
+ col2 VARCHAR(40), \
+ col3 SMALLINT, \
+ col4 TIMESTAMP)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql,
+ "select col1 FROM t1 where col1=1 union distinct "
+ "select col1 FROM t1 where col1=2");
+ check_stmt(stmt);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 0);
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+/*
+ This tests for various mysql_stmt_send_long_data bugs described in #1664
+*/
+
+static void test_bug1664()
+{
+ MYSQL_STMT *stmt;
+ int rc, int_data;
+ const char *data;
+ const char *str_data= "Simple string";
+ MYSQL_BIND my_bind[2];
+ const char *query= "INSERT INTO test_long_data(col2, col1) VALUES(?, ?)";
+
+ myheader("test_bug1664");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE test_long_data(col1 int, col2 long varchar)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ verify_param_count(stmt, 2);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *)str_data;
+ my_bind[0].buffer_length= strlen(str_data);
+
+ my_bind[1].buffer= (void *)&int_data;
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ int_data= 1;
+
+ /*
+ Let us supply empty long_data. This should work and should
+ not break following execution.
+ */
+ data= "";
+ rc= mysql_stmt_send_long_data(stmt, 0, data, strlen(data));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_col_data("test_long_data", "col1", "1");
+ verify_col_data("test_long_data", "col2", "");
+
+ rc= mysql_query(mysql, "DELETE FROM test_long_data");
+ myquery(rc);
+
+ /* This should pass OK */
+ data= (char *)"Data";
+ rc= mysql_stmt_send_long_data(stmt, 0, data, strlen(data));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_col_data("test_long_data", "col1", "1");
+ verify_col_data("test_long_data", "col2", "Data");
+
+ /* clean up */
+ rc= mysql_query(mysql, "DELETE FROM test_long_data");
+ myquery(rc);
+
+ /*
+ Now we are changing int parameter and don't do anything
+ with first parameter. Second mysql_stmt_execute() should run
+ OK treating this first parameter as string parameter.
+ */
+
+ int_data= 2;
+ /* execute */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_col_data("test_long_data", "col1", "2");
+ verify_col_data("test_long_data", "col2", str_data);
+
+ /* clean up */
+ rc= mysql_query(mysql, "DELETE FROM test_long_data");
+ myquery(rc);
+
+ /*
+ Now we are sending other long data. It should not be
+ concatened to previous.
+ */
+
+ data= (char *)"SomeOtherData";
+ rc= mysql_stmt_send_long_data(stmt, 0, data, strlen(data));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_col_data("test_long_data", "col1", "2");
+ verify_col_data("test_long_data", "col2", "SomeOtherData");
+
+ mysql_stmt_close(stmt);
+
+ /* clean up */
+ rc= mysql_query(mysql, "DELETE FROM test_long_data");
+ myquery(rc);
+
+ /* Now let us test how mysql_stmt_reset works. */
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ data= (char *)"SomeData";
+ rc= mysql_stmt_send_long_data(stmt, 0, data, strlen(data));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_reset(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ verify_col_data("test_long_data", "col1", "2");
+ verify_col_data("test_long_data", "col2", str_data);
+
+ mysql_stmt_close(stmt);
+
+ /* Final clean up */
+ rc= mysql_query(mysql, "DROP TABLE test_long_data");
+ myquery(rc);
+}
+
+
+static void test_order_param()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+
+ myheader("test_order_param");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(a INT, b char(10))");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql,
+ "select sum(a) + 200, 1 from t1 "
+ " union distinct "
+ "select sum(a) + 200, 1 from t1 group by b ");
+ check_stmt(stmt);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql,
+ "select sum(a) + 200, ? from t1 group by b "
+ " union distinct "
+ "select sum(a) + 200, 1 from t1 group by b ");
+ check_stmt(stmt);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql,
+ "select sum(a) + 200, ? from t1 "
+ " union distinct "
+ "select sum(a) + 200, 1 from t1 group by b ");
+ check_stmt(stmt);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_union_param()
+{
+ MYSQL_STMT *stmt;
+ char *query;
+ int rc, i;
+ MYSQL_BIND my_bind[2];
+ char my_val[4];
+ ulong my_length= 3L;
+ my_bool my_null= FALSE;
+ myheader("test_union_param");
+
+ strmov(my_val, "abc");
+
+ query= (char*)"select ? as my_col union distinct select ?";
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ /* bind parameters */
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (char*) &my_val;
+ my_bind[0].buffer_length= 4;
+ my_bind[0].length= &my_length;
+ my_bind[0].is_null= (char*)&my_null;
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (char*) &my_val;
+ my_bind[1].buffer_length= 4;
+ my_bind[1].length= &my_length;
+ my_bind[1].is_null= (char*)&my_null;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+ }
+
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_ps_i18n()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *stmt_text;
+ MYSQL_BIND bind_array[2];
+
+ /* Represented as numbers to keep UTF8 tools from clobbering them. */
+ const char *koi8= "\xee\xd5\x2c\x20\xda\xc1\x20\xd2\xd9\xc2\xc1\xcc\xcb\xd5";
+ const char *cp1251= "\xcd\xf3\x2c\x20\xe7\xe0\x20\xf0\xfb\xe1\xe0\xeb\xea\xf3";
+ char buf1[16], buf2[16];
+ ulong buf1_len, buf2_len;
+
+
+ myheader("test_ps_i18n");
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ /*
+ Create table with binary columns, set session character set to cp1251,
+ client character set to koi8, and make sure that there is conversion
+ on insert and no conversion on select
+ */
+
+ stmt_text= "CREATE TABLE t1 (c1 VARBINARY(255), c2 VARBINARY(255))";
+
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "SET CHARACTER_SET_CLIENT=koi8r, "
+ "CHARACTER_SET_CONNECTION=cp1251, "
+ "CHARACTER_SET_RESULTS=koi8r";
+
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ bzero((char*) bind_array, sizeof(bind_array));
+
+ bind_array[0].buffer_type= MYSQL_TYPE_STRING;
+ bind_array[0].buffer= (void *) koi8;
+ bind_array[0].buffer_length= strlen(koi8);
+
+ bind_array[1].buffer_type= MYSQL_TYPE_STRING;
+ bind_array[1].buffer= (void *) koi8;
+ bind_array[1].buffer_length= strlen(koi8);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ mysql_stmt_bind_param(stmt, bind_array);
+
+ mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8));
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ stmt_text= "SELECT c1, c2 FROM t1";
+
+ /* c1 and c2 are binary so no conversion will be done on select */
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bind_array[0].buffer= buf1;
+ bind_array[0].buffer_length= sizeof(buf1);
+ bind_array[0].length= &buf1_len;
+
+ bind_array[1].buffer= buf2;
+ bind_array[1].buffer_length= sizeof(buf2);
+ bind_array[1].length= &buf2_len;
+
+ mysql_stmt_bind_result(stmt, bind_array);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(buf1_len == strlen(cp1251));
+ DIE_UNLESS(buf2_len == strlen(cp1251));
+ DIE_UNLESS(!memcmp(buf1, cp1251, buf1_len));
+ DIE_UNLESS(!memcmp(buf2, cp1251, buf1_len));
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ /*
+ Now create table with two cp1251 columns, set client character
+ set to koi8 and supply columns of one row as string and another as
+ binary data. Binary data must not be converted on insert, and both
+ columns must be converted to client character set on select.
+ */
+
+ stmt_text= "CREATE TABLE t1 (c1 VARCHAR(255) CHARACTER SET cp1251, "
+ "c2 VARCHAR(255) CHARACTER SET cp1251)";
+
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ /* this data must be converted */
+ bind_array[0].buffer_type= MYSQL_TYPE_STRING;
+ bind_array[0].buffer= (void *) koi8;
+ bind_array[0].buffer_length= strlen(koi8);
+
+ bind_array[1].buffer_type= MYSQL_TYPE_STRING;
+ bind_array[1].buffer= (void *) koi8;
+ bind_array[1].buffer_length= strlen(koi8);
+
+ mysql_stmt_bind_param(stmt, bind_array);
+
+ mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8));
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* this data must not be converted */
+ bind_array[0].buffer_type= MYSQL_TYPE_BLOB;
+ bind_array[0].buffer= (void *) cp1251;
+ bind_array[0].buffer_length= strlen(cp1251);
+
+ bind_array[1].buffer_type= MYSQL_TYPE_BLOB;
+ bind_array[1].buffer= (void *) cp1251;
+ bind_array[1].buffer_length= strlen(cp1251);
+
+ mysql_stmt_bind_param(stmt, bind_array);
+
+ mysql_stmt_send_long_data(stmt, 0, cp1251, strlen(cp1251));
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* Fetch data and verify that rows are in koi8 */
+
+ stmt_text= "SELECT c1, c2 FROM t1";
+
+ /* c1 and c2 are binary so no conversion will be done on select */
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bind_array[0].buffer= buf1;
+ bind_array[0].buffer_length= sizeof(buf1);
+ bind_array[0].length= &buf1_len;
+
+ bind_array[1].buffer= buf2;
+ bind_array[1].buffer_length= sizeof(buf2);
+ bind_array[1].length= &buf2_len;
+
+ mysql_stmt_bind_result(stmt, bind_array);
+
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
+ {
+ DIE_UNLESS(buf1_len == strlen(koi8));
+ DIE_UNLESS(buf2_len == strlen(koi8));
+ DIE_UNLESS(!memcmp(buf1, koi8, buf1_len));
+ DIE_UNLESS(!memcmp(buf2, koi8, buf1_len));
+ }
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ mysql_stmt_close(stmt);
+
+ stmt_text= "DROP TABLE t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "SET NAMES DEFAULT";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+static void test_bug3796()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ const char *concat_arg0= "concat_with_";
+ enum { OUT_BUFF_SIZE= 30 };
+ char out_buff[OUT_BUFF_SIZE];
+ char canonical_buff[OUT_BUFF_SIZE];
+ ulong out_length;
+ const char *stmt_text;
+ int rc;
+
+ myheader("test_bug3796");
+
+ /* Create and fill test table */
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "CREATE TABLE t1 (a INT, b VARCHAR(30))";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "INSERT INTO t1 VALUES(1, 'ONE'), (2, 'TWO')";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ /* Create statement handle and prepare it with select */
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT concat(?, b) FROM t1";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ /* Bind input buffers */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void *) concat_arg0;
+ my_bind[0].buffer_length= strlen(concat_arg0);
+
+ mysql_stmt_bind_param(stmt, my_bind);
+
+ /* Execute the select statement */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_bind[0].buffer= (void *) out_buff;
+ my_bind[0].buffer_length= OUT_BUFF_SIZE;
+ my_bind[0].length= &out_length;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_fetch(stmt);
+ if (!opt_silent)
+ printf("Concat result: '%s'\n", out_buff);
+ check_execute(stmt, rc);
+ strmov(canonical_buff, concat_arg0);
+ strcat(canonical_buff, "ONE");
+ DIE_UNLESS(strlen(canonical_buff) == out_length &&
+ strncmp(out_buff, canonical_buff, out_length) == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ strmov(canonical_buff + strlen(concat_arg0), "TWO");
+ DIE_UNLESS(strlen(canonical_buff) == out_length &&
+ strncmp(out_buff, canonical_buff, out_length) == 0);
+ if (!opt_silent)
+ printf("Concat result: '%s'\n", out_buff);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+static void test_bug4026()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ MYSQL_TIME time_in, time_out;
+ MYSQL_TIME datetime_in, datetime_out;
+ const char *stmt_text;
+ int rc;
+
+ myheader("test_bug4026");
+
+ /* Check that microseconds are inserted and selected successfully */
+
+ /* Create a statement handle and prepare it with select */
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT ?, ?";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ /* Bind input buffers */
+ bzero((char*) my_bind, sizeof(my_bind));
+ bzero((char*) &time_in, sizeof(time_in));
+ bzero((char*) &time_out, sizeof(time_out));
+ bzero((char*) &datetime_in, sizeof(datetime_in));
+ bzero((char*) &datetime_out, sizeof(datetime_out));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_TIME;
+ my_bind[0].buffer= (void *) &time_in;
+ my_bind[1].buffer_type= MYSQL_TYPE_DATETIME;
+ my_bind[1].buffer= (void *) &datetime_in;
+
+ time_in.hour= 23;
+ time_in.minute= 59;
+ time_in.second= 59;
+ time_in.second_part= 123456;
+ /*
+ This is not necessary, just to make DIE_UNLESS below work: this field
+ is filled in when time is received from server
+ */
+ time_in.time_type= MYSQL_TIMESTAMP_TIME;
+
+ datetime_in= time_in;
+ datetime_in.year= 2003;
+ datetime_in.month= 12;
+ datetime_in.day= 31;
+ datetime_in.time_type= MYSQL_TIMESTAMP_DATETIME;
+
+ mysql_stmt_bind_param(stmt, my_bind);
+
+ /* Execute the select statement */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_bind[0].buffer= (void *) &time_out;
+ my_bind[1].buffer= (void *) &datetime_out;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+ if (!opt_silent)
+ {
+ printf("%d:%d:%d.%lu\n", time_out.hour, time_out.minute, time_out.second,
+ time_out.second_part);
+ printf("%d-%d-%d %d:%d:%d.%lu\n", datetime_out.year, datetime_out.month,
+ datetime_out.day, datetime_out.hour,
+ datetime_out.minute, datetime_out.second,
+ datetime_out.second_part);
+ }
+ DIE_UNLESS(memcmp(&time_in, &time_out, sizeof(time_in)) == 0);
+ DIE_UNLESS(memcmp(&datetime_in, &datetime_out, sizeof(datetime_in)) == 0);
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug4079()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ const char *stmt_text;
+ uint32 res;
+ int rc;
+
+ myheader("test_bug4079");
+
+ /* Create and fill table */
+ mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ mysql_query(mysql, "CREATE TABLE t1 (a int)");
+ mysql_query(mysql, "INSERT INTO t1 VALUES (1), (2)");
+
+ /* Prepare erroneous statement */
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT 1 < (SELECT a FROM t1)";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ /* Execute the select statement */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* Bind input buffers */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *) &res;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc != 0 && rc != MYSQL_NO_DATA);
+ if (!opt_silent)
+ printf("Got error from mysql_stmt_fetch (as expected):\n%s\n",
+ mysql_stmt_error(stmt));
+ /* buggy version of libmysql hanged up here */
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug4236()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ int rc;
+ MYSQL_STMT backup;
+
+ myheader("test_bug4236");
+
+ stmt= mysql_stmt_init(mysql);
+
+ /* mysql_stmt_execute() of statement with statement id= 0 crashed server */
+ stmt_text= "SELECT 1";
+ /* We need to prepare statement to pass by possible check in libmysql */
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ /* Hack to check that server works OK if statement wasn't found */
+ backup.stmt_id= stmt->stmt_id;
+ stmt->stmt_id= 0;
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc);
+ /* Restore original statement id to be able to reprepare it */
+ stmt->stmt_id= backup.stmt_id;
+
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug4030()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[3];
+ MYSQL_TIME time_canonical, time_out;
+ MYSQL_TIME date_canonical, date_out;
+ MYSQL_TIME datetime_canonical, datetime_out;
+ const char *stmt_text;
+ int rc;
+
+ myheader("test_bug4030");
+
+ /* Check that microseconds are inserted and selected successfully */
+
+ /* Execute a query with time values in prepared mode */
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT '23:59:59.123456', '2003-12-31', "
+ "'2003-12-31 23:59:59.123456'";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* Bind output buffers */
+ bzero((char*) my_bind, sizeof(my_bind));
+ bzero((char*) &time_canonical, sizeof(time_canonical));
+ bzero((char*) &time_out, sizeof(time_out));
+ bzero((char*) &date_canonical, sizeof(date_canonical));
+ bzero((char*) &date_out, sizeof(date_out));
+ bzero((char*) &datetime_canonical, sizeof(datetime_canonical));
+ bzero((char*) &datetime_out, sizeof(datetime_out));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_TIME;
+ my_bind[0].buffer= (void *) &time_out;
+ my_bind[1].buffer_type= MYSQL_TYPE_DATE;
+ my_bind[1].buffer= (void *) &date_out;
+ my_bind[2].buffer_type= MYSQL_TYPE_DATETIME;
+ my_bind[2].buffer= (void *) &datetime_out;
+
+ time_canonical.hour= 23;
+ time_canonical.minute= 59;
+ time_canonical.second= 59;
+ time_canonical.second_part= 123456;
+ time_canonical.time_type= MYSQL_TIMESTAMP_TIME;
+
+ date_canonical.year= 2003;
+ date_canonical.month= 12;
+ date_canonical.day= 31;
+ date_canonical.time_type= MYSQL_TIMESTAMP_DATE;
+
+ datetime_canonical= time_canonical;
+ datetime_canonical.year= 2003;
+ datetime_canonical.month= 12;
+ datetime_canonical.day= 31;
+ datetime_canonical.time_type= MYSQL_TIMESTAMP_DATETIME;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+ if (!opt_silent)
+ {
+ printf("%d:%d:%d.%lu\n", time_out.hour, time_out.minute, time_out.second,
+ time_out.second_part);
+ printf("%d-%d-%d\n", date_out.year, date_out.month, date_out.day);
+ printf("%d-%d-%d %d:%d:%d.%lu\n", datetime_out.year, datetime_out.month,
+ datetime_out.day, datetime_out.hour,
+ datetime_out.minute, datetime_out.second,
+ datetime_out.second_part);
+ }
+ DIE_UNLESS(memcmp(&time_canonical, &time_out, sizeof(time_out)) == 0);
+ DIE_UNLESS(memcmp(&date_canonical, &date_out, sizeof(date_out)) == 0);
+ DIE_UNLESS(memcmp(&datetime_canonical, &datetime_out, sizeof(datetime_out)) == 0);
+ mysql_stmt_close(stmt);
+}
+
+static void test_view()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ MYSQL_BIND my_bind[1];
+ char str_data[50];
+ ulong length = 0L;
+ long is_null = 0L;
+ const char *query=
+ "SELECT COUNT(*) FROM v1 WHERE SERVERNAME=?";
+
+ myheader("test_view");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,t2,t3,v1");
+ myquery(rc);
+
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1,t2,t3");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE t1 ("
+ " SERVERGRP varchar(20) NOT NULL default '', "
+ " DBINSTANCE varchar(20) NOT NULL default '', "
+ " PRIMARY KEY (SERVERGRP)) "
+ " CHARSET=latin1 collate=latin1_bin");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE t2 ("
+ " SERVERNAME varchar(20) NOT NULL, "
+ " SERVERGRP varchar(20) NOT NULL, "
+ " PRIMARY KEY (SERVERNAME)) "
+ " CHARSET=latin1 COLLATE latin1_bin");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "CREATE TABLE t3 ("
+ " SERVERGRP varchar(20) BINARY NOT NULL, "
+ " TABNAME varchar(30) NOT NULL, MAPSTATE char(1) NOT NULL, "
+ " ACTSTATE char(1) NOT NULL , "
+ " LOCAL_NAME varchar(30) NOT NULL, "
+ " CHG_DATE varchar(8) NOT NULL default '00000000', "
+ " CHG_TIME varchar(6) NOT NULL default '000000', "
+ " MXUSER varchar(12) NOT NULL default '', "
+ " PRIMARY KEY (SERVERGRP, TABNAME, MAPSTATE, ACTSTATE, "
+ " LOCAL_NAME)) CHARSET=latin1 COLLATE latin1_bin");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE VIEW v1 AS select sql_no_cache"
+ " T0001.SERVERNAME AS SERVERNAME, T0003.TABNAME AS"
+ " TABNAME,T0003.LOCAL_NAME AS LOCAL_NAME,T0002.DBINSTANCE AS"
+ " DBINSTANCE from t2 T0001 join t1 T0002 join t3 T0003 where"
+ " ((T0002.SERVERGRP = T0001.SERVERGRP) and"
+ " (T0002.SERVERGRP = T0003.SERVERGRP)"
+ " and (T0003.MAPSTATE = _latin1'A') and"
+ " (T0003.ACTSTATE = _latin1' '))");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ strmov(str_data, "TEST");
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (char *)&str_data;
+ my_bind[0].buffer_length= 50;
+ my_bind[0].length= &length;
+ length= 4;
+ my_bind[0].is_null= (char*)&is_null;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt,rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(1 == rc);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1,t2,t3");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+}
+
+
+static void test_view_where()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query=
+ "select v1.c,v2.c from v1, v2";
+
+ myheader("test_view_where");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1,v2");
+ myquery(rc);
+
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,v2,t1");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE t1 (a int, b int)");
+ myquery(rc);
+ rc= mysql_query(mysql,"insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10)");
+ myquery(rc);
+ rc= mysql_query(mysql,"create view v1 (c) as select b from t1 where a<3");
+ myquery(rc);
+ rc= mysql_query(mysql,"create view v2 (c) as select b from t1 where a>=3");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(4 == rc);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW v1, v2");
+ myquery(rc);
+}
+
+
+static void test_view_2where()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ MYSQL_BIND my_bind[8];
+ char parms[8][100];
+ ulong length[8];
+ const char *query=
+ "select relid, report, handle, log_group, username, variant, type, "
+ "version, erfdat, erftime, erfname, aedat, aetime, aename, dependvars, "
+ "inactive from V_LTDX where mandt = ? and relid = ? and report = ? and "
+ "handle = ? and log_group = ? and username in ( ? , ? ) and type = ?";
+
+ myheader("test_view_2where");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS LTDX");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS V_LTDX");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "CREATE TABLE LTDX (MANDT char(3) NOT NULL default '000', "
+ " RELID char(2) NOT NULL, REPORT varchar(40) NOT NULL,"
+ " HANDLE varchar(4) NOT NULL, LOG_GROUP varchar(4) NOT NULL,"
+ " USERNAME varchar(12) NOT NULL,"
+ " VARIANT varchar(12) NOT NULL,"
+ " TYPE char(1) NOT NULL, SRTF2 int(11) NOT NULL,"
+ " VERSION varchar(6) NOT NULL default '000000',"
+ " ERFDAT varchar(8) NOT NULL default '00000000',"
+ " ERFTIME varchar(6) NOT NULL default '000000',"
+ " ERFNAME varchar(12) NOT NULL,"
+ " AEDAT varchar(8) NOT NULL default '00000000',"
+ " AETIME varchar(6) NOT NULL default '000000',"
+ " AENAME varchar(12) NOT NULL,"
+ " DEPENDVARS varchar(10) NOT NULL,"
+ " INACTIVE char(1) NOT NULL, CLUSTR smallint(6) NOT NULL,"
+ " CLUSTD blob,"
+ " PRIMARY KEY (MANDT, RELID, REPORT, HANDLE, LOG_GROUP, "
+ "USERNAME, VARIANT, TYPE, SRTF2))"
+ " CHARSET=latin1 COLLATE latin1_bin");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "CREATE VIEW V_LTDX AS select T0001.MANDT AS "
+ " MANDT,T0001.RELID AS RELID,T0001.REPORT AS "
+ " REPORT,T0001.HANDLE AS HANDLE,T0001.LOG_GROUP AS "
+ " LOG_GROUP,T0001.USERNAME AS USERNAME,T0001.VARIANT AS "
+ " VARIANT,T0001.TYPE AS TYPE,T0001.VERSION AS "
+ " VERSION,T0001.ERFDAT AS ERFDAT,T0001.ERFTIME AS "
+ " ERFTIME,T0001.ERFNAME AS ERFNAME,T0001.AEDAT AS "
+ " AEDAT,T0001.AETIME AS AETIME,T0001.AENAME AS "
+ " AENAME,T0001.DEPENDVARS AS DEPENDVARS,T0001.INACTIVE AS "
+ " INACTIVE from LTDX T0001 where (T0001.SRTF2 = 0)");
+ myquery(rc);
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i=0; i < 8; i++) {
+ strmov(parms[i], "1");
+ my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ my_bind[i].buffer = (char *)&parms[i];
+ my_bind[i].buffer_length = 100;
+ my_bind[i].is_null = 0;
+ my_bind[i].length = &length[i];
+ length[i] = 1;
+ }
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt,rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(0 == rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP VIEW V_LTDX");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE LTDX");
+ myquery(rc);
+}
+
+
+static void test_view_star()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ MYSQL_BIND my_bind[8];
+ char parms[8][100];
+ ulong length[8];
+ const char *query= "SELECT * FROM vt1 WHERE a IN (?,?)";
+
+ myheader("test_view_star");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, vt1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, vt1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int)");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1");
+ myquery(rc);
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < 2; i++) {
+ sprintf((char *)&parms[i], "%d", i);
+ my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ my_bind[i].buffer = (char *)&parms[i];
+ my_bind[i].buffer_length = 100;
+ my_bind[i].is_null = 0;
+ my_bind[i].length = &length[i];
+ length[i] = 1;
+ }
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt,rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(0 == rc);
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW vt1");
+ myquery(rc);
+}
+
+
+static void test_view_insert()
+{
+ MYSQL_STMT *insert_stmt, *select_stmt;
+ int rc, i;
+ MYSQL_BIND my_bind[1];
+ int my_val = 0;
+ ulong my_length = 0L;
+ long my_null = 0L;
+ const char *query=
+ "insert into v1 values (?)";
+
+ myheader("test_view_insert");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1");
+ myquery(rc);
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS t1,v1");
+ myquery(rc);
+
+ rc= mysql_query(mysql,"create table t1 (a int, primary key (a))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create view v1 as select a from t1 where a>=1");
+ myquery(rc);
+
+ insert_stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(insert_stmt, query, strlen(query));
+ check_execute(insert_stmt, rc);
+ query= "select * from t1";
+ select_stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(select_stmt, query, strlen(query));
+ check_execute(select_stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type = MYSQL_TYPE_LONG;
+ my_bind[0].buffer = (char *)&my_val;
+ my_bind[0].length = &my_length;
+ my_bind[0].is_null = (char*)&my_null;
+ rc= mysql_stmt_bind_param(insert_stmt, my_bind);
+ check_execute(insert_stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ int rowcount= 0;
+ my_val= i;
+
+ rc= mysql_stmt_execute(insert_stmt);
+ check_execute(insert_stmt, rc);
+
+ rc= mysql_stmt_execute(select_stmt);
+ check_execute(select_stmt, rc);
+ rowcount= (int)my_process_stmt_result(select_stmt);
+ DIE_UNLESS((i+1) == rowcount);
+ }
+ mysql_stmt_close(insert_stmt);
+ mysql_stmt_close(select_stmt);
+
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_left_join_view()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query=
+ "select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x);";
+
+ myheader("test_left_join_view");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1");
+ myquery(rc);
+
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE t1 (a int)");
+ myquery(rc);
+ rc= mysql_query(mysql,"insert into t1 values (1), (2), (3)");
+ myquery(rc);
+ rc= mysql_query(mysql,"create view v1 (x) as select a from t1 where a > 1");
+ myquery(rc);
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(3 == rc);
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_view_insert_fields()
+{
+ MYSQL_STMT *stmt;
+ char parm[11][1000];
+ ulong l[11];
+ int rc, i;
+ MYSQL_BIND my_bind[11];
+ const char *query= "INSERT INTO `v1` ( `K1C4` ,`K2C4` ,`K3C4` ,`K4N4` ,`F1C4` ,`F2I4` ,`F3N5` ,`F7F8` ,`F6N4` ,`F5C8` ,`F9D8` ) VALUES( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )";
+
+ myheader("test_view_insert_fields");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, v1");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "CREATE TABLE t1 (K1C4 varchar(4) NOT NULL,"
+ "K2C4 varchar(4) NOT NULL, K3C4 varchar(4) NOT NULL,"
+ "K4N4 varchar(4) NOT NULL default '0000',"
+ "F1C4 varchar(4) NOT NULL, F2I4 int(11) NOT NULL,"
+ "F3N5 varchar(5) NOT NULL default '00000',"
+ "F4I4 int(11) NOT NULL default '0', F5C8 varchar(8) NOT NULL,"
+ "F6N4 varchar(4) NOT NULL default '0000',"
+ "F7F8 double NOT NULL default '0',"
+ "F8F8 double NOT NULL default '0',"
+ "F9D8 decimal(8,2) NOT NULL default '0.00',"
+ "PRIMARY KEY (K1C4,K2C4,K3C4,K4N4)) "
+ "CHARSET=latin1 COLLATE latin1_bin");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "CREATE VIEW v1 AS select sql_no_cache "
+ " K1C4 AS K1C4, K2C4 AS K2C4, K3C4 AS K3C4, K4N4 AS K4N4, "
+ " F1C4 AS F1C4, F2I4 AS F2I4, F3N5 AS F3N5,"
+ " F7F8 AS F7F8, F6N4 AS F6N4, F5C8 AS F5C8, F9D8 AS F9D8"
+ " from t1 T0001");
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < 11; i++)
+ {
+ l[i]= 20;
+ my_bind[i].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[i].is_null= 0;
+ my_bind[i].buffer= (char *)&parm[i];
+
+ memset(parm[i], 0, sizeof parm[i]);
+ strmov(parm[i], "1");
+ my_bind[i].buffer_length= 2;
+ my_bind[i].length= &l[i];
+ }
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+
+ query= "select * from t1";
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(1 == rc);
+
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+}
+
+static void test_bug5126()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ int32 c1, c2;
+ const char *stmt_text;
+ int rc;
+
+ myheader("test_bug5126");
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "CREATE TABLE t1 (a mediumint, b int)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "INSERT INTO t1 VALUES (8386608, 1)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT a, b FROM t1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* Bind output buffers */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= &c1;
+ my_bind[1].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[1].buffer= &c2;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+ DIE_UNLESS(c1 == 8386608 && c2 == 1);
+ if (!opt_silent)
+ printf("%ld, %ld\n", (long) c1, (long) c2);
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug4231()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ MYSQL_TIME tm[2];
+ const char *stmt_text;
+ int rc;
+
+ myheader("test_bug4231");
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "CREATE TABLE t1 (a int)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "INSERT INTO t1 VALUES (1)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT a FROM t1 WHERE ? = ?";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ /* Bind input buffers */
+ bzero((char*) my_bind, sizeof(my_bind));
+ bzero((char*) tm, sizeof(tm));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_DATE;
+ my_bind[0].buffer= &tm[0];
+ my_bind[1].buffer_type= MYSQL_TYPE_DATE;
+ my_bind[1].buffer= &tm[1];
+
+ mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /*
+ First set server-side params to some non-zero non-equal values:
+ then we will check that they are not used when client sends
+ new (zero) times.
+ */
+ tm[0].time_type = MYSQL_TIMESTAMP_DATE;
+ tm[0].year = 2000;
+ tm[0].month = 1;
+ tm[0].day = 1;
+ tm[1]= tm[0];
+ --tm[1].year; /* tm[0] != tm[1] */
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+
+ /* binds are unequal, no rows should be returned */
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ /* Set one of the dates to zero */
+ tm[0].year= tm[0].month= tm[0].day= 0;
+ tm[1]= tm[0];
+ mysql_stmt_execute(stmt);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+
+ mysql_stmt_close(stmt);
+ stmt_text= "DROP TABLE t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+static void test_bug5399()
+{
+ /*
+ Ascii 97 is 'a', which gets mapped to Ascii 65 'A' unless internal
+ statement id hash in the server uses binary collation.
+ */
+#define NUM_OF_USED_STMT 97
+ MYSQL_STMT *stmt_list[NUM_OF_USED_STMT];
+ MYSQL_STMT **stmt;
+ MYSQL_BIND my_bind[1];
+ char buff[600];
+ int rc;
+ int32 no;
+
+ myheader("test_bug5399");
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= &no;
+
+ for (stmt= stmt_list; stmt != stmt_list + NUM_OF_USED_STMT; ++stmt)
+ {
+ sprintf(buff, "select %d", (int) (stmt - stmt_list));
+ *stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(*stmt, buff, strlen(buff));
+ check_execute(*stmt, rc);
+ mysql_stmt_bind_result(*stmt, my_bind);
+ }
+ if (!opt_silent)
+ printf("%d statements prepared.\n", NUM_OF_USED_STMT);
+
+ for (stmt= stmt_list; stmt != stmt_list + NUM_OF_USED_STMT; ++stmt)
+ {
+ rc= mysql_stmt_execute(*stmt);
+ check_execute(*stmt, rc);
+ rc= mysql_stmt_store_result(*stmt);
+ check_execute(*stmt, rc);
+ rc= mysql_stmt_fetch(*stmt);
+ DIE_UNLESS(rc == 0);
+ DIE_UNLESS((int32) (stmt - stmt_list) == no);
+ }
+
+ for (stmt= stmt_list; stmt != stmt_list + NUM_OF_USED_STMT; ++stmt)
+ mysql_stmt_close(*stmt);
+#undef NUM_OF_USED_STMT
+}
+
+
+static void test_bug5194()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND *my_bind;
+ char *query;
+ char *param_str;
+ int param_str_length;
+ const char *stmt_text;
+ int rc;
+ float float_array[250] =
+ {
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25,
+ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25
+ };
+ float *fa_ptr= float_array;
+ /* Number of columns per row */
+ const int COLUMN_COUNT= sizeof(float_array)/sizeof(*float_array);
+ /* Number of rows per bulk insert to start with */
+ const int MIN_ROWS_PER_INSERT= 262;
+ /* Max number of rows per bulk insert to end with */
+ const int MAX_ROWS_PER_INSERT= 300;
+ const int MAX_PARAM_COUNT= COLUMN_COUNT*MAX_ROWS_PER_INSERT;
+ const char *query_template= "insert into t1 values %s";
+ const int CHARS_PER_PARAM= 5; /* space needed to place ", ?" in the query */
+ const int uint16_max= 65535;
+ int nrows, i;
+
+ myheader("test_bug5194");
+
+ stmt_text= "drop table if exists t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+
+ stmt_text= "create table if not exists t1"
+ "(c1 float, c2 float, c3 float, c4 float, c5 float, c6 float, "
+ "c7 float, c8 float, c9 float, c10 float, c11 float, c12 float, "
+ "c13 float, c14 float, c15 float, c16 float, c17 float, c18 float, "
+ "c19 float, c20 float, c21 float, c22 float, c23 float, c24 float, "
+ "c25 float, c26 float, c27 float, c28 float, c29 float, c30 float, "
+ "c31 float, c32 float, c33 float, c34 float, c35 float, c36 float, "
+ "c37 float, c38 float, c39 float, c40 float, c41 float, c42 float, "
+ "c43 float, c44 float, c45 float, c46 float, c47 float, c48 float, "
+ "c49 float, c50 float, c51 float, c52 float, c53 float, c54 float, "
+ "c55 float, c56 float, c57 float, c58 float, c59 float, c60 float, "
+ "c61 float, c62 float, c63 float, c64 float, c65 float, c66 float, "
+ "c67 float, c68 float, c69 float, c70 float, c71 float, c72 float, "
+ "c73 float, c74 float, c75 float, c76 float, c77 float, c78 float, "
+ "c79 float, c80 float, c81 float, c82 float, c83 float, c84 float, "
+ "c85 float, c86 float, c87 float, c88 float, c89 float, c90 float, "
+ "c91 float, c92 float, c93 float, c94 float, c95 float, c96 float, "
+ "c97 float, c98 float, c99 float, c100 float, c101 float, c102 float, "
+ "c103 float, c104 float, c105 float, c106 float, c107 float, c108 float, "
+ "c109 float, c110 float, c111 float, c112 float, c113 float, c114 float, "
+ "c115 float, c116 float, c117 float, c118 float, c119 float, c120 float, "
+ "c121 float, c122 float, c123 float, c124 float, c125 float, c126 float, "
+ "c127 float, c128 float, c129 float, c130 float, c131 float, c132 float, "
+ "c133 float, c134 float, c135 float, c136 float, c137 float, c138 float, "
+ "c139 float, c140 float, c141 float, c142 float, c143 float, c144 float, "
+ "c145 float, c146 float, c147 float, c148 float, c149 float, c150 float, "
+ "c151 float, c152 float, c153 float, c154 float, c155 float, c156 float, "
+ "c157 float, c158 float, c159 float, c160 float, c161 float, c162 float, "
+ "c163 float, c164 float, c165 float, c166 float, c167 float, c168 float, "
+ "c169 float, c170 float, c171 float, c172 float, c173 float, c174 float, "
+ "c175 float, c176 float, c177 float, c178 float, c179 float, c180 float, "
+ "c181 float, c182 float, c183 float, c184 float, c185 float, c186 float, "
+ "c187 float, c188 float, c189 float, c190 float, c191 float, c192 float, "
+ "c193 float, c194 float, c195 float, c196 float, c197 float, c198 float, "
+ "c199 float, c200 float, c201 float, c202 float, c203 float, c204 float, "
+ "c205 float, c206 float, c207 float, c208 float, c209 float, c210 float, "
+ "c211 float, c212 float, c213 float, c214 float, c215 float, c216 float, "
+ "c217 float, c218 float, c219 float, c220 float, c221 float, c222 float, "
+ "c223 float, c224 float, c225 float, c226 float, c227 float, c228 float, "
+ "c229 float, c230 float, c231 float, c232 float, c233 float, c234 float, "
+ "c235 float, c236 float, c237 float, c238 float, c239 float, c240 float, "
+ "c241 float, c242 float, c243 float, c244 float, c245 float, c246 float, "
+ "c247 float, c248 float, c249 float, c250 float)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ my_bind= (MYSQL_BIND*) malloc(MAX_PARAM_COUNT * sizeof(MYSQL_BIND));
+ query= (char*) malloc(strlen(query_template) +
+ MAX_PARAM_COUNT * CHARS_PER_PARAM + 1);
+ param_str= (char*) malloc(COLUMN_COUNT * CHARS_PER_PARAM);
+
+ if (my_bind == 0 || query == 0 || param_str == 0)
+ {
+ fprintf(stderr, "Can't allocate enough memory for query structs\n");
+ if (my_bind)
+ free(my_bind);
+ if (query)
+ free(query);
+ if (param_str)
+ free(param_str);
+ return;
+ }
+
+ stmt= mysql_stmt_init(mysql);
+
+ /* setup a template for one row of parameters */
+ sprintf(param_str, "(");
+ for (i= 1; i < COLUMN_COUNT; ++i)
+ strcat(param_str, "?, ");
+ strcat(param_str, "?)");
+ param_str_length= strlen(param_str);
+
+ /* setup bind array */
+ bzero((char*) my_bind, MAX_PARAM_COUNT * sizeof(MYSQL_BIND));
+ for (i= 0; i < MAX_PARAM_COUNT; ++i)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_FLOAT;
+ my_bind[i].buffer= fa_ptr;
+ if (++fa_ptr == float_array + COLUMN_COUNT)
+ fa_ptr= float_array;
+ }
+
+ /*
+ Test each number of rows per bulk insert, so that we can see where
+ MySQL fails.
+ */
+ for (nrows= MIN_ROWS_PER_INSERT; nrows <= MAX_ROWS_PER_INSERT; ++nrows)
+ {
+ char *query_ptr;
+ /* Create statement text for current number of rows */
+ sprintf(query, query_template, param_str);
+ query_ptr= query + strlen(query);
+ for (i= 1; i < nrows; ++i)
+ {
+ memcpy(query_ptr, ", ", 2);
+ query_ptr+= 2;
+ memcpy(query_ptr, param_str, param_str_length);
+ query_ptr+= param_str_length;
+ }
+ *query_ptr= '\0';
+
+ rc= mysql_stmt_prepare(stmt, query, (ulong)(query_ptr - query));
+ if (rc && nrows * COLUMN_COUNT > uint16_max)
+ {
+ if (!opt_silent)
+ printf("Failed to prepare a statement with %d placeholders "
+ "(as expected).\n", nrows * COLUMN_COUNT);
+ break;
+ }
+ else
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ printf("Insert: query length= %d, row count= %d, param count= %lu\n",
+ (int) strlen(query), nrows, mysql_stmt_param_count(stmt));
+
+ /* bind the parameter array and execute the query */
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_reset(stmt);
+ }
+
+ mysql_stmt_close(stmt);
+ free(my_bind);
+ free(query);
+ free(param_str);
+ stmt_text= "drop table t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+static void test_bug5315()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ int rc;
+
+ myheader("test_bug5315");
+
+ stmt_text= "SELECT 1";
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ DIE_UNLESS(rc == 0);
+ if (!opt_silent)
+ printf("Executing mysql_change_user\n");
+ mysql_change_user(mysql, opt_user, opt_password, current_db);
+ if (!opt_silent)
+ printf("Executing mysql_stmt_execute\n");
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc != 0);
+ if (rc)
+ {
+ if (!opt_silent)
+ printf("Got error (as expected): '%s'\n", mysql_stmt_error(stmt));
+ }
+ /* check that connection is OK */
+ if (!opt_silent)
+ printf("Executing mysql_stmt_close\n");
+ mysql_stmt_close(stmt);
+ if (!opt_silent)
+ printf("Executing mysql_stmt_init\n");
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ DIE_UNLESS(rc == 0);
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc == 0);
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug6049()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ const char *stmt_text;
+ char buffer[30];
+ ulong length;
+ int rc;
+
+ myheader("test_bug6049");
+
+ stmt_text= "SELECT MAKETIME(-25, 12, 12)";
+
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ res= mysql_store_result(mysql);
+ row= mysql_fetch_row(res);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type = MYSQL_TYPE_STRING;
+ my_bind[0].buffer = &buffer;
+ my_bind[0].buffer_length = sizeof(buffer);
+ my_bind[0].length = &length;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+
+ if (!opt_silent)
+ {
+ printf("Result from query: %s\n", row[0]);
+ printf("Result from prepared statement: %s\n", (char*) buffer);
+ }
+
+ DIE_UNLESS(strcmp(row[0], (char*) buffer) == 0);
+
+ mysql_free_result(res);
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug6058()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ const char *stmt_text;
+ char buffer[30];
+ ulong length;
+ int rc;
+
+ myheader("test_bug6058");
+
+ rc= mysql_query(mysql, "SET SQL_MODE=''");
+ myquery(rc);
+
+ stmt_text= "SELECT CAST('0000-00-00' AS DATE)";
+
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ res= mysql_store_result(mysql);
+ row= mysql_fetch_row(res);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type = MYSQL_TYPE_STRING;
+ my_bind[0].buffer = &buffer;
+ my_bind[0].buffer_length = sizeof(buffer);
+ my_bind[0].length = &length;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+
+ if (!opt_silent)
+ {
+ printf("Result from query: %s\n", row[0]);
+ printf("Result from prepared statement: %s\n", buffer);
+ }
+
+ DIE_UNLESS(strcmp(row[0], buffer) == 0);
+
+ mysql_free_result(res);
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug6059()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+
+ myheader("test_bug6059");
+
+ stmt_text= "SELECT 'foo' INTO OUTFILE 'x.3'";
+
+ stmt= mysql_stmt_init(mysql);
+ (void) mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 0);
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_bug6046()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ int rc;
+ short b= 1;
+ MYSQL_BIND my_bind[1];
+
+ myheader("test_bug6046");
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "CREATE TABLE t1 (a int, b int)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "INSERT INTO t1 VALUES (1,1),(2,2),(3,1),(4,2)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ stmt_text= "SELECT t1.a FROM t1 NATURAL JOIN t1 as X1 "
+ "WHERE t1.b > ? ORDER BY t1.a";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ b= 1;
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer= &b;
+ my_bind[0].buffer_type= MYSQL_TYPE_SHORT;
+
+ mysql_stmt_bind_param(stmt, my_bind);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_store_result(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+}
+
+
+
+static void test_basic_cursors()
+{
+ const char *basic_tables[]=
+ {
+ "DROP TABLE IF EXISTS t1, t2",
+
+ "CREATE TABLE t1 "
+ "(id INTEGER NOT NULL PRIMARY KEY, "
+ " name VARCHAR(20) NOT NULL)",
+
+ "INSERT INTO t1 (id, name) VALUES "
+ " (2, 'Ja'), (3, 'Ede'), "
+ " (4, 'Haag'), (5, 'Kabul'), "
+ " (6, 'Almere'), (7, 'Utrecht'), "
+ " (8, 'Qandahar'), (9, 'Amsterdam'), "
+ " (10, 'Amersfoort'), (11, 'Constantine')",
+
+ "CREATE TABLE t2 "
+ "(id INTEGER NOT NULL PRIMARY KEY, "
+ " name VARCHAR(20) NOT NULL)",
+
+ "INSERT INTO t2 (id, name) VALUES "
+ " (4, 'Guam'), (5, 'Aruba'), "
+ " (6, 'Angola'), (7, 'Albania'), "
+ " (8, 'Anguilla'), (9, 'Argentina'), "
+ " (10, 'Azerbaijan'), (11, 'Afghanistan'), "
+ " (12, 'Burkina Faso'), (13, 'Faroe Islands')"
+ };
+ const char *queries[]=
+ {
+ "SELECT * FROM t1",
+ "SELECT * FROM t2"
+ };
+
+ DBUG_ENTER("test_basic_cursors");
+ myheader("test_basic_cursors");
+
+ fill_tables(basic_tables, sizeof(basic_tables)/sizeof(*basic_tables));
+
+ fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH);
+ fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT);
+ DBUG_VOID_RETURN;
+}
+
+
+static void test_cursors_with_union()
+{
+ const char *queries[]=
+ {
+ "SELECT t1.name FROM t1 UNION SELECT t2.name FROM t2",
+ "SELECT t1.id FROM t1 WHERE t1.id < 5"
+ };
+ myheader("test_cursors_with_union");
+ fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH);
+ fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT);
+}
+
+
+static void test_cursors_with_procedure()
+{
+ const char *queries[]=
+ {
+ "SELECT * FROM t1 procedure analyse()"
+ };
+ myheader("test_cursors_with_procedure");
+ fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH);
+ fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT);
+}
+
+
+/*
+ Altough mysql_create_db(), mysql_rm_db() are deprecated since 4.0 they
+ should not crash server and should not hang in case of errors.
+
+ Since those functions can't be seen in modern API (unless client library
+ was compiled with USE_OLD_FUNCTIONS define) we use simple_command() macro.
+*/
+static void test_bug6081()
+{
+ int rc;
+ myheader("test_bug6081");
+
+ rc= simple_command(mysql, COM_DROP_DB, (uchar*) current_db,
+ (ulong)strlen(current_db), 0);
+ if (rc == 0 && mysql_errno(mysql) != ER_UNKNOWN_COM_ERROR)
+ {
+ myerror(NULL); /* purecov: inspected */
+ die(__FILE__, __LINE__, "COM_DROP_DB failed"); /* purecov: inspected */
+ }
+ rc= simple_command(mysql, COM_DROP_DB, (uchar*) current_db,
+ (ulong)strlen(current_db), 0);
+ myquery_r(rc);
+ rc= simple_command(mysql, COM_CREATE_DB, (uchar*) current_db,
+ (ulong)strlen(current_db), 0);
+ if (rc == 0 && mysql_errno(mysql) != ER_UNKNOWN_COM_ERROR)
+ {
+ myerror(NULL); /* purecov: inspected */
+ die(__FILE__, __LINE__, "COM_CREATE_DB failed"); /* purecov: inspected */
+ }
+ rc= simple_command(mysql, COM_CREATE_DB, (uchar*) current_db,
+ (ulong)strlen(current_db), 0);
+ myquery_r(rc);
+ rc= mysql_select_db(mysql, current_db);
+ myquery(rc);
+}
+
+
+static void test_bug6096()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *query_result, *stmt_metadata;
+ const char *stmt_text;
+ MYSQL_BIND my_bind[12];
+ MYSQL_FIELD *query_field_list, *stmt_field_list;
+ ulong query_field_count, stmt_field_count;
+ int rc;
+ my_bool update_max_length= TRUE;
+ uint i;
+
+ myheader("test_bug6096");
+
+ stmt_text= "drop table if exists t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ mysql_query(mysql, "set sql_mode=''");
+ stmt_text= "create table t1 (c_tinyint tinyint, c_smallint smallint, "
+ " c_mediumint mediumint, c_int int, "
+ " c_bigint bigint, c_float float, "
+ " c_double double, c_varchar varchar(20), "
+ " c_char char(20), c_time time, c_date date, "
+ " c_datetime datetime)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "insert into t1 values (-100, -20000, 30000000, 4, 8, 1.0, "
+ "2.0, 'abc', 'def', now(), now(), now())";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "select * from t1";
+
+ /* Run select in prepared and non-prepared mode and compare metadata */
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ query_result= mysql_store_result(mysql);
+ query_field_list= mysql_fetch_fields(query_result);
+ query_field_count= mysql_num_fields(query_result);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH,
+ (void*) &update_max_length);
+ mysql_stmt_store_result(stmt);
+ stmt_metadata= mysql_stmt_result_metadata(stmt);
+ stmt_field_list= mysql_fetch_fields(stmt_metadata);
+ stmt_field_count= mysql_num_fields(stmt_metadata);
+ DIE_UNLESS(stmt_field_count == query_field_count);
+
+ /* Print out and check the metadata */
+
+ if (!opt_silent)
+ {
+ printf(" ------------------------------------------------------------\n");
+ printf(" | Metadata \n");
+ printf(" ------------------------------------------------------------\n");
+ printf(" | Query | Prepared statement \n");
+ printf(" ------------------------------------------------------------\n");
+ printf(" field name | length | max_length | length | max_length\n");
+ printf(" ------------------------------------------------------------\n");
+
+ for (i= 0; i < query_field_count; ++i)
+ {
+ MYSQL_FIELD *f1= &query_field_list[i], *f2= &stmt_field_list[i];
+ printf(" %-11s | %9lu | %10lu | %9lu | %10lu \n",
+ f1->name, f1->length, f1->max_length, f2->length, f2->max_length);
+ DIE_UNLESS(f1->length == f2->length);
+ }
+ printf(" ---------------------------------------------------------------\n");
+ }
+
+ /* Bind and fetch the data */
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < stmt_field_count; ++i)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[i].buffer_length= stmt_field_list[i].max_length + 1;
+ my_bind[i].buffer= malloc(my_bind[i].buffer_length);
+ }
+ mysql_stmt_bind_result(stmt, my_bind);
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ /* Clean up */
+
+ for (i= 0; i < stmt_field_count; ++i)
+ free(my_bind[i].buffer);
+ mysql_stmt_close(stmt);
+ mysql_free_result(query_result);
+ mysql_free_result(stmt_metadata);
+ stmt_text= "drop table t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+/*
+ Test of basic checks that are performed in server for components
+ of MYSQL_TIME parameters.
+*/
+
+static void test_datetime_ranges()
+{
+ const char *stmt_text;
+ int rc, i;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[6];
+ MYSQL_TIME tm[6];
+
+ myheader("test_datetime_ranges");
+
+ stmt_text= "drop table if exists t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "create table t1 (year datetime, month datetime, day datetime, "
+ "hour datetime, min datetime, sec datetime)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql,
+ "INSERT INTO t1 VALUES (?, ?, ?, ?, ?, ?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 6);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < 6; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_DATETIME;
+ my_bind[i].buffer= &tm[i];
+ }
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ tm[0].year= 2004; tm[0].month= 11; tm[0].day= 10;
+ tm[0].hour= 12; tm[0].minute= 30; tm[0].second= 30;
+ tm[0].second_part= 0; tm[0].neg= 0;
+
+ tm[5]= tm[4]= tm[3]= tm[2]= tm[1]= tm[0];
+ tm[0].year= 10000; tm[1].month= 13; tm[2].day= 32;
+ tm[3].hour= 24; tm[4].minute= 60; tm[5].second= 60;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ my_process_warnings(mysql, 6);
+
+ verify_col_data("t1", "year", "0000-00-00 00:00:00");
+ verify_col_data("t1", "month", "0000-00-00 00:00:00");
+ verify_col_data("t1", "day", "0000-00-00 00:00:00");
+ verify_col_data("t1", "hour", "0000-00-00 00:00:00");
+ verify_col_data("t1", "min", "0000-00-00 00:00:00");
+ verify_col_data("t1", "sec", "0000-00-00 00:00:00");
+
+ mysql_stmt_close(stmt);
+
+ stmt_text= "delete from t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 (year, month, day) "
+ "VALUES (?, ?, ?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 3);
+
+ /*
+ We reuse contents of bind and tm arrays left from previous part of test.
+ */
+ for (i= 0; i < 3; i++)
+ my_bind[i].buffer_type= MYSQL_TYPE_DATE;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ my_process_warnings(mysql, 3);
+
+ verify_col_data("t1", "year", "0000-00-00 00:00:00");
+ verify_col_data("t1", "month", "0000-00-00 00:00:00");
+ verify_col_data("t1", "day", "0000-00-00 00:00:00");
+
+ mysql_stmt_close(stmt);
+
+ stmt_text= "drop table t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "create table t1 (day_ovfl time, day time, hour time, min time, sec time)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql,
+ "INSERT INTO t1 VALUES (?, ?, ?, ?, ?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 5);
+
+ /*
+ Again we reuse what we can from previous part of test.
+ */
+ for (i= 0; i < 5; i++)
+ my_bind[i].buffer_type= MYSQL_TYPE_TIME;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ tm[0].year= 0; tm[0].month= 0; tm[0].day= 10;
+ tm[0].hour= 12; tm[0].minute= 30; tm[0].second= 30;
+ tm[0].second_part= 0; tm[0].neg= 0;
+
+ tm[4]= tm[3]= tm[2]= tm[1]= tm[0];
+ tm[0].day= 35; tm[1].day= 34; tm[2].hour= 30; tm[3].minute= 60; tm[4].second= 60;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ my_process_warnings(mysql, 2);
+
+ verify_col_data("t1", "day_ovfl", "838:59:59");
+ verify_col_data("t1", "day", "828:30:30");
+ verify_col_data("t1", "hour", "270:30:30");
+ verify_col_data("t1", "min", "00:00:00");
+ verify_col_data("t1", "sec", "00:00:00");
+
+ mysql_stmt_close(stmt);
+
+ stmt_text= "drop table t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+/*
+ This test is used in:
+ mysql-test/suite/binlog/binlog_stm_datetime_ranges_mdev15289.test
+*/
+static void test_datetime_ranges_mdev15289()
+{
+ const char *stmt_text;
+ int rc, i;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[4];
+ MYSQL_TIME tm[4];
+
+ myheader("test_datetime_ranges_mdev15289");
+
+ stmt_text= "SET sql_mode=''";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "create or replace table t1 "
+ "(t time, d date, dt datetime,ts timestamp)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES (?, ?, ?, ?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 4);
+
+ /*** Testing DATETIME ***/
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < 4; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_DATETIME;
+ my_bind[i].buffer= &tm[i];
+ }
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /* Notice bad year */
+ tm[0].year= 20010; tm[0].month= 1; tm[0].day= 2;
+ tm[0].hour= 03; tm[0].minute= 04; tm[0].second= 05;
+ tm[0].second_part= 0; tm[0].neg= 0;
+ tm[0].time_type= MYSQL_TIMESTAMP_DATETIME;
+ tm[3]= tm[2]= tm[1]= tm[0];
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ my_process_warnings(mysql, 4);
+
+ verify_col_data("t1", "t", "00:00:00");
+ verify_col_data("t1", "d", "0000-00-00");
+ verify_col_data("t1", "dt", "0000-00-00 00:00:00");
+ verify_col_data("t1", "ts", "0000-00-00 00:00:00");
+
+ /*** Testing DATE ***/
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < 4; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_DATE;
+ my_bind[i].buffer= &tm[i];
+ }
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /* Notice bad year */
+ tm[0].year= 20010; tm[0].month= 1; tm[0].day= 2;
+ tm[0].hour= 00; tm[0].minute= 00; tm[0].second= 00;
+ tm[0].second_part= 0; tm[0].neg= 0;
+ tm[0].time_type= MYSQL_TIMESTAMP_DATE;
+ tm[3]= tm[2]= tm[1]= tm[0];
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ my_process_warnings(mysql, 4);
+
+ verify_col_data("t1", "t", "00:00:00");
+ verify_col_data("t1", "d", "0000-00-00");
+ verify_col_data("t1", "dt", "0000-00-00 00:00:00");
+ verify_col_data("t1", "ts", "0000-00-00 00:00:00");
+
+ /*** Testing TIME ***/
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i= 0; i < 4; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_TIME;
+ my_bind[i].buffer= &tm[i];
+ }
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /* Notice bad hour */
+ tm[0].year= 0; tm[0].month= 0; tm[0].day= 0;
+ tm[0].hour= 100; tm[0].minute= 64; tm[0].second= 05;
+ tm[0].second_part= 0; tm[0].neg= 0;
+ tm[0].time_type= MYSQL_TIMESTAMP_TIME;
+ tm[3]= tm[2]= tm[1]= tm[0];
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ my_process_warnings(mysql, 4);
+
+ verify_col_data("t1", "t", "00:00:00");
+ verify_col_data("t1", "d", "0000-00-00");
+ verify_col_data("t1", "dt", "0000-00-00 00:00:00");
+ verify_col_data("t1", "ts", "0000-00-00 00:00:00");
+
+ mysql_stmt_close(stmt);
+
+ stmt_text= "drop table t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+static void test_bug4172()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[3];
+ const char *stmt_text;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ int rc;
+ char f[100], d[100], e[100];
+ ulong f_len, d_len, e_len;
+
+ myheader("test_bug4172");
+
+ mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ mysql_query(mysql, "CREATE TABLE t1 (f float, d double, e decimal(10,4))");
+ mysql_query(mysql, "INSERT INTO t1 VALUES (12345.1234, 123456.123456, "
+ "123456.1234)");
+
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT f, d, e FROM t1";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= f;
+ my_bind[0].buffer_length= sizeof(f);
+ my_bind[0].length= &f_len;
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= d;
+ my_bind[1].buffer_length= sizeof(d);
+ my_bind[1].length= &d_len;
+ my_bind[2].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[2].buffer= e;
+ my_bind[2].buffer_length= sizeof(e);
+ my_bind[2].length= &e_len;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ mysql_stmt_store_result(stmt);
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ res= mysql_store_result(mysql);
+ row= mysql_fetch_row(res);
+
+ if (!opt_silent)
+ {
+ printf("Binary protocol: float=%s, double=%s, decimal(10,4)=%s\n",
+ f, d, e);
+ printf("Text protocol: float=%s, double=%s, decimal(10,4)=%s\n",
+ row[0], row[1], row[2]);
+ }
+ DIE_UNLESS(!strcmp(f, row[0]) && !strcmp(d, row[1]) && !strcmp(e, row[2]));
+
+ mysql_free_result(res);
+ mysql_stmt_close(stmt);
+}
+
+
+static void test_conversion()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ int rc;
+ MYSQL_BIND my_bind[1];
+ uchar buff[4];
+ ulong length;
+
+ myheader("test_conversion");
+
+ stmt_text= "DROP TABLE IF EXISTS t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "CREATE TABLE t1 (a TEXT) DEFAULT CHARSET latin1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "SET character_set_connection=utf8, character_set_client=utf8, "
+ " character_set_results=latin1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ stmt_text= "INSERT INTO t1 (a) VALUES (?)";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer= (char*) buff;
+ my_bind[0].length= &length;
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+
+ mysql_stmt_bind_param(stmt, my_bind);
+
+ buff[0]= (uchar) 0xC3;
+ buff[1]= (uchar) 0xA0;
+ length= 2;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ stmt_text= "SELECT a FROM t1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ my_bind[0].buffer_length= sizeof(buff);
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+ DIE_UNLESS(length == 1);
+ DIE_UNLESS(buff[0] == 0xE0);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+ stmt_text= "DROP TABLE t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "SET NAMES DEFAULT";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+static void test_rewind(void)
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind;
+ int rc = 0;
+ const char *stmt_text;
+ long unsigned int length=4, Data=0;
+ my_bool isnull=0;
+
+ myheader("test_rewind");
+
+ stmt_text= "CREATE TABLE t1 (a int)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "INSERT INTO t1 VALUES(2),(3),(4)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ stmt_text= "SELECT * FROM t1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ bzero((char*) &my_bind, sizeof(MYSQL_BIND));
+ my_bind.buffer_type= MYSQL_TYPE_LONG;
+ my_bind.buffer= (void *)&Data; /* this buffer won't be altered */
+ my_bind.length= &length;
+ my_bind.is_null= &isnull;
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_bind_result(stmt, &my_bind);
+ DIE_UNLESS(rc == 0);
+
+ /* retreive all result sets till we are at the end */
+ while(!mysql_stmt_fetch(stmt))
+ if (!opt_silent)
+ printf("fetched result:%ld\n", Data);
+
+ DIE_UNLESS(rc != MYSQL_NO_DATA);
+
+ /* seek to the first row */
+ mysql_stmt_data_seek(stmt, 0);
+
+ /* now we should be able to fetch the results again */
+ /* but mysql_stmt_fetch returns MYSQL_NO_DATA */
+ while(!(rc= mysql_stmt_fetch(stmt)))
+ if (!opt_silent)
+ printf("fetched result after seek:%ld\n", Data);
+
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ stmt_text= "DROP TABLE t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ rc= mysql_stmt_free_result(stmt);
+ rc= mysql_stmt_close(stmt);
+}
+
+
+static void test_truncation()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ int rc;
+ uint bind_count;
+ MYSQL_BIND *bind_array, *my_bind;
+
+ myheader("test_truncation");
+
+ /* Prepare the test table */
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ stmt_text= "create table t1 ("
+ "i8 tinyint, ui8 tinyint unsigned, "
+ "i16 smallint, i16_1 smallint, "
+ "ui16 smallint unsigned, i32 int, i32_1 int, "
+ "d double, d_1 double, ch char(30), ch_1 char(30), "
+ "tx text, tx_1 text, ch_2 char(30) "
+ ")";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ {
+ const char insert_text[]=
+ "insert into t1 VALUES ("
+ "-10, " /* i8 */
+ "200, " /* ui8 */
+ "32000, " /* i16 */
+ "-32767, " /* i16_1 */
+ "64000, " /* ui16 */
+ "1073741824, " /* i32 */
+ "1073741825, " /* i32_1 */
+ "123.456, " /* d */
+ "-12345678910, " /* d_1 */
+ "'111111111111111111111111111111',"/* ch */
+ "'abcdef', " /* ch_1 */
+ "'12345 ', " /* tx */
+ "'12345.67 ', " /* tx_1 */
+ "'12345.67abc'" /* ch_2 */
+ ")";
+ rc= mysql_real_query(mysql, insert_text, strlen(insert_text));
+ myquery(rc);
+ }
+
+ stmt_text= "select i8 c1, i8 c2, ui8 c3, i16_1 c4, ui16 c5, "
+ " i16 c6, ui16 c7, i32 c8, i32_1 c9, i32_1 c10, "
+ " d c11, d_1 c12, d_1 c13, ch c14, ch_1 c15, tx c16, "
+ " tx_1 c17, ch_2 c18 "
+ "from t1";
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ bind_count= (uint) mysql_stmt_field_count(stmt);
+
+ /*************** Fill in the bind structure and bind it **************/
+ bind_array= malloc(sizeof(MYSQL_BIND) * bind_count);
+ bzero((char*) bind_array, sizeof(MYSQL_BIND) * bind_count);
+ for (my_bind= bind_array; my_bind < bind_array + bind_count; my_bind++)
+ my_bind->error= &my_bind->error_value;
+ my_bind= bind_array;
+
+ my_bind->buffer= malloc(sizeof(uint8));
+ my_bind->buffer_type= MYSQL_TYPE_TINY;
+ my_bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(uint32));
+ my_bind->buffer_type= MYSQL_TYPE_LONG;
+ my_bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(int8));
+ my_bind->buffer_type= MYSQL_TYPE_TINY;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(uint16));
+ my_bind->buffer_type= MYSQL_TYPE_SHORT;
+ my_bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(int16));
+ my_bind->buffer_type= MYSQL_TYPE_SHORT;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(uint16));
+ my_bind->buffer_type= MYSQL_TYPE_SHORT;
+ my_bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(int8));
+ my_bind->buffer_type= MYSQL_TYPE_TINY;
+ my_bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(float));
+ my_bind->buffer_type= MYSQL_TYPE_FLOAT;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(float));
+ my_bind->buffer_type= MYSQL_TYPE_FLOAT;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(double));
+ my_bind->buffer_type= MYSQL_TYPE_DOUBLE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(longlong));
+ my_bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(ulonglong));
+ my_bind->buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind->is_unsigned= TRUE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(longlong));
+ my_bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(longlong));
+ my_bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(longlong));
+ my_bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(longlong));
+ my_bind->buffer_type= MYSQL_TYPE_LONGLONG;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(double));
+ my_bind->buffer_type= MYSQL_TYPE_DOUBLE;
+
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ my_bind->buffer= malloc(sizeof(double));
+ my_bind->buffer_type= MYSQL_TYPE_DOUBLE;
+
+ rc= mysql_stmt_bind_result(stmt, bind_array);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+
+ /*************** Verify truncation results ***************************/
+ my_bind= bind_array;
+
+ /* signed tiny -> tiny */
+ DIE_UNLESS(*my_bind->error && * (int8*) my_bind->buffer == -10);
+
+ /* signed tiny -> uint32 */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error && * (int32*) my_bind->buffer == -10);
+
+ /* unsigned tiny -> tiny */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error && * (uint8*) my_bind->buffer == 200);
+
+ /* short -> ushort */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error && * (int16*) my_bind->buffer == -32767);
+
+ /* ushort -> short */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error && * (uint16*) my_bind->buffer == 64000);
+
+ /* short -> ushort (no truncation, data is in the range of target type) */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(! *my_bind->error && * (uint16*) my_bind->buffer == 32000);
+
+ /* ushort -> utiny */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error && * (int8*) my_bind->buffer == 0);
+
+ /* int -> float: no truncation, the number is a power of two */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(! *my_bind->error && * (float*) my_bind->buffer == 1073741824);
+
+ /* int -> float: truncation, not enough bits in float */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error);
+
+ /* int -> double: no truncation */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(! *my_bind->error && * (double*) my_bind->buffer == 1073741825);
+
+ /* double -> longlong: fractional part is lost */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+
+ /* double -> ulonglong, negative fp number to unsigned integer */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ /* Value in the buffer is not defined: don't test it */
+ DIE_UNLESS(*my_bind->error);
+
+ /* double -> longlong, negative fp number to signed integer: no loss */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(! *my_bind->error && * (longlong*) my_bind->buffer == -12345678910LL);
+
+ /* big numeric string -> number */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error);
+
+ /* junk string -> number */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(*my_bind->error && *(longlong*) my_bind->buffer == 0);
+
+ /* string with trailing spaces -> number */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(! *my_bind->error && *(longlong*) my_bind->buffer == 12345);
+
+ /* string with trailing spaces -> double */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ DIE_UNLESS(! *my_bind->error && *(double*) my_bind->buffer == 12345.67);
+
+ /* string with trailing junk -> double */
+ DIE_UNLESS(my_bind++ < bind_array + bind_count);
+ /*
+ XXX: There must be a truncation error: but it's not the way the server
+ behaves, so let's leave it for now.
+ */
+ DIE_UNLESS(*(double*) my_bind->buffer == 12345.67);
+ /*
+ TODO: string -> double, double -> time, double -> string (truncation
+ errors are not supported here yet)
+ longlong -> time/date/datetime
+ date -> time, date -> timestamp, date -> number
+ time -> string, time -> date, time -> timestamp,
+ number -> date string -> date
+ */
+ /*************** Cleanup *********************************************/
+
+ mysql_stmt_close(stmt);
+
+ for (my_bind= bind_array; my_bind < bind_array + bind_count; my_bind++)
+ free(my_bind->buffer);
+ free(bind_array);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+static void test_truncation_option()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ int rc;
+ uint8 buf;
+ my_bool option= 0;
+ my_bool error;
+ MYSQL_BIND my_bind;
+
+ myheader("test_truncation_option");
+
+ /* Prepare the test table */
+ stmt_text= "select -1";
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) &my_bind, sizeof(my_bind));
+
+ my_bind.buffer= (void*) &buf;
+ my_bind.buffer_type= MYSQL_TYPE_TINY;
+ my_bind.is_unsigned= TRUE;
+ my_bind.error= &error;
+
+ rc= mysql_stmt_bind_result(stmt, &my_bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_DATA_TRUNCATED);
+ DIE_UNLESS(error);
+ rc= mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, (char*) &option);
+ myquery(rc);
+ /* need to rebind for the new setting to take effect */
+ rc= mysql_stmt_bind_result(stmt, &my_bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ /* The only change is rc - error pointers are still filled in */
+ DIE_UNLESS(error == 1);
+ /* restore back the defaults */
+ option= 1;
+ mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, (char*) &option);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Bug#6761 - mysql_list_fields doesn't work */
+
+static void test_bug6761(void)
+{
+ const char *stmt_text;
+ MYSQL_RES *res;
+ int rc;
+ myheader("test_bug6761");
+
+ stmt_text= "CREATE TABLE t1 (a int, b char(255), c decimal)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ res= mysql_list_fields(mysql, "t1", "%");
+ DIE_UNLESS(res && mysql_num_fields(res) == 3);
+ mysql_free_result(res);
+
+ stmt_text= "DROP TABLE t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+/* Bug#8330 - mysql_stmt_execute crashes (libmysql) */
+
+static void test_bug8330()
+{
+ const char *stmt_text;
+ MYSQL_STMT *stmt[2];
+ int i, rc;
+ const char *query= "select a,b from t1 where a=?";
+ MYSQL_BIND my_bind[2];
+ long lval[2];
+
+ myheader("test_bug8330");
+
+ stmt_text= "drop table if exists t1";
+ /* in case some previos test failed */
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "create table t1 (a int, b int)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i=0; i < 2; i++)
+ {
+ stmt[i]= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt[i], query, strlen(query));
+ check_execute(stmt[i], rc);
+
+ my_bind[i].buffer_type= MYSQL_TYPE_LONG;
+ lval[i]= 0;
+ my_bind[i].buffer= (void*) &lval[i];
+ my_bind[i].is_null= 0;
+ mysql_stmt_bind_param(stmt[i], &my_bind[i]);
+ }
+
+ rc= mysql_stmt_execute(stmt[0]);
+ check_execute(stmt[0], rc);
+
+ rc= mysql_stmt_execute(stmt[1]);
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt[1]) == CR_COMMANDS_OUT_OF_SYNC);
+ rc= mysql_stmt_execute(stmt[0]);
+ check_execute(stmt[0], rc);
+
+ mysql_stmt_close(stmt[0]);
+ mysql_stmt_close(stmt[1]);
+
+ stmt_text= "drop table t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+/* Bug#7990 - mysql_stmt_close doesn't reset mysql->net.last_error */
+
+static void test_bug7990()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ myheader("test_bug7990");
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, "foo", 3);
+ /*
+ XXX: the fact that we store errno both in STMT and in
+ MYSQL is not documented and is subject to change in 5.0
+ */
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt) && mysql_errno(mysql));
+ mysql_stmt_close(stmt);
+ DIE_UNLESS(!mysql_errno(mysql));
+}
+
+/*
+ Bug #15518 - Reusing a stmt that has failed during prepare
+ does not clear error
+*/
+
+static void test_bug15518()
+{
+ MYSQL_STMT *stmt;
+ MYSQL* mysql1;
+ int rc;
+ myheader("test_bug15518");
+
+ mysql1= mysql_client_init(NULL);
+
+ if (!mysql_real_connect(mysql1, opt_host, opt_user, opt_password,
+ opt_db ? opt_db : "test", opt_port, opt_unix_socket,
+ CLIENT_MULTI_STATEMENTS))
+ {
+ fprintf(stderr, "Failed to connect to the database\n");
+ DIE_UNLESS(0);
+ }
+
+ stmt= mysql_stmt_init(mysql1);
+
+ /*
+ The prepare of foo should fail with errno 1064 since
+ it's not a valid query
+ */
+ rc= mysql_stmt_prepare(stmt, "foo", 3);
+ if (!opt_silent)
+ fprintf(stdout, "rc: %d, mysql_stmt_errno: %d, mysql_errno: %d\n",
+ rc, mysql_stmt_errno(stmt), mysql_errno(mysql1));
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt) && mysql_errno(mysql1));
+
+ /*
+ Use the same stmt and reprepare with another query that
+ suceeds
+ */
+ rc= mysql_stmt_prepare(stmt, "SHOW STATUS", 12);
+ if (!opt_silent)
+ fprintf(stdout, "rc: %d, mysql_stmt_errno: %d, mysql_errno: %d\n",
+ rc, mysql_stmt_errno(stmt), mysql_errno(mysql1));
+ DIE_UNLESS(!rc || mysql_stmt_errno(stmt) || mysql_errno(mysql1));
+
+ mysql_stmt_close(stmt);
+ DIE_UNLESS(!mysql_errno(mysql1));
+
+ /*
+ part2, when connection to server has been closed
+ after first prepare
+ */
+ stmt= mysql_stmt_init(mysql1);
+ rc= mysql_stmt_prepare(stmt, "foo", 3);
+ if (!opt_silent)
+ fprintf(stdout, "rc: %d, mysql_stmt_errno: %d, mysql_errno: %d\n",
+ rc, mysql_stmt_errno(stmt), mysql_errno(mysql1));
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt) && mysql_errno(mysql1));
+
+ /* Close connection to server */
+ mysql_close(mysql1);
+
+ /*
+ Use the same stmt and reprepare with another query that
+ suceeds. The prepare should fail with error 2013 since
+ connection to server has been closed.
+ */
+ rc= mysql_stmt_prepare(stmt, "SHOW STATUS", 12);
+ if (!opt_silent)
+ fprintf(stdout, "rc: %d, mysql_stmt_errno: %d\n",
+ rc, mysql_stmt_errno(stmt));
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt));
+
+ mysql_stmt_close(stmt);
+}
+
+
+static void disable_query_logs()
+{
+ int rc;
+ rc= mysql_query(mysql, "set @@global.general_log=off");
+ myquery(rc);
+ rc= mysql_query(mysql, "set @@global.slow_query_log=off");
+ myquery(rc);
+}
+
+
+static void enable_query_logs(int truncate)
+{
+ int rc;
+
+ rc= mysql_query(mysql, "set @save_global_general_log=@@global.general_log");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "set @save_global_slow_query_log=@@global.slow_query_log");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "set @@global.general_log=on");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "set @@global.slow_query_log=on");
+ myquery(rc);
+
+
+ if (truncate)
+ {
+ rc= mysql_query(mysql, "truncate mysql.general_log");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "truncate mysql.slow_log");
+ myquery(rc);
+ }
+}
+
+
+static void restore_query_logs()
+{
+ int rc;
+ rc= mysql_query(mysql, "set @@global.general_log=@save_global_general_log");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "set @@global.slow_query_log=@save_global_slow_query_log");
+ myquery(rc);
+}
+
+
+static void test_view_sp_list_fields()
+{
+ int rc;
+ MYSQL_RES *res;
+
+ myheader("test_view_sp_list_fields");
+
+ rc= mysql_query(mysql, "DROP FUNCTION IF EXISTS f1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS v1, t1, t2");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS v1, t1, t2");
+ myquery(rc);
+ rc= mysql_query(mysql, "create function f1 () returns int return 5");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (s1 char,s2 char)");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t2 (s1 int);");
+ myquery(rc);
+ rc= mysql_query(mysql, "create view v1 as select s2,sum(s1) - \
+count(s2) as vx from t1 group by s2 having sum(s1) - count(s2) < (select f1() \
+from t2);");
+ myquery(rc);
+ res= mysql_list_fields(mysql, "v1", NullS);
+ DIE_UNLESS(res != 0 && mysql_num_fields(res) != 0);
+ rc= mysql_query(mysql, "DROP FUNCTION f1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1, t2");
+ mysql_free_result(res);
+ myquery(rc);
+
+}
+
+
+/*
+ Test mysql_real_escape_string() with gbk charset
+
+ The important part is that 0x27 (') is the second-byte in a invalid
+ two-byte GBK character here. But 0xbf5c is a valid GBK character, so
+ it needs to be escaped as 0x5cbf27
+*/
+#define TEST_BUG8378_IN "\xef\xbb\xbf\x27\xbf\x10"
+#define TEST_BUG8378_OUT "\xef\xbb\x5c\xbf\x5c\x27\x5c\xbf\x10"
+
+static void test_bug8378()
+{
+#if defined(HAVE_CHARSET_gbk) && !defined(EMBEDDED_LIBRARY)
+ MYSQL *lmysql;
+ char out[9]; /* strlen(TEST_BUG8378)*2+1 */
+ char buf[256];
+ int len, rc;
+
+ myheader("test_bug8378");
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a test connection ...");
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+ if (mysql_options(lmysql, MYSQL_SET_CHARSET_NAME, "gbk"))
+ {
+ myerror("mysql_options() failed");
+ exit(1);
+ }
+ if (!(mysql_real_connect(lmysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ exit(1);
+ }
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+
+ rc= mysql_query(lmysql, "SET SQL_MODE=''");
+ myquery(rc);
+
+ len= mysql_real_escape_string(lmysql, out, TEST_BUG8378_IN, 4);
+
+ /* No escaping should have actually happened. */
+ DIE_UNLESS(memcmp(out, TEST_BUG8378_OUT, len) == 0);
+
+ sprintf(buf, "SELECT '%s'", out);
+
+ rc=mysql_real_query(lmysql, buf, strlen(buf));
+ myquery(rc);
+
+ mysql_close(lmysql);
+#endif
+}
+
+
+static void test_bug8722()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *stmt_text;
+
+ myheader("test_bug8722");
+ /* Prepare test data */
+ stmt_text= "drop table if exists t1, v1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "CREATE TABLE t1 (c1 varchar(10), c2 varchar(10), c3 varchar(10),"
+ " c4 varchar(10), c5 varchar(10), c6 varchar(10),"
+ " c7 varchar(10), c8 varchar(10), c9 varchar(10),"
+ "c10 varchar(10))";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "INSERT INTO t1 VALUES (1,2,3,4,5,6,7,8,9,10)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ stmt_text= "CREATE VIEW v1 AS SELECT * FROM t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ /* Note: if you uncomment following block everything works fine */
+/*
+ rc= mysql_query(mysql, "sellect * from v1");
+ myquery(rc);
+ mysql_free_result(mysql_store_result(mysql));
+*/
+
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "select * from v1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+ stmt_text= "drop table if exists t1, v1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+}
+
+
+MYSQL_STMT *open_cursor(const char *query)
+{
+ int rc;
+ const ulong type= (ulong)CURSOR_TYPE_READ_ONLY;
+
+ MYSQL_STMT *stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
+ return stmt;
+}
+
+
+static void test_bug8880()
+{
+ MYSQL_STMT *stmt_list[2], **stmt;
+ MYSQL_STMT **stmt_list_end= (MYSQL_STMT**) stmt_list + 2;
+ int rc;
+
+ myheader("test_bug8880");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (a int not null primary key, b int)");
+ rc= mysql_query(mysql, "insert into t1 values (1,1)");
+ myquery(rc); /* one check is enough */
+ /*
+ when inserting 2 rows everything works well
+ mysql_query(mysql, "INSERT INTO t1 VALUES (1,1),(2,2)");
+ */
+ for (stmt= stmt_list; stmt < stmt_list_end; stmt++)
+ *stmt= open_cursor("select a from t1");
+ for (stmt= stmt_list; stmt < stmt_list_end; stmt++)
+ {
+ rc= mysql_stmt_execute(*stmt);
+ check_execute(*stmt, rc);
+ }
+ for (stmt= stmt_list; stmt < stmt_list_end; stmt++)
+ mysql_stmt_close(*stmt);
+}
+
+/*
+ Test executing a query with prepared statements while query cache is active
+*/
+
+static void test_open_cursor_prepared_statement_query_cache()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_open_cursor_prepared_statement_query_cache");
+ if (! is_query_cache_available())
+ {
+ fprintf(stdout, "Skipping test_open_cursor_prepared_statement_query_cache: Query cache not available.\n");
+ return;
+ }
+
+ rc= mysql_query(mysql,
+ "set @save_query_cache_type="
+ "@@global.query_cache_type,"
+ "@save_query_cache_size="
+ "@@global.query_cache_size");
+ myquery(rc);
+ rc= mysql_query(mysql, "set global query_cache_type=ON");
+ myquery(rc);
+ rc= mysql_query(mysql, "set local query_cache_type=ON");
+ myquery(rc);
+ rc= mysql_query(mysql, "set global query_cache_size=1000000");
+ myquery(rc);
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (a int not null primary key, b int)");
+ rc= mysql_query(mysql, "insert into t1 values (1,1)");
+ myquery(rc); /* one check is enough */
+
+ /* Store query in query cache */
+ rc= mysql_query(mysql, "SELECT * FROM t1");
+ myquery(rc);
+ result= mysql_store_result(mysql);
+ mytest(result);
+ (void) my_process_result_set(result);
+ mysql_free_result(result);
+
+ /* Test using a cursor */
+ stmt= open_cursor("select a from t1");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "set global query_cache_type=@save_query_cache_type");
+ myquery(rc);
+ rc= mysql_query(mysql, "set global query_cache_size=@save_query_cache_size");
+ myquery(rc);
+}
+
+
+static void test_bug9159()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *stmt_text= "select a, b from t1";
+ const unsigned long type= CURSOR_TYPE_READ_ONLY;
+
+ myheader("test_bug9159");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (a int not null primary key, b int)");
+ rc= mysql_query(mysql, "insert into t1 values (1,1)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void *)&type);
+
+ mysql_stmt_execute(stmt);
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+}
+
+
+/* Crash when opening a cursor to a query with DISTICNT and no key */
+
+static void test_bug9520()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char a[6];
+ ulong a_len;
+ int rc, row_count= 0;
+
+ myheader("test_bug9520");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (a char(5), b char(5), c char(5),"
+ " primary key (a, b, c))");
+ rc= mysql_query(mysql, "insert into t1 values ('x', 'y', 'z'), "
+ " ('a', 'b', 'c'), ('k', 'l', 'm')");
+ myquery(rc);
+
+ stmt= open_cursor("select distinct b from t1");
+
+ /*
+ Not crashes with:
+ stmt= open_cursor("select distinct a from t1");
+ */
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (char*) a;
+ my_bind[0].buffer_length= sizeof(a);
+ my_bind[0].length= &a_len;
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ row_count++;
+
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ if (!opt_silent)
+ printf("Fetched %d rows\n", row_count);
+ DBUG_ASSERT(row_count == 3);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/*
+ We can't have more than one cursor open for a prepared statement.
+ Test re-executions of a PS with cursor; mysql_stmt_reset must close
+ the cursor attached to the statement, if there is one.
+*/
+
+static void test_bug9478()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char a[6];
+ ulong a_len;
+ int rc, i;
+ DBUG_ENTER("test_bug9478");
+
+ myheader("test_bug9478");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (id integer not null primary key, "
+ " name varchar(20) not null)");
+ rc= mysql_query(mysql, "insert into t1 (id, name) values "
+ " (1, 'aaa'), (2, 'bbb'), (3, 'ccc')");
+ myquery(rc);
+
+ stmt= open_cursor("select name from t1 where id=2");
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (char*) a;
+ my_bind[0].buffer_length= sizeof(a);
+ my_bind[0].length= &a_len;
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ for (i= 0; i < 5; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent && i == 0)
+ printf("Fetched row: %s\n", a);
+
+ /*
+ The query above is a one-row result set. Therefore, there is no
+ cursor associated with it, as the server won't bother with opening
+ a cursor for a one-row result set. The first row was read from the
+ server in the fetch above. But there is eof packet pending in the
+ network. mysql_stmt_execute will flush the packet and successfully
+ execute the statement.
+ */
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent && i == 0)
+ printf("Fetched row: %s\n", a);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ {
+ char buff[8];
+ /* Fill in the fetch packet */
+ int4store(buff, stmt->stmt_id);
+ buff[4]= 1; /* prefetch rows */
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc);
+ if (!opt_silent && i == 0)
+ printf("Got error (as expected): %s\n", mysql_error(mysql));
+ }
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent && i == 0)
+ printf("Fetched row: %s\n", a);
+
+ rc= mysql_stmt_reset(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt));
+ if (!opt_silent && i == 0)
+ printf("Got error (as expected): %s\n", mysql_stmt_error(stmt));
+ }
+ rc= mysql_stmt_close(stmt);
+ DIE_UNLESS(rc == 0);
+
+ /* Test the case with a server side cursor */
+ stmt= open_cursor("select name from t1");
+
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ for (i= 0; i < 5; i++)
+ {
+ DBUG_PRINT("loop",("i: %d", i));
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent && i == 0)
+ printf("Fetched row: %s\n", a);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ while (! (rc= mysql_stmt_fetch(stmt)))
+ {
+ if (!opt_silent && i == 0)
+ printf("Fetched row: %s\n", a);
+ }
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent && i == 0)
+ printf("Fetched row: %s\n", a);
+
+ rc= mysql_stmt_reset(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt));
+ if (!opt_silent && i == 0)
+ printf("Got error (as expected): %s\n", mysql_stmt_error(stmt));
+ }
+
+ rc= mysql_stmt_close(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Error message is returned for unsupported features.
+ Test also cursors with non-default PREFETCH_ROWS
+*/
+
+static void test_bug9643()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ int32 a;
+ int rc;
+ const char *stmt_text;
+ int num_rows= 0;
+ ulong type;
+ ulong prefetch_rows= 5;
+
+ myheader("test_bug9643");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (id integer not null primary key)");
+ rc= mysql_query(mysql, "insert into t1 (id) values "
+ " (1), (2), (3), (4), (5), (6), (7), (8), (9)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ /* Not implemented in 5.0 */
+ type= (ulong) CURSOR_TYPE_SCROLLABLE;
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_stmt_error(stmt));
+
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS,
+ (void*) &prefetch_rows);
+ check_execute(stmt, rc);
+ stmt_text= "select * from t1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void*) &a;
+ my_bind[0].buffer_length= sizeof(a);
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
+ ++num_rows;
+ DIE_UNLESS(num_rows == 9);
+
+ rc= mysql_stmt_close(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+/*
+ Bug#11111: fetch from view returns wrong data
+*/
+
+static void test_bug11111()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ char buf[2][20];
+ ulong len[2];
+ int i;
+ int rc;
+ const char *query= "SELECT DISTINCT f1,ff2 FROM v1";
+
+ myheader("test_bug11111");
+
+ rc= mysql_query(mysql, "drop table if exists t1, t2, v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "drop view if exists t1, t2, v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (f1 int, f2 int)");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t2 (ff1 int, ff2 int)");
+ myquery(rc);
+ rc= mysql_query(mysql, "create view v1 as select * from t1, t2 where f1=ff1");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1,1), (2,2), (3,3)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t2 values (1,1), (2,2), (3,3)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ mysql_stmt_prepare(stmt, query, strlen(query));
+ mysql_stmt_execute(stmt);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ for (i=0; i < 2; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[i].buffer= (uchar* *)&buf[i];
+ my_bind[i].buffer_length= 20;
+ my_bind[i].length= &len[i];
+ }
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ printf("return: %s", buf[1]);
+ DIE_UNLESS(!strcmp(buf[1],"1"));
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop view v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "drop table t1, t2");
+ myquery(rc);
+}
+
+/*
+ Check that proper cleanups are done for prepared statement when
+ fetching thorugh a cursor.
+*/
+
+static void test_bug10729()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char a[21];
+ int rc;
+ const char *stmt_text;
+ int i= 0;
+ const char *name_array[3]= { "aaa", "bbb", "ccc" };
+ ulong type;
+
+ myheader("test_bug10729");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (id integer not null primary key,"
+ "name VARCHAR(20) NOT NULL)");
+ rc= mysql_query(mysql, "insert into t1 (id, name) values "
+ "(1, 'aaa'), (2, 'bbb'), (3, 'ccc')");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
+ check_execute(stmt, rc);
+ stmt_text= "select name from t1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void*) a;
+ my_bind[0].buffer_length= sizeof(a);
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ for (i= 0; i < 3; i++)
+ {
+ int row_no= 0;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
+ {
+ DIE_UNLESS(strcmp(a, name_array[row_no]) == 0);
+ if (!opt_silent)
+ printf("%d: %s\n", row_no, a);
+ ++row_no;
+ }
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ }
+ rc= mysql_stmt_close(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/*
+ Check that mysql_next_result works properly in case when one of
+ the statements used in a multi-statement query is erroneous
+*/
+
+static void test_bug9992()
+{
+ MYSQL *mysql1;
+ MYSQL_RES* res ;
+ int rc;
+
+ myheader("test_bug9992");
+
+ if (!opt_silent)
+ printf("Establishing a connection with option CLIENT_MULTI_STATEMENTS..\n");
+
+ mysql1= mysql_client_init(NULL);
+
+ if (!mysql_real_connect(mysql1, opt_host, opt_user, opt_password,
+ opt_db ? opt_db : "test", opt_port, opt_unix_socket,
+ CLIENT_MULTI_STATEMENTS))
+ {
+ fprintf(stderr, "Failed to connect to the database\n");
+ DIE_UNLESS(0);
+ }
+
+
+ /* Sic: SHOW DATABASE is incorrect syntax. */
+ rc= mysql_query(mysql1, "SHOW TABLES; SHOW DATABASE; SELECT 1;");
+
+ if (rc)
+ {
+ fprintf(stderr, "[%d] %s\n", mysql_errno(mysql1), mysql_error(mysql1));
+ DIE_UNLESS(0);
+ }
+
+ if (!opt_silent)
+ printf("Testing mysql_store_result/mysql_next_result..\n");
+
+ res= mysql_store_result(mysql1);
+ DIE_UNLESS(res);
+ mysql_free_result(res);
+ rc= mysql_next_result(mysql1);
+ DIE_UNLESS(rc == 1); /* Got errors, as expected */
+
+ if (!opt_silent)
+ fprintf(stdout, "Got error, as expected:\n [%d] %s\n",
+ mysql_errno(mysql1), mysql_error(mysql1));
+
+ mysql_close(mysql1);
+}
+
+/* Bug#10736: cursors and subqueries, memroot management */
+
+static void test_bug10736()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ char a[21];
+ int rc;
+ const char *stmt_text;
+ int i= 0;
+ ulong type;
+
+ myheader("test_bug10736");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (id integer not null primary key,"
+ "name VARCHAR(20) NOT NULL)");
+ rc= mysql_query(mysql, "insert into t1 (id, name) values "
+ "(1, 'aaa'), (2, 'bbb'), (3, 'ccc')");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
+ check_execute(stmt, rc);
+ stmt_text= "select name from t1 where name=(select name from t1 where id=2)";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void*) a;
+ my_bind[0].buffer_length= sizeof(a);
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ for (i= 0; i < 3; i++)
+ {
+ int row_no= 0;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
+ {
+ if (!opt_silent)
+ printf("%d: %s\n", row_no, a);
+ ++row_no;
+ }
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ }
+ rc= mysql_stmt_close(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+/* Bug#10794: cursors, packets out of order */
+
+static void test_bug10794()
+{
+ MYSQL_STMT *stmt, *stmt1;
+ MYSQL_BIND my_bind[2];
+ char a[21];
+ int id_val;
+ ulong a_len;
+ int rc;
+ const char *stmt_text;
+ int i= 0;
+ ulong type;
+
+ myheader("test_bug10794");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (id integer not null primary key,"
+ "name varchar(20) not null)");
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "insert into t1 (id, name) values (?, ?)";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void*) &id_val;
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void*) a;
+ my_bind[1].length= &a_len;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+ for (i= 0; i < 42; i++)
+ {
+ id_val= (i+1)*10;
+ sprintf(a, "a%d", i);
+ a_len= strlen(a); /* safety against broken sprintf */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+ stmt_text= "select name from t1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+ stmt1= mysql_stmt_init(mysql);
+ mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void*) a;
+ my_bind[0].buffer_length= sizeof(a);
+ my_bind[0].length= &a_len;
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ if (!opt_silent)
+ printf("Fetched row from stmt: %s\n", a);
+ /* Don't optimize: an attribute of the original test case */
+ mysql_stmt_free_result(stmt);
+ mysql_stmt_reset(stmt);
+ stmt_text= "select name from t1 where id=10";
+ rc= mysql_stmt_prepare(stmt1, stmt_text, strlen(stmt_text));
+ check_execute(stmt1, rc);
+ rc= mysql_stmt_bind_result(stmt1, my_bind);
+ check_execute(stmt1, rc);
+ rc= mysql_stmt_execute(stmt1);
+ while (1)
+ {
+ rc= mysql_stmt_fetch(stmt1);
+ if (rc == MYSQL_NO_DATA)
+ {
+ if (!opt_silent)
+ printf("End of data in stmt1\n");
+ break;
+ }
+ check_execute(stmt1, rc);
+ if (!opt_silent)
+ printf("Fetched row from stmt1: %s\n", a);
+ }
+ mysql_stmt_close(stmt);
+ mysql_stmt_close(stmt1);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/* Bug#11172: cursors, crash on a fetch from a datetime column */
+
+static void test_bug11172()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind_in[1], bind_out[2];
+ MYSQL_TIME hired;
+ int rc;
+ const char *stmt_text;
+ int i= 0, id;
+ ulong type;
+
+ myheader("test_bug11172");
+
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (id integer not null primary key,"
+ "hired date not null)");
+ rc= mysql_query(mysql,
+ "insert into t1 (id, hired) values (1, '1933-08-24'), "
+ "(2, '1965-01-01'), (3, '1949-08-17'), (4, '1945-07-07'), "
+ "(5, '1941-05-15'), (6, '1978-09-15'), (7, '1936-03-28')");
+ myquery(rc);
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "SELECT id, hired FROM t1 WHERE hired=?";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+
+ bzero((char*) bind_in, sizeof(bind_in));
+ bzero((char*) bind_out, sizeof(bind_out));
+ bzero((char*) &hired, sizeof(hired));
+ hired.year= 1965;
+ hired.month= 1;
+ hired.day= 1;
+ bind_in[0].buffer_type= MYSQL_TYPE_DATE;
+ bind_in[0].buffer= (void*) &hired;
+ bind_in[0].buffer_length= sizeof(hired);
+ bind_out[0].buffer_type= MYSQL_TYPE_LONG;
+ bind_out[0].buffer= (void*) &id;
+ bind_out[1]= bind_in[0];
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_bind_param(stmt, bind_in);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_bind_result(stmt, bind_out);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
+ {
+ if (!opt_silent)
+ printf("fetched data %d:%d-%d-%d\n", id,
+ hired.year, hired.month, hired.day);
+ }
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+ if (!mysql_stmt_free_result(stmt))
+ mysql_stmt_reset(stmt);
+ }
+ mysql_stmt_close(stmt);
+ mysql_rollback(mysql);
+ mysql_rollback(mysql);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/* Bug#11656: cursors, crash on a fetch from a query with distinct. */
+
+static void test_bug11656()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ int rc;
+ const char *stmt_text;
+ char buf[2][20];
+ int i= 0;
+ ulong type;
+
+ myheader("test_bug11656");
+
+ mysql_query(mysql, "drop table if exists t1");
+
+ rc= mysql_query(mysql, "create table t1 ("
+ "server varchar(40) not null, "
+ "test_kind varchar(1) not null, "
+ "test_id varchar(30) not null , "
+ "primary key (server,test_kind,test_id))");
+ myquery(rc);
+
+ stmt_text= "select distinct test_kind, test_id from t1 "
+ "where server in (?, ?)";
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ strmov(buf[0], "pcint502_MY2");
+ strmov(buf[1], "*");
+ for (i=0; i < 2; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[i].buffer= (uchar* *)&buf[i];
+ my_bind[i].buffer_length= strlen(buf[i]);
+ }
+ mysql_stmt_bind_param(stmt, my_bind);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/*
+ Check that the server signals when NO_BACKSLASH_ESCAPES mode is in effect,
+ and mysql_real_escape_string() does the right thing as a result.
+*/
+
+static void test_bug10214()
+{
+ int len;
+ char out[8];
+
+ myheader("test_bug10214");
+
+ DIE_UNLESS(!(mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES));
+
+ len= mysql_real_escape_string(mysql, out, "a'b\\c", 5);
+ DIE_UNLESS(memcmp(out, "a\\'b\\\\c", len) == 0);
+
+ mysql_query(mysql, "set sql_mode='NO_BACKSLASH_ESCAPES'");
+ DIE_UNLESS(mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES);
+
+ len= mysql_real_escape_string(mysql, out, "a'b\\c", 5);
+ DIE_UNLESS(memcmp(out, "a''b\\c", len) == 0);
+
+ mysql_query(mysql, "set sql_mode=''");
+}
+
+static void test_client_character_set()
+{
+ MY_CHARSET_INFO cs;
+ char *csname= (char*) "utf8";
+ char *csdefault= (char*)mysql_character_set_name(mysql);
+ int rc;
+
+ myheader("test_client_character_set");
+
+ rc= mysql_set_character_set(mysql, csname);
+ DIE_UNLESS(rc == 0);
+
+ mysql_get_character_set_info(mysql, &cs);
+ DIE_UNLESS(!strcmp(cs.csname, "utf8mb3"));
+ DIE_UNLESS(!strcmp(cs.name, "utf8mb3_general_ci"));
+ /* Restore the default character set */
+ rc= mysql_set_character_set(mysql, csdefault);
+ myquery(rc);
+}
+
+/* Test correct max length for MEDIUMTEXT and LONGTEXT columns */
+
+static void test_bug9735()
+{
+ MYSQL_RES *res;
+ int rc;
+
+ myheader("test_bug9735");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (a mediumtext, b longtext) "
+ "character set latin1");
+ myquery(rc);
+ rc= mysql_query(mysql, "select * from t1");
+ myquery(rc);
+ res= mysql_store_result(mysql);
+ verify_prepare_field(res, 0, "a", "a", MYSQL_TYPE_BLOB,
+ "t1", "t1", current_db, (1U << 24)-1, 0);
+ verify_prepare_field(res, 1, "b", "b", MYSQL_TYPE_BLOB,
+ "t1", "t1", current_db, ~0U, 0);
+ mysql_free_result(res);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/* Bug#11183 "mysql_stmt_reset() doesn't reset information about error" */
+
+static void test_bug11183()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ char bug_statement[]= "insert into t1 values (1)";
+
+ myheader("test_bug11183");
+
+ mysql_query(mysql, "drop table t1 if exists");
+ mysql_query(mysql, "create table t1 (a int)");
+
+ stmt= mysql_stmt_init(mysql);
+ DIE_UNLESS(stmt != 0);
+
+ rc= mysql_stmt_prepare(stmt, bug_statement, strlen(bug_statement));
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+
+ /* Trying to execute statement that should fail on execute stage */
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc);
+
+ mysql_stmt_reset(stmt);
+ DIE_UNLESS(mysql_stmt_errno(stmt) == 0);
+
+ mysql_query(mysql, "create table t1 (a int)");
+
+ /* Trying to execute statement that should pass ok */
+ if (mysql_stmt_execute(stmt))
+ {
+ mysql_stmt_reset(stmt);
+ DIE_UNLESS(mysql_stmt_errno(stmt) == 0);
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+static void test_bug11037()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *stmt_text;
+
+ myheader("test_bug11037");
+
+ mysql_query(mysql, "drop table if exists t1");
+
+ rc= mysql_query(mysql, "create table t1 (id int not null)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 values (1)");
+ myquery(rc);
+
+ stmt_text= "select id FROM t1";
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+
+ /* expected error */
+ rc = mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc==1);
+ if (!opt_silent)
+ fprintf(stdout, "Got error, as expected:\n [%d] %s\n",
+ mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc==0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc==MYSQL_NO_DATA);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc==MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+/* Bug#10760: cursors, crash in a fetch after rollback. */
+
+static void test_bug10760()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ int rc;
+ const char *stmt_text;
+ char id_buf[20];
+ ulong id_len;
+ int i= 0;
+ ulong type;
+
+ myheader("test_bug10760");
+
+ mysql_query(mysql, "drop table if exists t1, t2");
+
+ /* create tables */
+ rc= mysql_query(mysql, "create table t1 (id integer not null primary key)"
+ " engine=MyISAM");
+ myquery(rc);
+ for (; i < 42; ++i)
+ {
+ char buf[100];
+ sprintf(buf, "insert into t1 (id) values (%d)", i+1);
+ rc= mysql_query(mysql, buf);
+ myquery(rc);
+ }
+ mysql_autocommit(mysql, FALSE);
+ /* create statement */
+ stmt= mysql_stmt_init(mysql);
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+
+ /*
+ 1: check that a deadlock within the same connection
+ is resolved and an error is returned. The deadlock is modelled
+ as follows:
+ con1: open cursor for select * from t1;
+ con1: insert into t1 (id) values (1)
+ */
+ stmt_text= "select id from t1 order by 1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_query(mysql, "update t1 set id=id+100");
+ /*
+ If cursors are not materialized, the update will return an error;
+ we mainly test that it won't deadlock.
+ */
+ if (rc && !opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(mysql));
+ /*
+ 2: check that MyISAM tables used in cursors survive
+ COMMIT/ROLLBACK.
+ */
+ rc= mysql_rollback(mysql); /* should not close the cursor */
+ myquery(rc);
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ /*
+ 3: check that cursors to InnoDB tables are closed (for now) by
+ COMMIT/ROLLBACK.
+ */
+ if (! have_innodb)
+ {
+ if (!opt_silent)
+ printf("Testing that cursors are closed at COMMIT/ROLLBACK requires "
+ "InnoDB.\n");
+ }
+ else
+ {
+ stmt_text= "select id from t1 order by 1";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "alter table t1 engine=InnoDB");
+ myquery(rc);
+
+ bzero(my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (void*) id_buf;
+ my_bind[0].buffer_length= sizeof(id_buf);
+ my_bind[0].length= &id_len;
+ check_execute(stmt, rc);
+ mysql_stmt_bind_result(stmt, my_bind);
+
+ rc= mysql_stmt_execute(stmt);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+ if (!opt_silent)
+ printf("Fetched row %s\n", id_buf);
+ rc= mysql_rollback(mysql); /* should close the cursor */
+ myquery(rc);
+#if 0
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc);
+ if (!opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(mysql));
+#endif
+ }
+
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+ mysql_autocommit(mysql, TRUE); /* restore default */
+}
+
+static void test_bug12001()
+{
+ MYSQL *mysql_local;
+ MYSQL_RES *result;
+ const char *query= "DROP TABLE IF EXISTS test_table;"
+ "CREATE TABLE test_table(id INT);"
+ "INSERT INTO test_table VALUES(10);"
+ "UPDATE test_table SET id=20 WHERE id=10;"
+ "SELECT * FROM test_table;"
+ "INSERT INTO non_existent_table VALUES(11);";
+ int rc, res;
+
+ myheader("test_bug12001");
+
+ if (!(mysql_local= mysql_client_init(NULL)))
+ {
+ fprintf(stdout, "\n mysql_client_init() failed");
+ exit(1);
+ }
+
+ /* Create connection that supports multi statements */
+ if (!mysql_real_connect(mysql_local, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, CLIENT_MULTI_STATEMENTS))
+ {
+ fprintf(stdout, "\n mysql_real_connect() failed");
+ exit(1);
+ }
+
+ rc= mysql_query(mysql_local, query);
+ myquery(rc);
+
+ do
+ {
+ if (mysql_field_count(mysql_local) &&
+ (result= mysql_use_result(mysql_local)))
+ {
+ mysql_free_result(result);
+ }
+ }
+ while (!(res= mysql_next_result(mysql_local)));
+
+ rc= mysql_query(mysql_local, "DROP TABLE IF EXISTS test_table");
+ myquery(rc);
+
+ mysql_close(mysql_local);
+ DIE_UNLESS(res==1);
+}
+
+
+/* Bug#11909: wrong metadata if fetching from two cursors */
+
+static void test_bug11909()
+{
+ MYSQL_STMT *stmt1, *stmt2;
+ MYSQL_BIND my_bind[7];
+ int rc;
+ char firstname[20], midinit[20], lastname[20], workdept[20];
+ ulong firstname_len, midinit_len, lastname_len, workdept_len;
+ uint32 empno;
+ double salary;
+ float bonus;
+ const char *stmt_text;
+
+ myheader("test_bug11909");
+
+ stmt_text= "drop table if exists t1";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "create table t1 ("
+ " empno int(11) not null, firstname varchar(20) not null,"
+ " midinit varchar(20) not null, lastname varchar(20) not null,"
+ " workdept varchar(6) not null, salary double not null,"
+ " bonus float not null, primary key (empno)"
+ ") default charset=latin1 collate=latin1_bin";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "insert into t1 values "
+ "(10, 'CHRISTINE', 'I', 'HAAS', 'A00', 52750, 1000), "
+ "(20, 'MICHAEL', 'L', 'THOMPSON', 'B01', 41250, 800),"
+ "(30, 'SALLY', 'A', 'KWAN', 'C01', 38250, 800),"
+ "(50, 'JOHN', 'B', 'GEYER', 'E01', 40175, 800), "
+ "(60, 'IRVING', 'F', 'STERN', 'D11', 32250, 500)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ /* ****** Begin of trace ****** */
+
+ stmt1= open_cursor("SELECT empno, firstname, midinit, lastname,"
+ "workdept, salary, bonus FROM t1");
+
+ bzero(my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void*) &empno;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+ my_bind[1].buffer= (void*) firstname;
+ my_bind[1].buffer_length= sizeof(firstname);
+ my_bind[1].length= &firstname_len;
+
+ my_bind[2].buffer_type= MYSQL_TYPE_VAR_STRING;
+ my_bind[2].buffer= (void*) midinit;
+ my_bind[2].buffer_length= sizeof(midinit);
+ my_bind[2].length= &midinit_len;
+
+ my_bind[3].buffer_type= MYSQL_TYPE_VAR_STRING;
+ my_bind[3].buffer= (void*) lastname;
+ my_bind[3].buffer_length= sizeof(lastname);
+ my_bind[3].length= &lastname_len;
+
+ my_bind[4].buffer_type= MYSQL_TYPE_VAR_STRING;
+ my_bind[4].buffer= (void*) workdept;
+ my_bind[4].buffer_length= sizeof(workdept);
+ my_bind[4].length= &workdept_len;
+
+ my_bind[5].buffer_type= MYSQL_TYPE_DOUBLE;
+ my_bind[5].buffer= (void*) &salary;
+
+ my_bind[6].buffer_type= MYSQL_TYPE_FLOAT;
+ my_bind[6].buffer= (void*) &bonus;
+ rc= mysql_stmt_bind_result(stmt1, my_bind);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_fetch(stmt1);
+ DIE_UNLESS(rc == 0);
+ DIE_UNLESS(empno == 10);
+ DIE_UNLESS(strcmp(firstname, "CHRISTINE") == 0);
+ DIE_UNLESS(strcmp(midinit, "I") == 0);
+ DIE_UNLESS(strcmp(lastname, "HAAS") == 0);
+ DIE_UNLESS(strcmp(workdept, "A00") == 0);
+ DIE_UNLESS(salary == (double) 52750.0);
+ DIE_UNLESS(bonus == (float) 1000.0);
+
+ stmt2= open_cursor("SELECT empno, firstname FROM t1");
+ rc= mysql_stmt_bind_result(stmt2, my_bind);
+ check_execute(stmt2, rc);
+
+ rc= mysql_stmt_execute(stmt2);
+ check_execute(stmt2, rc);
+
+ rc= mysql_stmt_fetch(stmt2);
+ DIE_UNLESS(rc == 0);
+
+ DIE_UNLESS(empno == 10);
+ DIE_UNLESS(strcmp(firstname, "CHRISTINE") == 0);
+
+ rc= mysql_stmt_reset(stmt2);
+ check_execute(stmt2, rc);
+
+ /* ERROR: next statement should return 0 */
+
+ rc= mysql_stmt_fetch(stmt1);
+ DIE_UNLESS(rc == 0);
+
+ mysql_stmt_close(stmt1);
+ mysql_stmt_close(stmt2);
+ rc= mysql_rollback(mysql);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+/* Cursors: opening a cursor to a compilicated query with ORDER BY */
+
+static void test_bug11901()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[2];
+ int rc;
+ char workdept[20];
+ ulong workdept_len;
+ uint32 empno;
+ const char *stmt_text;
+
+ myheader("test_bug11901");
+
+ stmt_text= "drop table if exists t1, t2";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "create table t1 ("
+ " empno int(11) not null, firstname varchar(20) not null,"
+ " midinit varchar(20) not null, lastname varchar(20) not null,"
+ " workdept varchar(6) not null, salary double not null,"
+ " bonus float not null, primary key (empno), "
+ " unique key (workdept, empno) "
+ ") default charset=latin1 collate=latin1_bin";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "insert into t1 values "
+ "(10, 'CHRISTINE', 'I', 'HAAS', 'A00', 52750, 1000),"
+ "(20, 'MICHAEL', 'L', 'THOMPSON', 'B01', 41250, 800), "
+ "(30, 'SALLY', 'A', 'KWAN', 'C01', 38250, 800), "
+ "(50, 'JOHN', 'B', 'GEYER', 'E01', 40175, 800), "
+ "(60, 'IRVING', 'F', 'STERN', 'D11', 32250, 500), "
+ "(70, 'EVA', 'D', 'PULASKI', 'D21', 36170, 700), "
+ "(90, 'EILEEN', 'W', 'HENDERSON', 'E11', 29750, 600), "
+ "(100, 'THEODORE', 'Q', 'SPENSER', 'E21', 26150, 500), "
+ "(110, 'VINCENZO', 'G', 'LUCCHESSI', 'A00', 46500, 900), "
+ "(120, 'SEAN', '', 'O\\'CONNELL', 'A00', 29250, 600), "
+ "(130, 'DOLORES', 'M', 'QUINTANA', 'C01', 23800, 500), "
+ "(140, 'HEATHER', 'A', 'NICHOLLS', 'C01', 28420, 600), "
+ "(150, 'BRUCE', '', 'ADAMSON', 'D11', 25280, 500), "
+ "(160, 'ELIZABETH', 'R', 'PIANKA', 'D11', 22250, 400), "
+ "(170, 'MASATOSHI', 'J', 'YOSHIMURA', 'D11', 24680, 500), "
+ "(180, 'MARILYN', 'S', 'SCOUTTEN', 'D11', 21340, 500), "
+ "(190, 'JAMES', 'H', 'WALKER', 'D11', 20450, 400), "
+ "(200, 'DAVID', '', 'BROWN', 'D11', 27740, 600), "
+ "(210, 'WILLIAM', 'T', 'JONES', 'D11', 18270, 400), "
+ "(220, 'JENNIFER', 'K', 'LUTZ', 'D11', 29840, 600), "
+ "(230, 'JAMES', 'J', 'JEFFERSON', 'D21', 22180, 400), "
+ "(240, 'SALVATORE', 'M', 'MARINO', 'D21', 28760, 600), "
+ "(250, 'DANIEL', 'S', 'SMITH', 'D21', 19180, 400), "
+ "(260, 'SYBIL', 'P', 'JOHNSON', 'D21', 17250, 300), "
+ "(270, 'MARIA', 'L', 'PEREZ', 'D21', 27380, 500), "
+ "(280, 'ETHEL', 'R', 'SCHNEIDER', 'E11', 26250, 500), "
+ "(290, 'JOHN', 'R', 'PARKER', 'E11', 15340, 300), "
+ "(300, 'PHILIP', 'X', 'SMITH', 'E11', 17750, 400), "
+ "(310, 'MAUDE', 'F', 'SETRIGHT', 'E11', 15900, 300), "
+ "(320, 'RAMLAL', 'V', 'MEHTA', 'E21', 19950, 400), "
+ "(330, 'WING', '', 'LEE', 'E21', 25370, 500), "
+ "(340, 'JASON', 'R', 'GOUNOT', 'E21', 23840, 500)";
+
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "create table t2 ("
+ " deptno varchar(6) not null, deptname varchar(20) not null,"
+ " mgrno int(11) not null, location varchar(20) not null,"
+ " admrdept varchar(6) not null, refcntd int(11) not null,"
+ " refcntu int(11) not null, primary key (deptno)"
+ ") default charset=latin1 collate=latin1_bin";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ stmt_text= "insert into t2 values "
+ "('A00', 'SPIFFY COMPUTER SERV', 10, '', 'A00', 0, 0), "
+ "('B01', 'PLANNING', 20, '', 'A00', 0, 0), "
+ "('C01', 'INFORMATION CENTER', 30, '', 'A00', 0, 0), "
+ "('D01', 'DEVELOPMENT CENTER', 0, '', 'A00', 0, 0),"
+ "('D11', 'MANUFACTURING SYSTEM', 60, '', 'D01', 0, 0), "
+ "('D21', 'ADMINISTRATION SYSTE', 70, '', 'D01', 0, 0), "
+ "('E01', 'SUPPORT SERVICES', 50, '', 'A00', 0, 0), "
+ "('E11', 'OPERATIONS', 90, '', 'E01', 0, 0), "
+ "('E21', 'SOFTWARE SUPPORT', 100,'', 'E01', 0, 0)";
+ rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
+ myquery(rc);
+
+ /* ****** Begin of trace ****** */
+
+ stmt= open_cursor("select t1.empno, t1.workdept "
+ "from (t1 left join t2 on t2.deptno = t1.workdept) "
+ "where t2.deptno in "
+ " (select t2.deptno "
+ " from (t1 left join t2 on t2.deptno = t1.workdept) "
+ " where t1.empno = ?) "
+ "order by 1");
+ bzero(my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= &empno;
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ my_bind[1].buffer_type= MYSQL_TYPE_VAR_STRING;
+ my_bind[1].buffer= (void*) workdept;
+ my_bind[1].buffer_length= sizeof(workdept);
+ my_bind[1].length= &workdept_len;
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ empno= 10;
+ /* ERROR: next statement causes a server crash */
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1, t2");
+ myquery(rc);
+}
+
+/* Bug#11904: mysql_stmt_attr_set CURSOR_TYPE_READ_ONLY grouping wrong result */
+
+static void test_bug11904()
+{
+ MYSQL_STMT *stmt1;
+ int rc;
+ const char *stmt_text;
+ const ulong type= (ulong)CURSOR_TYPE_READ_ONLY;
+ MYSQL_BIND my_bind[2];
+ int country_id=0;
+ char row_data[11]= {0};
+
+ myheader("test_bug11904");
+
+ /* create tables */
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS bug11904b");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE bug11904b (id int, name char(10), primary key(id, name))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO bug11904b VALUES (1, 'sofia'), (1,'plovdiv'),"
+ " (1,'varna'), (2,'LA'), (2,'new york'), (3,'heidelberg'),"
+ " (3,'berlin'), (3, 'frankfurt')");
+
+ myquery(rc);
+ mysql_commit(mysql);
+ /* create statement */
+ stmt1= mysql_stmt_init(mysql);
+ mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+
+ stmt_text= "SELECT id, MIN(name) FROM bug11904b GROUP BY id";
+
+ rc= mysql_stmt_prepare(stmt1, stmt_text, strlen(stmt_text));
+ check_execute(stmt1, rc);
+
+ memset(my_bind, 0, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer=& country_id;
+ my_bind[0].buffer_length= 0;
+ my_bind[0].length= 0;
+
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer=& row_data;
+ my_bind[1].buffer_length= sizeof(row_data) - 1;
+ my_bind[1].length= 0;
+
+ rc= mysql_stmt_bind_result(stmt1, my_bind);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+ DIE_UNLESS(country_id == 1);
+ DIE_UNLESS(memcmp(row_data, "plovdiv", 7) == 0);
+
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+ DIE_UNLESS(country_id == 2);
+ DIE_UNLESS(memcmp(row_data, "LA", 2) == 0);
+
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+ DIE_UNLESS(country_id == 3);
+ DIE_UNLESS(memcmp(row_data, "berlin", 6) == 0);
+
+ rc= mysql_stmt_close(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_query(mysql, "drop table bug11904b");
+ myquery(rc);
+}
+
+
+/* Bug#12243: multiple cursors, crash in a fetch after commit. */
+
+static void test_bug12243()
+{
+ MYSQL_STMT *stmt1, *stmt2;
+ int rc;
+ const char *stmt_text;
+ ulong type;
+
+ myheader("test_bug12243");
+
+ if (! have_innodb)
+ {
+ if (!opt_silent)
+ printf("This test requires InnoDB.\n");
+ return;
+ }
+
+ /* create tables */
+ mysql_query(mysql, "drop table if exists t1");
+ mysql_query(mysql, "create table t1 (a int) engine=InnoDB");
+ rc= mysql_query(mysql, "insert into t1 (a) values (1), (2)");
+ myquery(rc);
+ mysql_autocommit(mysql, FALSE);
+ /* create statement */
+ stmt1= mysql_stmt_init(mysql);
+ stmt2= mysql_stmt_init(mysql);
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+ mysql_stmt_attr_set(stmt2, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+
+ stmt_text= "select a from t1";
+
+ rc= mysql_stmt_prepare(stmt1, stmt_text, strlen(stmt_text));
+ check_execute(stmt1, rc);
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_prepare(stmt2, stmt_text, strlen(stmt_text));
+ check_execute(stmt2, rc);
+ rc= mysql_stmt_execute(stmt2);
+ check_execute(stmt2, rc);
+ rc= mysql_stmt_fetch(stmt2);
+ check_execute(stmt2, rc);
+
+ rc= mysql_stmt_close(stmt1);
+ check_execute(stmt1, rc);
+ rc= mysql_commit(mysql);
+ myquery(rc);
+ rc= mysql_stmt_fetch(stmt2);
+ check_execute(stmt2, rc);
+
+ mysql_stmt_close(stmt2);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+ mysql_autocommit(mysql, TRUE); /* restore default */
+}
+
+
+/*
+ Bug#11718: query with function, join and order by returns wrong type
+*/
+
+static void test_bug11718()
+{
+ MYSQL_RES *res;
+ int rc;
+ const char *query= "select str_to_date(concat(f3),'%Y%m%d') from t1,t2 "
+ "where f1=f2 order by f1";
+
+ myheader("test_bug11718");
+
+ rc= mysql_query(mysql, "drop table if exists t1, t2");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (f1 int)");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t2 (f2 int, f3 numeric(8))");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1), (2)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t2 values (1,20050101), (2,20050202)");
+ myquery(rc);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+ res = mysql_store_result(mysql);
+
+ if (!opt_silent)
+ printf("return type: %s", (res->fields[0].type == MYSQL_TYPE_DATE)?"DATE":
+ "not DATE");
+ DIE_UNLESS(res->fields[0].type == MYSQL_TYPE_DATE);
+ mysql_free_result(res);
+ rc= mysql_query(mysql, "drop table t1, t2");
+ myquery(rc);
+}
+
+
+/*
+ Bug #12925: Bad handling of maximum values in getopt
+*/
+static void test_bug12925()
+{
+ myheader("test_bug12925");
+ if (opt_getopt_ll_test)
+ DIE_UNLESS(opt_getopt_ll_test == 25600LL*1024*1024);
+}
+
+
+/*
+ Bug#14210 "Simple query with > operator on large table gives server
+ crash"
+*/
+
+static void test_bug14210()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *stmt_text;
+ ulong type;
+
+ myheader("test_bug14210");
+
+ mysql_query(mysql, "drop table if exists t1");
+ /*
+ To trigger the problem the table must be InnoDB, although the problem
+ itself is not InnoDB related. In case the table is MyISAM this test
+ is harmless.
+ */
+ mysql_query(mysql, "create table t1 (a varchar(255)) engine=InnoDB");
+ rc= mysql_query(mysql, "insert into t1 (a) values (repeat('a', 256))");
+ myquery(rc);
+ rc= mysql_query(mysql, "set @@session.max_heap_table_size=16384");
+ /* Create a big enough table (more than max_heap_table_size) */
+ for (i= 0; i < 8; i++)
+ {
+ rc= mysql_query(mysql, "insert into t1 (a) select a from t1");
+ myquery(rc);
+ }
+ /* create statement */
+ stmt= mysql_stmt_init(mysql);
+ type= (ulong) CURSOR_TYPE_READ_ONLY;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+
+ stmt_text= "select a from t1";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ while ((rc= mysql_stmt_fetch(stmt)) == 0)
+ ;
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "set @@session.max_heap_table_size=default");
+ myquery(rc);
+}
+
+/* Bug#13488: wrong column metadata when fetching from cursor */
+
+static void test_bug13488()
+{
+ MYSQL_BIND my_bind[3];
+ MYSQL_STMT *stmt1;
+ int rc, f1, f2, f3, i;
+ const ulong type= CURSOR_TYPE_READ_ONLY;
+ const char *query= "select * from t1 left join t2 on f1=f2 where f1=1";
+
+ myheader("test_bug13488");
+
+ rc= mysql_query(mysql, "drop table if exists t1, t2");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (f1 int not null primary key)");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t2 (f2 int not null primary key, "
+ "f3 int not null)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1), (2)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t2 values (1,2), (2,4)");
+ myquery(rc);
+
+ memset(my_bind, 0, sizeof(my_bind));
+ for (i= 0; i < 3; i++)
+ {
+ my_bind[i].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[i].buffer_length= 4;
+ my_bind[i].length= 0;
+ }
+ my_bind[0].buffer=&f1;
+ my_bind[1].buffer=&f2;
+ my_bind[2].buffer=&f3;
+
+ stmt1= mysql_stmt_init(mysql);
+ rc= mysql_stmt_attr_set(stmt1,STMT_ATTR_CURSOR_TYPE, (const void *)&type);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_prepare(stmt1, query, strlen(query));
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_bind_result(stmt1, my_bind);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_fetch(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_free_result(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_reset(stmt1);
+ check_execute(stmt1, rc);
+
+ rc= mysql_stmt_close(stmt1);
+ check_execute(stmt1, rc);
+
+ if (!opt_silent)
+ {
+ printf("data: f1: %d; f2: %d; f3: %d\n", f1, f2, f3);
+ printf("data is: %s\n",
+ (f1 == 1 && f2 == 1 && f3 == 2) ? "OK" : "wrong");
+ }
+ DIE_UNLESS(f1 == 1 && f2 == 1 && f3 == 2);
+ rc= mysql_query(mysql, "drop table t1, t2");
+ myquery(rc);
+}
+
+/*
+ Bug#13524: warnings of a previous command are not reset when fetching
+ from a cursor.
+*/
+
+static void test_bug13524()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ unsigned int warning_count;
+ const ulong type= CURSOR_TYPE_READ_ONLY;
+ const char *query= "select * from t1";
+
+ myheader("test_bug13524");
+
+ rc= mysql_query(mysql, "drop table if exists t1, t2");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (a int not null primary key)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1), (2), (3), (4)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ warning_count= mysql_warning_count(mysql);
+ DIE_UNLESS(warning_count == 0);
+
+ /* Check that DROP TABLE produced a warning (no such table) */
+ rc= mysql_query(mysql, "drop table if exists t2");
+ myquery(rc);
+ warning_count= mysql_warning_count(mysql);
+ DIE_UNLESS(warning_count == 1);
+
+ /*
+ Check that fetch from a cursor cleared the warning from the previous
+ command.
+ */
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+ warning_count= mysql_warning_count(mysql);
+ DIE_UNLESS(warning_count == 0);
+
+ /* Cleanup */
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+/*
+ Bug#14845 "mysql_stmt_fetch returns MYSQL_NO_DATA when COUNT(*) is 0"
+*/
+
+static void test_bug14845()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const ulong type= CURSOR_TYPE_READ_ONLY;
+ const char *query= "select count(*) from t1 where 1 = 0";
+
+ myheader("test_bug14845");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (id int(11) default null, "
+ "name varchar(20) default null)"
+ "engine=MyISAM DEFAULT CHARSET=utf8");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1,'abc'),(2,'def')");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ /* Cleanup */
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/*
+ Bug #15510: mysql_warning_count returns 0 after mysql_stmt_fetch which
+ should warn
+*/
+static void test_bug15510()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *query= "select 1 from dual where 1/0";
+
+ myheader("test_bug15510");
+
+ rc= mysql_query(mysql, "set @@sql_mode='ERROR_FOR_DIVISION_BY_ZERO'");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(mysql_warning_count(mysql));
+
+ /* Cleanup */
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "set @@sql_mode=''");
+ myquery(rc);
+}
+
+
+/* Test MYSQL_OPT_RECONNECT, Bug#15719 */
+
+static void test_opt_reconnect()
+{
+ MYSQL *lmysql;
+
+
+ myheader("test_opt_reconnect");
+
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+
+ if (!opt_silent)
+ fprintf(stdout, "reconnect before mysql_options: %d\n", get_reconnect(lmysql));
+ DIE_UNLESS(get_reconnect(lmysql) == 0);
+
+ if (mysql_options(lmysql, MYSQL_OPT_RECONNECT, &my_true))
+ {
+ myerror("mysql_options failed: unknown option MYSQL_OPT_RECONNECT\n");
+ DIE_UNLESS(0);
+ }
+
+ /* reconnect should be 1 */
+ if (!opt_silent)
+ fprintf(stdout, "reconnect after mysql_options: %d\n", get_reconnect(lmysql));
+ DIE_UNLESS(get_reconnect(lmysql) == 1);
+
+ if (!(mysql_real_connect(lmysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ DIE_UNLESS(0);
+ }
+
+ /* reconnect should still be 1 */
+ if (!opt_silent)
+ fprintf(stdout, "reconnect after mysql_real_connect: %d\n",
+ get_reconnect(lmysql));
+ DIE_UNLESS(get_reconnect(lmysql) == 1);
+
+ mysql_close(lmysql);
+
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ DIE_UNLESS(0);
+ }
+
+ if (!opt_silent)
+ fprintf(stdout, "reconnect before mysql_real_connect: %d\n", get_reconnect(lmysql));
+ DIE_UNLESS(get_reconnect(lmysql) == 0);
+
+ if (!(mysql_real_connect(lmysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ DIE_UNLESS(0);
+ }
+
+ /* reconnect should still be 0 */
+ if (!opt_silent)
+ fprintf(stdout, "reconnect after mysql_real_connect: %d\n",
+ get_reconnect(lmysql));
+ DIE_UNLESS(get_reconnect(lmysql) == 0);
+
+ mysql_close(lmysql);
+}
+
+
+#ifndef EMBEDDED_LIBRARY
+
+static void test_bug12744()
+{
+ MYSQL_STMT *prep_stmt = NULL;
+ MYSQL *lmysql;
+ int rc;
+ myheader("test_bug12744");
+
+ lmysql= mysql_client_init(NULL);
+ DIE_UNLESS(lmysql);
+
+ if (!mysql_real_connect(lmysql, opt_host, opt_user, opt_password,
+ current_db, opt_port, opt_unix_socket, 0))
+ {
+ fprintf(stderr, "Failed to connect to the database\n");
+ DIE_UNLESS(0);
+ }
+
+ prep_stmt= mysql_stmt_init(lmysql);
+ rc= mysql_stmt_prepare(prep_stmt, "SELECT 1", 8);
+ DIE_UNLESS(rc == 0);
+
+ mysql_close(lmysql);
+
+ rc= mysql_stmt_execute(prep_stmt);
+ DIE_UNLESS(rc);
+ rc= mysql_stmt_reset(prep_stmt);
+ DIE_UNLESS(rc);
+ rc= mysql_stmt_close(prep_stmt);
+ DIE_UNLESS(rc == 0);
+}
+
+#endif /* EMBEDDED_LIBRARY */
+
+/* Bug #16143: mysql_stmt_sqlstate returns an empty string instead of '00000' */
+
+static void test_bug16143()
+{
+ MYSQL_STMT *stmt;
+ myheader("test_bug16143");
+
+ stmt= mysql_stmt_init(mysql);
+ /* Check mysql_stmt_sqlstate return "no error" */
+ DIE_UNLESS(strcmp(mysql_stmt_sqlstate(stmt), "00000") == 0);
+
+ mysql_stmt_close(stmt);
+}
+
+
+/* Bug #16144: mysql_stmt_attr_get type error */
+
+static void test_bug16144()
+{
+ const my_bool flag_orig= (my_bool) 0xde;
+ my_bool flag= flag_orig;
+ MYSQL_STMT *stmt;
+ myheader("test_bug16144");
+
+ /* Check that attr_get returns correct data on little and big endian CPUs */
+ stmt= mysql_stmt_init(mysql);
+ mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (const void*) &flag);
+ mysql_stmt_attr_get(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &flag);
+ DIE_UNLESS(flag == flag_orig);
+
+ mysql_stmt_close(stmt);
+}
+
+/*
+ Bug #15613: "libmysqlclient API function mysql_stmt_prepare returns wrong
+ field length"
+*/
+
+static void test_bug15613()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ MYSQL_RES *metadata;
+ MYSQL_FIELD *field;
+ int rc;
+ myheader("test_bug15613");
+
+ /* I. Prepare the table */
+ rc= mysql_query(mysql, "set names latin1");
+ myquery(rc);
+ mysql_query(mysql, "drop table if exists t1");
+ rc= mysql_query(mysql,
+ "create table t1 (t text character set utf8, "
+ "tt tinytext character set utf8, "
+ "mt mediumtext character set utf8, "
+ "lt longtext character set utf8, "
+ "vl varchar(255) character set latin1,"
+ "vb varchar(255) character set binary,"
+ "vu varchar(255) character set utf8)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+
+ /* II. Check SELECT metadata */
+ stmt_text= ("select t, tt, mt, lt, vl, vb, vu from t1");
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ metadata= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_fields(metadata);
+ if (!opt_silent)
+ {
+ printf("Field lengths (client character set is latin1):\n"
+ "text character set utf8:\t\t%lu\n"
+ "tinytext character set utf8:\t\t%lu\n"
+ "mediumtext character set utf8:\t\t%lu\n"
+ "longtext character set utf8:\t\t%lu\n"
+ "varchar(255) character set latin1:\t%lu\n"
+ "varchar(255) character set binary:\t%lu\n"
+ "varchar(255) character set utf8:\t%lu\n",
+ field[0].length, field[1].length, field[2].length, field[3].length,
+ field[4].length, field[5].length, field[6].length);
+ }
+ DIE_UNLESS(field[0].length == 65535);
+ DIE_UNLESS(field[1].length == 255);
+ DIE_UNLESS(field[2].length == 16777215);
+ DIE_UNLESS(field[3].length == 4294967295UL);
+ DIE_UNLESS(field[4].length == 255);
+ DIE_UNLESS(field[5].length == 255);
+ DIE_UNLESS(field[6].length == 255);
+ mysql_free_result(metadata);
+ mysql_stmt_free_result(stmt);
+
+ /* III. Cleanup */
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "set names default");
+ myquery(rc);
+ mysql_stmt_close(stmt);
+}
+
+/*
+ Bug#17667: An attacker has the opportunity to bypass query logging.
+
+ Note! Also tests Bug#21813, where prepared statements are used to
+ run queries
+*/
+static void test_bug17667()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ enum query_type { QT_NORMAL, QT_PREPARED};
+ struct buffer_and_length {
+ enum query_type qt;
+ const char *buffer;
+ const uint length;
+ } statements[]= {
+ { QT_NORMAL, "drop table if exists bug17667", 29 },
+ { QT_NORMAL, "create table bug17667 (c varchar(20))", 37 },
+ { QT_NORMAL, "insert into bug17667 (c) values ('regular') /* NUL=\0 with comment */", 68 },
+ { QT_PREPARED,
+ "insert into bug17667 (c) values ('prepared') /* NUL=\0 with comment */", 69, },
+ { QT_NORMAL, "insert into bug17667 (c) values ('NUL=\0 in value')", 50 },
+ { QT_NORMAL, "insert into bug17667 (c) values ('5 NULs=\0\0\0\0\0')", 48 },
+ { QT_PREPARED, "insert into bug17667 (c) values ('6 NULs=\0\0\0\0\0\0')", 50 },
+ { QT_NORMAL, "/* NUL=\0 with comment */ insert into bug17667 (c) values ('encore')", 67 },
+ { QT_NORMAL, "drop table bug17667", 19 },
+ { QT_NORMAL, NULL, 0 } };
+
+ struct buffer_and_length *statement_cursor;
+ FILE *log_file;
+ char *master_log_filename;
+
+ myheader("test_bug17667");
+
+ master_log_filename = (char *) malloc(strlen(opt_vardir) + strlen("/log/master.log") + 1);
+ strxmov(master_log_filename, opt_vardir, "/log/master.log", NullS);
+ if (!opt_silent)
+ printf("Opening '%s'\n", master_log_filename);
+ log_file= my_fopen(master_log_filename, (int) (O_RDONLY | O_BINARY), MYF(0));
+ free(master_log_filename);
+
+ if (log_file == NULL)
+ {
+ if (!opt_silent)
+ {
+ printf("Could not find the log file, VARDIR/log/master.log, so "
+ "test_bug17667 is not run.\n"
+ "Run test from the mysql-test/mysql-test-run* program to set up "
+ "correct environment for this test.\n\n");
+ }
+ return;
+ }
+
+ enable_query_logs(1);
+
+ for (statement_cursor= statements; statement_cursor->buffer != NULL;
+ statement_cursor++)
+ {
+ if (statement_cursor->qt == QT_NORMAL)
+ {
+ /* Run statement as normal query */
+ rc= mysql_real_query(mysql, statement_cursor->buffer,
+ statement_cursor->length);
+ myquery(rc);
+ }
+ else if (statement_cursor->qt == QT_PREPARED)
+ {
+ /*
+ Run as prepared statement
+
+ NOTE! All these queries should be in the log twice,
+ one time for prepare and one time for execute
+ */
+ stmt= mysql_stmt_init(mysql);
+
+ rc= mysql_stmt_prepare(stmt, statement_cursor->buffer,
+ statement_cursor->length);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+ }
+ else
+ {
+ DIE_UNLESS(0==1);
+ }
+ }
+
+ /* Make sure the server has written the logs to disk before reading it */
+ rc= mysql_query(mysql, "flush logs");
+ myquery(rc);
+
+ for (statement_cursor= statements; statement_cursor->buffer != NULL;
+ statement_cursor++)
+ {
+ int expected_hits= 1, hits= 0;
+ char line_buffer[MAX_TEST_QUERY_LENGTH*2];
+ /* more than enough room for the query and some marginalia. */
+
+ /* Prepared statments always occurs twice in log */
+ if (statement_cursor->qt == QT_PREPARED)
+ expected_hits++;
+
+ /* Loop until we found expected number of log entries */
+ do {
+ /* Loop until statement is found in log */
+ do {
+ memset(line_buffer, '/', MAX_TEST_QUERY_LENGTH*2);
+
+ if(fgets(line_buffer, MAX_TEST_QUERY_LENGTH*2, log_file) == NULL)
+ {
+ /* If fgets returned NULL, it indicates either error or EOF */
+ if (feof(log_file))
+ DIE("Found EOF before all statements where found");
+
+ fprintf(stderr, "Got error %d while reading from file\n",
+ ferror(log_file));
+ DIE("Read error");
+ }
+
+ } while (my_memmem(line_buffer, MAX_TEST_QUERY_LENGTH*2,
+ statement_cursor->buffer,
+ statement_cursor->length) == NULL);
+ hits++;
+ } while (hits < expected_hits);
+
+ if (!opt_silent)
+ printf("Found statement starting with \"%s\"\n",
+ statement_cursor->buffer);
+ }
+
+ restore_query_logs();
+
+ if (!opt_silent)
+ printf("success. All queries found intact in the log.\n");
+
+ my_fclose(log_file, MYF(0));
+}
+
+
+/*
+ Bug#14169: type of group_concat() result changed to blob if tmp_table was
+ used
+*/
+static void test_bug14169()
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ MYSQL_RES *res;
+ MYSQL_FIELD *field;
+ int rc;
+
+ myheader("test_bug14169");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "set session group_concat_max_len=1024");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (f1 int unsigned, f2 varchar(255))");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1,repeat('a',255)),"
+ "(2,repeat('b',255))");
+ myquery(rc);
+ stmt= mysql_stmt_init(mysql);
+ stmt_text= "select f2,group_concat(f1) from t1 group by f2";
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ myquery(rc);
+ res= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_fields(res);
+ if (!opt_silent)
+ printf("GROUP_CONCAT() result type %i", field[1].type);
+ DIE_UNLESS(field[1].type == MYSQL_TYPE_BLOB);
+ mysql_free_result(res);
+ mysql_stmt_free_result(stmt);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "set session group_concat_max_len=@@global.group_concat_max_len");
+ myquery(rc);
+}
+
+/*
+ Test that mysql_insert_id() behaves as documented in our manual
+*/
+static void test_mysql_insert_id()
+{
+ my_ulonglong res;
+ int rc;
+
+ myheader("test_mysql_insert_id");
+
+ rc= mysql_query(mysql, "drop table if exists t1,t2");
+ myquery(rc);
+ /* table without auto_increment column */
+ rc= mysql_query(mysql, "create table t1 (f1 int, f2 varchar(255), key(f1))");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1,'a')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "insert into t1 values (null,'b')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "insert into t1 select 5,'c'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+
+ /*
+ Test for bug #34889: mysql_client_test::test_mysql_insert_id test fails
+ sporadically
+ */
+ rc= mysql_query(mysql, "create table t2 (f1 int not null primary key auto_increment, f2 varchar(255))");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t2 values (null,'b')");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 select 5,'c'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "drop table t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 select null,'d'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "insert into t1 values (null,last_insert_id(300))");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 300);
+ rc= mysql_query(mysql, "insert into t1 select null,last_insert_id(400)");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ /*
+ Behaviour change: old code used to return 0; but 400 is consistent
+ with INSERT VALUES, and the manual's section of mysql_insert_id() does not
+ say INSERT SELECT should be different.
+ */
+ DIE_UNLESS(res == 400);
+
+ /* table with auto_increment column */
+ rc= mysql_query(mysql, "create table t2 (f1 int not null primary key auto_increment, f2 varchar(255))");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t2 values (1,'a')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 1);
+ /* this should not influence next INSERT if it doesn't have auto_inc */
+ rc= mysql_query(mysql, "insert into t1 values (10,'e')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+
+ rc= mysql_query(mysql, "insert into t2 values (null,'b')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 2);
+ rc= mysql_query(mysql, "insert into t2 select 5,'c'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ /*
+ Manual says that for multirow insert this should have been 5, but does not
+ say for INSERT SELECT. This is a behaviour change: old code used to return
+ 0. We try to be consistent with INSERT VALUES.
+ */
+ DIE_UNLESS(res == 5);
+ rc= mysql_query(mysql, "insert into t2 select null,'d'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 6);
+ /* with more than one row */
+ rc= mysql_query(mysql, "insert into t2 values (10,'a'),(11,'b')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 11);
+ rc= mysql_query(mysql, "insert into t2 select 12,'a' union select 13,'b'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ /*
+ Manual says that for multirow insert this should have been 13, but does
+ not say for INSERT SELECT. This is a behaviour change: old code used to
+ return 0. We try to be consistent with INSERT VALUES.
+ */
+ DIE_UNLESS(res == 13);
+ rc= mysql_query(mysql, "insert into t2 values (null,'a'),(null,'b')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 14);
+ rc= mysql_query(mysql, "insert into t2 select null,'a' union select null,'b'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 16);
+ rc= mysql_query(mysql, "insert into t2 select 12,'a' union select 13,'b'");
+ myquery_r(rc);
+ rc= mysql_query(mysql, "insert ignore into t2 select 12,'a' union select 13,'b'");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "insert into t2 values (12,'a'),(13,'b')");
+ myquery_r(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "insert ignore into t2 values (12,'a'),(13,'b')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ /* mixing autogenerated and explicit values */
+ rc= mysql_query(mysql, "insert into t2 values (null,'e'),(12,'a'),(13,'b')");
+ myquery_r(rc);
+ rc= mysql_query(mysql, "insert into t2 values (null,'e'),(12,'a'),(13,'b'),(25,'g')");
+ myquery_r(rc);
+ rc= mysql_query(mysql, "insert into t2 values (null,last_insert_id(300))");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ /*
+ according to the manual, this might be 20 or 300, but it looks like
+ auto_increment column takes priority over last_insert_id().
+ */
+ DIE_UNLESS(res == 20);
+ /* If first autogenerated number fails and 2nd works: */
+ rc= mysql_query(mysql, "drop table t2");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t2 (f1 int not null primary key "
+ "auto_increment, f2 varchar(255), unique (f2))");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t2 values (null,'e')");
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 1);
+ rc= mysql_query(mysql, "insert ignore into t2 values (null,'e'),(null,'a'),(null,'e')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 2);
+ /* If autogenerated fails and explicit works: */
+ rc= mysql_query(mysql, "insert ignore into t2 values (null,'e'),(12,'c'),(null,'d')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ /*
+ Behaviour change: old code returned 3 (first autogenerated, even if it
+ fails); we now return first successful autogenerated.
+ */
+ DIE_UNLESS(res == 13);
+ /* UPDATE may update mysql_insert_id() if it uses LAST_INSERT_ID(#) */
+ rc= mysql_query(mysql, "update t2 set f1=14 where f1=12");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "update t2 set f1=0 where f1=14");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ rc= mysql_query(mysql, "update t2 set f2=last_insert_id(372) where f1=0");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 372);
+ /* check that LAST_INSERT_ID() does not update mysql_insert_id(): */
+ rc= mysql_query(mysql, "insert into t2 values (null,'g')");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 15);
+ rc= mysql_query(mysql, "update t2 set f2=(@li:=last_insert_id()) where f1=15");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 0);
+ /*
+ Behaviour change: now if ON DUPLICATE KEY UPDATE updates a row,
+ mysql_insert_id() returns the id of the row, instead of not being
+ affected.
+ */
+ rc= mysql_query(mysql, "insert into t2 values (null,@li) on duplicate key "
+ "update f2=concat('we updated ',f2)");
+ myquery(rc);
+ res= mysql_insert_id(mysql);
+ DIE_UNLESS(res == 15);
+
+ rc= mysql_query(mysql, "drop table t1,t2");
+ myquery(rc);
+}
+
+/*
+ Bug#20152: mysql_stmt_execute() writes to MYSQL_TYPE_DATE buffer
+*/
+
+static void test_bug20152()
+{
+ MYSQL_BIND my_bind[1];
+ MYSQL_STMT *stmt;
+ MYSQL_TIME tm;
+ int rc;
+ const char *query= "INSERT INTO t1 (f1) VALUES (?)";
+
+ myheader("test_bug20152");
+
+ memset(my_bind, 0, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_DATE;
+ my_bind[0].buffer= (void*)&tm;
+
+ memset(&tm, 0, sizeof tm);
+ tm.year = 2006;
+ tm.month = 6;
+ tm.day = 18;
+ tm.hour = 14;
+ tm.minute = 9;
+ tm.second = 42;
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (f1 DATE)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_close(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ if (tm.hour == 14 && tm.minute == 9 && tm.second == 42) {
+ if (!opt_silent)
+ printf("OK!");
+ } else {
+ printf("[14:09:42] != [%02d:%02d:%02d]\n", tm.hour, tm.minute, tm.second);
+ DIE_UNLESS(0==1);
+ }
+}
+
+/* Bug#15752 "Lost connection to MySQL server when calling a SP from C API" */
+
+static void test_bug15752()
+{
+ MYSQL mysql_local;
+ int rc, i;
+ const int ITERATION_COUNT= 100;
+ const char *query= "CALL p1()";
+
+ myheader("test_bug15752");
+
+ rc= mysql_query(mysql, "drop procedure if exists p1");
+ myquery(rc);
+ rc= mysql_query(mysql, "create procedure p1() select 1");
+ myquery(rc);
+
+ mysql_client_init(&mysql_local);
+ if (! mysql_real_connect(&mysql_local, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket,
+ CLIENT_MULTI_STATEMENTS))
+ {
+ printf("Unable connect to MariaDB server: %s\n", mysql_error(&mysql_local));
+ DIE_UNLESS(0);
+ }
+ rc= mysql_real_query(&mysql_local, query, strlen(query));
+ myquery(rc);
+ mysql_free_result(mysql_store_result(&mysql_local));
+
+ rc= mysql_real_query(&mysql_local, query, strlen(query));
+ DIE_UNLESS(rc && mysql_errno(&mysql_local) == CR_COMMANDS_OUT_OF_SYNC);
+
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(&mysql_local));
+
+ /* Check some other commands too */
+
+ DIE_UNLESS(mysql_next_result(&mysql_local) == 0);
+ mysql_free_result(mysql_store_result(&mysql_local));
+ DIE_UNLESS(mysql_next_result(&mysql_local) == -1);
+
+ /* The second problem is not reproducible: add the test case */
+ for (i = 0; i < ITERATION_COUNT; i++)
+ {
+ if (mysql_real_query(&mysql_local, query, strlen(query)))
+ {
+ printf("\ni=%d %s failed: %s\n", i, query, mysql_error(&mysql_local));
+ break;
+ }
+ mysql_free_result(mysql_store_result(&mysql_local));
+ DIE_UNLESS(mysql_next_result(&mysql_local) == 0);
+ mysql_free_result(mysql_store_result(&mysql_local));
+ DIE_UNLESS(mysql_next_result(&mysql_local) == -1);
+
+ }
+ mysql_close(&mysql_local);
+ rc= mysql_query(mysql, "drop procedure p1");
+ myquery(rc);
+}
+
+/*
+ Bug#21206: memory corruption when too many cursors are opened at once
+
+ Memory corruption happens when more than 1024 cursors are open
+ simultaneously.
+*/
+static void test_bug21206()
+{
+ const size_t cursor_count= 1025;
+
+ const char *create_table[]=
+ {
+ "DROP TABLE IF EXISTS t1",
+ "CREATE TABLE t1 (i INT)",
+ "INSERT INTO t1 VALUES (1), (2), (3)"
+ };
+ const char *query= "SELECT * FROM t1";
+
+ Stmt_fetch *fetch_array=
+ (Stmt_fetch*) calloc(cursor_count, sizeof(Stmt_fetch));
+
+ Stmt_fetch *fetch;
+
+ DBUG_ENTER("test_bug21206");
+ myheader("test_bug21206");
+
+ fill_tables(create_table, sizeof(create_table) / sizeof(*create_table));
+
+ for (fetch= fetch_array; fetch < fetch_array + cursor_count; ++fetch)
+ {
+ /* Init will exit(1) in case of error */
+ stmt_fetch_init(fetch, (uint)(fetch - fetch_array), query);
+ }
+
+ for (fetch= fetch_array; fetch < fetch_array + cursor_count; ++fetch)
+ stmt_fetch_close(fetch);
+
+ free(fetch_array);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Ensure we execute the status code while testing
+*/
+
+static void test_status()
+{
+ DBUG_ENTER("test_status");
+ myheader("test_status");
+
+ if (!mysql_stat(mysql))
+ {
+ myerror("mysql_stat failed"); /* purecov: inspected */
+ die(__FILE__, __LINE__, "mysql_stat failed"); /* purecov: inspected */
+ }
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Bug#21726: Incorrect result with multiple invocations of
+ LAST_INSERT_ID
+
+ Test that client gets updated value of insert_id on UPDATE that uses
+ LAST_INSERT_ID(expr).
+ select_query added to test for bug
+ #26921 Problem in mysql_insert_id() Embedded C API function
+*/
+static void test_bug21726()
+{
+ const char *create_table[]=
+ {
+ "DROP TABLE IF EXISTS t1",
+ "CREATE TABLE t1 (i INT)",
+ "INSERT INTO t1 VALUES (1)",
+ };
+ const char *update_query= "UPDATE t1 SET i= LAST_INSERT_ID(i + 1)";
+ int rc;
+ my_ulonglong insert_id;
+ const char *select_query= "SELECT * FROM t1";
+ MYSQL_RES *result;
+
+ DBUG_ENTER("test_bug21726");
+ myheader("test_bug21726");
+
+ fill_tables(create_table, sizeof(create_table) / sizeof(*create_table));
+
+ rc= mysql_query(mysql, update_query);
+ myquery(rc);
+ insert_id= mysql_insert_id(mysql);
+ DIE_UNLESS(insert_id == 2);
+
+ rc= mysql_query(mysql, update_query);
+ myquery(rc);
+ insert_id= mysql_insert_id(mysql);
+ DIE_UNLESS(insert_id == 3);
+
+ rc= mysql_query(mysql, select_query);
+ myquery(rc);
+ insert_id= mysql_insert_id(mysql);
+ DIE_UNLESS(insert_id == 3);
+ result= mysql_store_result(mysql);
+ mysql_free_result(result);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ BUG#23383: mysql_affected_rows() returns different values than
+ mysql_stmt_affected_rows()
+
+ Test that both mysql_affected_rows() and mysql_stmt_affected_rows()
+ return -1 on error, 0 when no rows were affected, and (positive) row
+ count when some rows were affected.
+*/
+static void test_bug23383()
+{
+ const char *insert_query= "INSERT INTO t1 VALUES (1), (2)";
+ const char *update_query= "UPDATE t1 SET i= 4 WHERE i = 3";
+ MYSQL_STMT *stmt;
+ my_ulonglong row_count;
+ int rc;
+
+ DBUG_ENTER("test_bug23383");
+ myheader("test_bug23383");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (i INT UNIQUE)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, insert_query);
+ myquery(rc);
+ row_count= mysql_affected_rows(mysql);
+ DIE_UNLESS(row_count == 2);
+
+ rc= mysql_query(mysql, insert_query);
+ DIE_UNLESS(rc != 0);
+ row_count= mysql_affected_rows(mysql);
+ DIE_UNLESS(row_count == (my_ulonglong)-1);
+
+ rc= mysql_query(mysql, update_query);
+ myquery(rc);
+ row_count= mysql_affected_rows(mysql);
+ DIE_UNLESS(row_count == 0);
+
+ rc= mysql_query(mysql, "DELETE FROM t1");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ DIE_UNLESS(stmt != 0);
+
+ rc= mysql_stmt_prepare(stmt, insert_query, strlen(insert_query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ row_count= mysql_stmt_affected_rows(stmt);
+ DIE_UNLESS(row_count == 2);
+
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc != 0);
+ row_count= mysql_stmt_affected_rows(stmt);
+ DIE_UNLESS(row_count == (my_ulonglong)-1);
+
+ rc= mysql_stmt_prepare(stmt, update_query, strlen(update_query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ row_count= mysql_stmt_affected_rows(stmt);
+ DIE_UNLESS(row_count == 0);
+
+ rc= mysql_stmt_close(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ BUG#21635: MYSQL_FIELD struct's member strings seem to misbehave for
+ expression cols
+
+ Check that for MIN(), MAX(), COUNT() only MYSQL_FIELD::name is set
+ to either expression or its alias, and db, org_table, table,
+ org_name fields are empty strings.
+*/
+static void test_bug21635()
+{
+ const char *expr[]=
+ {
+ "MIN(i)", "MIN(i)",
+ "MIN(i) AS A1", "A1",
+ "MAX(i)", "MAX(i)",
+ "MAX(i) AS A2", "A2",
+ "COUNT(i)", "COUNT(i)",
+ "COUNT(i) AS A3", "A3",
+ };
+ char query[MAX_TEST_QUERY_LENGTH];
+ char *query_end;
+ MYSQL_RES *result;
+ MYSQL_FIELD *field;
+ unsigned int field_count, i, j;
+ int rc;
+
+ DBUG_ENTER("test_bug21635");
+ myheader("test_bug21635");
+
+ query_end= strxmov(query, "SELECT ", NullS);
+ for (i= 0; i < sizeof(expr) / sizeof(*expr) / 2; ++i)
+ query_end= strxmov(query_end, expr[i * 2], ", ", NullS);
+ query_end= strxmov(query_end - 2, " FROM t1 GROUP BY i", NullS);
+ DIE_UNLESS(query_end - query < MAX_TEST_QUERY_LENGTH);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (i INT)");
+ myquery(rc);
+ /*
+ We need this loop to ensure correct behavior with both constant and
+ non-constant tables.
+ */
+ for (j= 0; j < 2 ; j++)
+ {
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)");
+ myquery(rc);
+
+ rc= mysql_real_query(mysql, query, (ulong)(query_end - query));
+ myquery(rc);
+
+ result= mysql_use_result(mysql);
+ DIE_UNLESS(result);
+
+ field_count= mysql_field_count(mysql);
+ for (i= 0; i < field_count; ++i)
+ {
+ field= mysql_fetch_field_direct(result, i);
+ if (!opt_silent)
+ if (!opt_silent)
+ printf("%s -> %s ... ", expr[i * 2], field->name);
+ fflush(stdout);
+ DIE_UNLESS(field->db[0] == 0 && field->org_table[0] == 0 &&
+ field->table[0] == 0 && field->org_name[0] == 0);
+ DIE_UNLESS(strcmp(field->name, expr[i * 2 + 1]) == 0);
+ if (!opt_silent)
+ if (!opt_silent)
+ puts("OK");
+ }
+
+ mysql_free_result(result);
+ }
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Bug#24179 "select b into $var" fails with --cursor_protocol"
+ The failure is correct, check that the returned message is meaningful.
+*/
+
+static void test_bug24179()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+
+ DBUG_ENTER("test_bug24179");
+ myheader("test_bug24179");
+
+ stmt= open_cursor("select 1 into @a");
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc);
+ if (!opt_silent)
+ {
+ printf("Got error (as expected): %d %s\n",
+ mysql_stmt_errno(stmt),
+ mysql_stmt_error(stmt));
+ }
+ DIE_UNLESS(mysql_stmt_errno(stmt) == 1323);
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Bug#32265 Server returns different metadata if prepared statement is used
+*/
+
+static void test_bug32265()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_FIELD *field;
+ MYSQL_RES *metadata;
+
+ DBUG_ENTER("test_bug32265");
+ myheader("test_bug32265");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a INTEGER)");
+ myquery(rc);
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE VIEW v1 AS SELECT * FROM t1");
+ myquery(rc);
+
+ stmt= open_cursor("SELECT * FROM t1");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ metadata= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_field(metadata);
+ DIE_UNLESS(field);
+ DIE_UNLESS(strcmp(field->table, "t1") == 0);
+ DIE_UNLESS(strcmp(field->org_table, "t1") == 0);
+ DIE_UNLESS(strcmp(field->db, "client_test_db") == 0);
+ mysql_free_result(metadata);
+ mysql_stmt_close(stmt);
+
+ stmt= open_cursor("SELECT a '' FROM t1 ``");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ metadata= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_field(metadata);
+ DIE_UNLESS(strcmp(field->table, "") == 0);
+ DIE_UNLESS(strcmp(field->org_table, "t1") == 0);
+ DIE_UNLESS(strcmp(field->db, "client_test_db") == 0);
+ mysql_free_result(metadata);
+ mysql_stmt_close(stmt);
+
+ stmt= open_cursor("SELECT a '' FROM t1 ``");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ metadata= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_field(metadata);
+ DIE_UNLESS(strcmp(field->table, "") == 0);
+ DIE_UNLESS(strcmp(field->org_table, "t1") == 0);
+ DIE_UNLESS(strcmp(field->db, "client_test_db") == 0);
+ mysql_free_result(metadata);
+ mysql_stmt_close(stmt);
+
+ stmt= open_cursor("SELECT * FROM v1");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ metadata= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_field(metadata);
+ DIE_UNLESS(strcmp(field->table, "v1") == 0);
+ DIE_UNLESS(strcmp(field->org_table, "v1") == 0);
+ DIE_UNLESS(strcmp(field->db, "client_test_db") == 0);
+ mysql_free_result(metadata);
+ mysql_stmt_close(stmt);
+
+ stmt= open_cursor("SELECT * FROM v1 /* SIC */ GROUP BY 1");
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ metadata= mysql_stmt_result_metadata(stmt);
+ field= mysql_fetch_field(metadata);
+ DIE_UNLESS(strcmp(field->table, "v1") == 0);
+ DIE_UNLESS(strcmp(field->org_table, "v1") == 0);
+ DIE_UNLESS(strcmp(field->db, "client_test_db") == 0);
+ mysql_free_result(metadata);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Bug#28075 "COM_DEBUG crashes mysqld"
+*/
+
+static void test_bug28075()
+{
+ int rc;
+
+ DBUG_ENTER("test_bug28075");
+ myheader("test_bug28075");
+
+ rc= mysql_dump_debug_info(mysql);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_ping(mysql);
+ DIE_UNLESS(rc == 0);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Bug#27876 (SF with cyrillic variable name fails during execution (regression))
+*/
+
+static void test_bug27876()
+{
+ int rc;
+ MYSQL_RES *result;
+
+ uchar utf8_func[] =
+ {
+ 0xd1, 0x84, 0xd1, 0x83, 0xd0, 0xbd, 0xd0, 0xba,
+ 0xd1, 0x86, 0xd0, 0xb8, 0xd0, 0xb9, 0xd0, 0xba,
+ 0xd0, 0xb0,
+ 0x00
+ };
+
+ uchar utf8_param[] =
+ {
+ 0xd0, 0xbf, 0xd0, 0xb0, 0xd1, 0x80, 0xd0, 0xb0,
+ 0xd0, 0xbc, 0xd0, 0xb5, 0xd1, 0x82, 0xd1, 0x8a,
+ 0xd1, 0x80, 0x5f, 0xd0, 0xb2, 0xd0, 0xb5, 0xd1,
+ 0x80, 0xd1, 0x81, 0xd0, 0xb8, 0xd1, 0x8f,
+ 0x00
+ };
+
+ char query[500];
+
+ DBUG_ENTER("test_bug27876");
+ myheader("test_bug27876");
+
+ rc= mysql_query(mysql, "set names utf8");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "select version()");
+ myquery(rc);
+ result= mysql_store_result(mysql);
+ mytest(result);
+ mysql_free_result(result);
+
+ sprintf(query, "DROP FUNCTION IF EXISTS %s", (char*) utf8_func);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ sprintf(query,
+ "CREATE FUNCTION %s( %s VARCHAR(25))"
+ " RETURNS VARCHAR(25) DETERMINISTIC RETURN %s",
+ (char*) utf8_func, (char*) utf8_param, (char*) utf8_param);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+ sprintf(query, "SELECT %s(VERSION())", (char*) utf8_func);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+ result= mysql_store_result(mysql);
+ mytest(result);
+ mysql_free_result(result);
+
+ sprintf(query, "DROP FUNCTION %s", (char*) utf8_func);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "set names default");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Bug#28505: mysql_affected_rows() returns wrong value if CLIENT_FOUND_ROWS
+ flag is set.
+*/
+
+static void test_bug28505()
+{
+ my_ulonglong res;
+
+ myquery(mysql_query(mysql, "drop table if exists t1"));
+ myquery(mysql_query(mysql, "create table t1(f1 int primary key)"));
+ myquery(mysql_query(mysql, "insert into t1 values(1)"));
+ myquery(mysql_query(mysql,
+ "insert into t1 values(1) on duplicate key update f1=1"));
+ res= mysql_affected_rows(mysql);
+ DIE_UNLESS(!res);
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+
+/*
+ Bug#28934: server crash when receiving malformed com_execute packets
+*/
+
+static void test_bug28934()
+{
+ my_bool error= 0;
+ MYSQL_BIND bind[5];
+ MYSQL_STMT *stmt;
+ int cnt;
+
+ myquery(mysql_query(mysql, "drop table if exists t1"));
+ myquery(mysql_query(mysql, "create table t1(id int)"));
+
+ myquery(mysql_query(mysql, "insert into t1 values(1),(2),(3),(4),(5)"));
+ stmt= mysql_simple_prepare(mysql,"select * from t1 where id in(?,?,?,?,?)");
+ check_stmt(stmt);
+
+ memset (&bind, 0, sizeof (bind));
+ for (cnt= 0; cnt < 5; cnt++)
+ {
+ bind[cnt].buffer_type= MYSQL_TYPE_LONG;
+ bind[cnt].buffer= (char*)&cnt;
+ bind[cnt].buffer_length= 0;
+ }
+ myquery(mysql_stmt_bind_param(stmt, bind));
+
+ stmt->param_count=2;
+ error= mysql_stmt_execute(stmt);
+ DIE_UNLESS(error != 0);
+ myerror(NULL);
+ mysql_stmt_close(stmt);
+
+ myquery(mysql_query(mysql, "drop table t1"));
+}
+
+/*
+ Test mysql_change_user() C API and COM_CHANGE_USER
+*/
+
+static void test_change_user()
+{
+ char buff[256];
+ const char *user_pw= "mysqltest_pw";
+ const char *user_no_pw= "mysqltest_no_pw";
+ const char *pw= "password";
+ const char *db= "mysqltest_user_test_database";
+ int rc;
+ MYSQL* conn;
+ MYSQL_RES* res;
+ DBUG_ENTER("test_change_user");
+ myheader("test_change_user");
+
+ /* Prepare environment */
+ sprintf(buff, "drop database if exists %s", db);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff, "create database %s", db);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SET SQL_MODE=''");
+ myquery(rc);
+
+ sprintf(buff,
+ "grant select on %s.* to %s@'%%' identified by '%s'",
+ db,
+ user_pw,
+ pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff,
+ "grant select on %s.* to %s@'localhost' identified by '%s'",
+ db,
+ user_pw,
+ pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff,
+ "grant select on %s.* to %s@'%%'",
+ db,
+ user_no_pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff,
+ "grant select on %s.* to %s@'localhost'",
+ db,
+ user_no_pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ /* Try some combinations */
+ rc= mysql_change_user(conn, NULL, NULL, NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+
+ rc= mysql_change_user(conn, "", NULL, NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, "", "", NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, "", "", "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, NULL, "", "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+
+ rc= mysql_change_user(conn, NULL, NULL, "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, "", NULL, "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, user_pw, NULL, "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, user_pw, "", "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, user_pw, "", NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, user_pw, NULL, NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, user_pw, "", db);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, user_pw, NULL, db);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, user_pw, pw, db);
+ myquery(rc);
+
+ rc= mysql_change_user(conn, user_pw, pw, NULL);
+ myquery(rc);
+
+ rc= mysql_change_user(conn, user_pw, pw, "");
+ myquery(rc);
+
+ /* MDEV-14581 : Check that there are no warnings after change user.*/
+ rc = mysql_query(conn,"SIGNAL SQLSTATE '01000'");
+ myquery(rc);
+
+ rc = mysql_change_user(conn, user_pw, pw, "");
+ myquery(rc);
+
+ rc = mysql_query(conn, "SHOW WARNINGS");
+ myquery(rc);
+ res = mysql_store_result(conn);
+ rc = my_process_result_set(res);
+ DIE_UNLESS(rc == 0);
+ mysql_free_result(res);
+
+ rc= mysql_change_user(conn, user_no_pw, pw, db);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, user_no_pw, pw, "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, user_no_pw, pw, NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, user_no_pw, "", NULL);
+ myquery(rc);
+
+ rc= mysql_change_user(conn, user_no_pw, "", "");
+ myquery(rc);
+
+ rc= mysql_change_user(conn, user_no_pw, "", db);
+ myquery(rc);
+
+ rc= mysql_change_user(conn, user_no_pw, NULL, db);
+ myquery(rc);
+
+ rc= mysql_change_user(conn, "", pw, db);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, "", pw, "");
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, "", pw, NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, NULL, pw, NULL);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, NULL, NULL, db);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, NULL, "", db);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ rc= mysql_change_user(conn, "", "", db);
+ DIE_UNLESS(rc);
+ if (! opt_silent)
+ printf("Got error (as expected): %s\n", mysql_error(conn));
+
+ /* Cleanup the environment */
+
+ mysql_change_user(conn, opt_user, opt_password, current_db);
+
+ mysql_close(conn);
+
+ sprintf(buff, "drop database %s", db);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff, "drop user %s@'%%'", user_pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff, "drop user %s@'%%'", user_no_pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff, "drop user %s@'localhost'", user_pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ sprintf(buff, "drop user %s@'localhost'", user_no_pw);
+ rc= mysql_query(mysql, buff);
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Bug#27592 (stack overrun when storing datetime value using prepared statements)
+*/
+
+static void test_bug27592()
+{
+ const int NUM_ITERATIONS= 40;
+ int i;
+ int rc;
+ MYSQL_STMT *stmt= NULL;
+ MYSQL_BIND bind[1];
+ MYSQL_TIME time_val;
+
+ DBUG_ENTER("test_bug27592");
+ myheader("test_bug27592");
+
+ mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ mysql_query(mysql, "CREATE TABLE t1(c2 DATETIME)");
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES (?)");
+ DIE_UNLESS(stmt);
+
+ memset(bind, 0, sizeof(bind));
+
+ bind[0].buffer_type= MYSQL_TYPE_DATETIME;
+ bind[0].buffer= (char *) &time_val;
+ bind[0].length= NULL;
+
+ for (i= 0; i < NUM_ITERATIONS; i++)
+ {
+ time_val.year= 2007;
+ time_val.month= 6;
+ time_val.day= 7;
+ time_val.hour= 18;
+ time_val.minute= 41;
+ time_val.second= 3;
+
+ time_val.second_part=0;
+ time_val.neg=0;
+
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ }
+
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Bug#29687 mysql_stmt_store_result memory leak in libmysqld
+*/
+
+static void test_bug29687()
+{
+ const int NUM_ITERATIONS= 40;
+ int i;
+ int rc;
+ MYSQL_STMT *stmt= NULL;
+
+ DBUG_ENTER("test_bug29687");
+ myheader("test_bug29687");
+
+ stmt= mysql_simple_prepare(mysql, "SELECT 1 FROM dual WHERE 0=2");
+ DIE_UNLESS(stmt);
+
+ for (i= 0; i < NUM_ITERATIONS; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_store_result(stmt);
+ while (mysql_stmt_fetch(stmt)==0);
+ mysql_stmt_free_result(stmt);
+ }
+
+ mysql_stmt_close(stmt);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Bug #29692 Single row inserts can incorrectly report a huge number of
+ row insertions
+*/
+
+static void test_bug29692()
+{
+ MYSQL* conn;
+
+ if (!(conn= mysql_client_init(NULL)))
+ {
+ myerror("test_bug29692 init failed");
+ exit(1);
+ }
+
+ if (!(mysql_real_connect(conn, opt_host, opt_user,
+ opt_password, opt_db ? opt_db:"test", opt_port,
+ opt_unix_socket, CLIENT_FOUND_ROWS)))
+ {
+ myerror("test_bug29692 connection failed");
+ mysql_close(mysql);
+ exit(1);
+ }
+ myquery(mysql_query(conn, "drop table if exists t1"));
+ myquery(mysql_query(conn, "create table t1(f1 int)"));
+ myquery(mysql_query(conn, "insert into t1 values(1)"));
+ DIE_UNLESS(1 == mysql_affected_rows(conn));
+ myquery(mysql_query(conn, "drop table t1"));
+ mysql_close(conn);
+}
+
+/**
+ Bug#29306 Truncated data in MS Access with decimal (3,1) columns in a VIEW
+*/
+
+static void test_bug29306()
+{
+ MYSQL_FIELD *field;
+ int rc;
+ MYSQL_RES *res;
+
+ DBUG_ENTER("test_bug29306");
+ myheader("test_bug29306");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS tab17557");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS view17557");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE tab17557 (dd decimal (3,1))");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE VIEW view17557 as SELECT dd FROM tab17557");
+ myquery(rc);
+ rc= mysql_query(mysql, "INSERT INTO tab17557 VALUES (7.6)");
+ myquery(rc);
+
+ /* Checking the view */
+ res= mysql_list_fields(mysql, "view17557", NULL);
+ while ((field= mysql_fetch_field(res)))
+ {
+ if (! opt_silent)
+ {
+ printf("field name %s\n", field->name);
+ printf("field table %s\n", field->table);
+ printf("field decimals %d\n", field->decimals);
+ if (field->decimals < 1)
+ printf("Error! No decimals! \n");
+ printf("\n\n");
+ }
+ DIE_UNLESS(field->decimals == 1);
+ }
+ mysql_free_result(res);
+
+ rc= mysql_query(mysql, "DROP TABLE tab17557");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW view17557");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+/*
+ Bug#30472: libmysql doesn't reset charset, insert_id after succ.
+ mysql_change_user() call row insertions.
+*/
+
+static void bug30472_retrieve_charset_info(MYSQL *con,
+ char *character_set_name,
+ char *character_set_client,
+ char *character_set_results,
+ char *collation_connection)
+{
+ MYSQL_RES *rs;
+ MYSQL_ROW row;
+
+ /* Get the cached client character set name. */
+
+ strcpy(character_set_name, mysql_character_set_name(con));
+
+ /* Retrieve server character set information. */
+
+ DIE_IF(mysql_query(con, "SHOW VARIABLES LIKE 'character_set_client'"));
+ DIE_UNLESS(rs= mysql_store_result(con));
+ DIE_UNLESS(row= mysql_fetch_row(rs));
+ strcpy(character_set_client, row[1]);
+ mysql_free_result(rs);
+
+ DIE_IF(mysql_query(con, "SHOW VARIABLES LIKE 'character_set_results'"));
+ DIE_UNLESS(rs= mysql_store_result(con));
+ DIE_UNLESS(row= mysql_fetch_row(rs));
+ strcpy(character_set_results, row[1]);
+ mysql_free_result(rs);
+
+ DIE_IF(mysql_query(con, "SHOW VARIABLES LIKE 'collation_connection'"));
+ DIE_UNLESS(rs= mysql_store_result(con));
+ DIE_UNLESS(row= mysql_fetch_row(rs));
+ strcpy(collation_connection, row[1]);
+ mysql_free_result(rs);
+}
+
+static void test_bug30472()
+{
+ MYSQL con;
+
+ char character_set_name_1[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_client_1[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_results_1[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char collation_connnection_1[MY_CS_COLLATION_NAME_SIZE];
+
+ char character_set_name_2[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_client_2[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_results_2[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char collation_connnection_2[MY_CS_COLLATION_NAME_SIZE];
+
+ char character_set_name_3[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_client_3[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_results_3[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char collation_connnection_3[MY_CS_COLLATION_NAME_SIZE];
+
+ char character_set_name_4[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_client_4[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char character_set_results_4[MY_CS_CHARACTER_SET_NAME_SIZE];
+ char collation_connnection_4[MY_CS_COLLATION_NAME_SIZE];
+
+ /* Create a new connection. */
+
+ DIE_UNLESS(mysql_client_init(&con));
+
+ DIE_UNLESS(mysql_real_connect(&con,
+ opt_host,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test",
+ opt_port,
+ opt_unix_socket,
+ CLIENT_FOUND_ROWS));
+
+ /* Retrieve character set information. */
+
+ bug30472_retrieve_charset_info(&con,
+ character_set_name_1,
+ character_set_client_1,
+ character_set_results_1,
+ collation_connnection_1);
+
+ /* Switch client character set. */
+
+ DIE_IF(mysql_set_character_set(&con, "latin2"));
+
+ /* Retrieve character set information. */
+
+ bug30472_retrieve_charset_info(&con,
+ character_set_name_2,
+ character_set_client_2,
+ character_set_results_2,
+ collation_connnection_2);
+
+ /*
+ Check that
+ 1) character set has been switched and
+ 2) new character set is different from the original one.
+ */
+
+ DIE_UNLESS(strcmp(character_set_name_2, "latin2") == 0);
+ DIE_UNLESS(strcmp(character_set_client_2, "latin2") == 0);
+ DIE_UNLESS(strcmp(character_set_results_2, "latin2") == 0);
+ DIE_UNLESS(strcmp(collation_connnection_2, "latin2_general_ci") == 0);
+
+ DIE_UNLESS(strcmp(character_set_name_1, character_set_name_2) != 0);
+ DIE_UNLESS(strcmp(character_set_client_1, character_set_client_2) != 0);
+ DIE_UNLESS(strcmp(character_set_results_1, character_set_results_2) != 0);
+ DIE_UNLESS(strcmp(collation_connnection_1, collation_connnection_2) != 0);
+
+ /* Call mysql_change_user() with the same username, password, database. */
+
+ DIE_IF(mysql_change_user(&con,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test"));
+
+ /* Retrieve character set information. */
+
+ bug30472_retrieve_charset_info(&con,
+ character_set_name_3,
+ character_set_client_3,
+ character_set_results_3,
+ collation_connnection_3);
+
+ /* Check that character set information has been reset. */
+
+ DIE_UNLESS(strcmp(character_set_name_1, character_set_name_3) == 0);
+ DIE_UNLESS(strcmp(character_set_client_1, character_set_client_3) == 0);
+ DIE_UNLESS(strcmp(character_set_results_1, character_set_results_3) == 0);
+ DIE_UNLESS(strcmp(collation_connnection_1, collation_connnection_3) == 0);
+
+ /* Change connection-default character set in the client. */
+
+ mysql_options(&con, MYSQL_SET_CHARSET_NAME, "utf8");
+
+ /*
+ Call mysql_change_user() in order to check that new connection will
+ have UTF8 character set on the client and on the server.
+ */
+
+ DIE_IF(mysql_change_user(&con,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test"));
+
+ /* Retrieve character set information. */
+
+ bug30472_retrieve_charset_info(&con,
+ character_set_name_4,
+ character_set_client_4,
+ character_set_results_4,
+ collation_connnection_4);
+
+ /* Check that we have UTF8 on the server and on the client. */
+
+ DIE_UNLESS(strcmp(character_set_name_4, "utf8mb3") == 0);
+ DIE_UNLESS(strcmp(character_set_client_4, "utf8mb3") == 0);
+ DIE_UNLESS(strcmp(character_set_results_4, "utf8mb3") == 0);
+ DIE_UNLESS(strcmp(collation_connnection_4, "utf8mb3_general_ci") == 0);
+
+ /* That's it. Cleanup. */
+
+ mysql_close(&con);
+}
+
+static void bug20023_change_user(MYSQL *con)
+{
+ DIE_IF(mysql_change_user(con,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test"));
+}
+
+static my_bool query_str_variable(MYSQL *con,
+ const char *var_name,
+ char *str,
+ size_t len)
+{
+ MYSQL_RES *rs;
+ MYSQL_ROW row;
+
+ char query_buffer[MAX_TEST_QUERY_LENGTH];
+
+ my_bool is_null;
+
+ my_snprintf(query_buffer, sizeof (query_buffer),
+ "SELECT %s", var_name);
+
+ DIE_IF(mysql_query(con, query_buffer));
+ DIE_UNLESS(rs= mysql_store_result(con));
+ DIE_UNLESS(row= mysql_fetch_row(rs));
+
+ is_null= row[0] == NULL;
+
+ if (!is_null)
+ my_snprintf(str, len, "%s", row[0]);
+
+ mysql_free_result(rs);
+
+ return is_null;
+}
+
+static my_bool query_int_variable(MYSQL *con,
+ const char *var_name,
+ int *var_value)
+{
+ char str[32];
+ my_bool is_null= query_str_variable(con, var_name, str, sizeof(str));
+
+ if (!is_null)
+ *var_value= atoi(str);
+
+ return is_null;
+}
+
+static void test_bug20023()
+{
+ MYSQL con;
+
+ int sql_big_selects_orig= 0;
+ /*
+ Type of max_join_size is ha_rows, which might be ulong or off_t
+ depending on the platform or configure options. Preserve the string
+ to avoid type overflow pitfalls.
+ */
+ char max_join_size_orig[32];
+
+ int sql_big_selects_2= 0;
+ int sql_big_selects_3= 0;
+ int sql_big_selects_4= 0;
+ int sql_big_selects_5= 0;
+
+ char query_buffer[MAX_TEST_QUERY_LENGTH];
+
+ /* Create a new connection. */
+
+ DIE_UNLESS(mysql_client_init(&con));
+
+ DIE_UNLESS(mysql_real_connect(&con,
+ opt_host,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test",
+ opt_port,
+ opt_unix_socket,
+ CLIENT_FOUND_ROWS));
+
+ /***********************************************************************
+ Remember original SQL_BIG_SELECTS, MAX_JOIN_SIZE values.
+ ***********************************************************************/
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_orig);
+
+ query_str_variable(&con,
+ "@@global.max_join_size",
+ max_join_size_orig,
+ sizeof(max_join_size_orig));
+
+ /***********************************************************************
+ Test that COM_CHANGE_USER resets the SQL_BIG_SELECTS to the initial value.
+ ***********************************************************************/
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_2);
+
+ /* Check that SQL_BIG_SELECTS is reset properly. */
+
+ DIE_UNLESS(sql_big_selects_orig == sql_big_selects_2);
+
+ /***********************************************************************
+ Test that if MAX_JOIN_SIZE set to non-default value,
+ SQL_BIG_SELECTS will be 0.
+ ***********************************************************************/
+
+ /* Set MAX_JOIN_SIZE to some non-default value. */
+
+ DIE_IF(mysql_query(&con, "SET @@global.max_join_size = 10000"));
+ DIE_IF(mysql_query(&con, "SET @@session.max_join_size = default"));
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_3);
+
+ /* Check that SQL_BIG_SELECTS is 0. */
+
+ DIE_UNLESS(sql_big_selects_3 == 0);
+
+ /***********************************************************************
+ Test that if MAX_JOIN_SIZE set to default value,
+ SQL_BIG_SELECTS will be 1.
+ ***********************************************************************/
+
+ /* Set MAX_JOIN_SIZE to the default value (2^64-1). */
+
+ DIE_IF(mysql_query(&con, "SET @@global.max_join_size = cast(-1 as unsigned int)"));
+ DIE_IF(mysql_query(&con, "SET @@session.max_join_size = default"));
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_4);
+
+ /* Check that SQL_BIG_SELECTS is 1. */
+
+ DIE_UNLESS(sql_big_selects_4 == 1);
+
+ /***********************************************************************
+ Restore MAX_JOIN_SIZE.
+ Check that SQL_BIG_SELECTS will be the original one.
+ ***********************************************************************/
+
+ /* Restore MAX_JOIN_SIZE. */
+
+ my_snprintf(query_buffer,
+ sizeof (query_buffer),
+ "SET @@global.max_join_size = %s",
+ max_join_size_orig);
+
+ DIE_IF(mysql_query(&con, query_buffer));
+
+ DIE_IF(mysql_query(&con, "SET @@global.max_join_size = cast(-1 as unsigned int)"));
+ DIE_IF(mysql_query(&con, "SET @@session.max_join_size = default"));
+
+ /* Issue COM_CHANGE_USER. */
+
+ bug20023_change_user(&con);
+
+ /* Query SQL_BIG_SELECTS. */
+
+ query_int_variable(&con,
+ "@@session.sql_big_selects",
+ &sql_big_selects_5);
+
+ /* Check that SQL_BIG_SELECTS is 1. */
+
+ DIE_UNLESS(sql_big_selects_5 == sql_big_selects_orig);
+
+ /***********************************************************************
+ That's it. Cleanup.
+ ***********************************************************************/
+
+ mysql_close(&con);
+}
+
+static void bug31418_impl()
+{
+ MYSQL con;
+
+ my_bool is_null;
+ int rc= 0;
+
+ /* Create a new connection. */
+
+ DIE_UNLESS(mysql_client_init(&con));
+
+ DIE_UNLESS(mysql_real_connect(&con,
+ opt_host,
+ opt_user,
+ opt_password,
+ opt_db ? opt_db : "test",
+ opt_port,
+ opt_unix_socket,
+ CLIENT_FOUND_ROWS));
+
+ /***********************************************************************
+ Check that lock is free:
+ - IS_FREE_LOCK() should return 1;
+ - IS_USED_LOCK() should return NULL;
+ ***********************************************************************/
+
+ is_null= query_int_variable(&con,
+ "IS_FREE_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && rc);
+
+ is_null= query_int_variable(&con,
+ "IS_USED_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(is_null);
+
+ /***********************************************************************
+ Acquire lock and check the lock status (the lock must be in use):
+ - IS_FREE_LOCK() should return 0;
+ - IS_USED_LOCK() should return non-zero thread id;
+ ***********************************************************************/
+
+ query_int_variable(&con, "GET_LOCK('bug31418', 1)", &rc);
+ DIE_UNLESS(rc);
+
+ is_null= query_int_variable(&con,
+ "IS_FREE_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && !rc);
+
+ is_null= query_int_variable(&con,
+ "IS_USED_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && rc);
+
+ /***********************************************************************
+ Issue COM_CHANGE_USER command and check the lock status
+ (the lock must be free):
+ - IS_FREE_LOCK() should return 1;
+ - IS_USED_LOCK() should return NULL;
+ **********************************************************************/
+
+ bug20023_change_user(&con);
+
+ is_null= query_int_variable(&con,
+ "IS_FREE_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(!is_null && rc);
+
+ is_null= query_int_variable(&con,
+ "IS_USED_LOCK('bug31418')",
+ &rc);
+ DIE_UNLESS(is_null);
+
+ /***********************************************************************
+ That's it. Cleanup.
+ ***********************************************************************/
+
+ mysql_close(&con);
+}
+
+static void test_bug31418()
+{
+ /* Run test case for BUG#31418 for three different connections. */
+
+ bug31418_impl();
+
+ bug31418_impl();
+
+ bug31418_impl();
+}
+
+
+
+/**
+ Bug#31669 Buffer overflow in mysql_change_user()
+*/
+
+#define LARGE_BUFFER_SIZE 2048
+#define OLD_USERNAME_CHAR_LENGTH 16
+
+static void test_bug31669()
+{
+ int rc;
+ static char buff[LARGE_BUFFER_SIZE+1];
+#ifndef EMBEDDED_LIBRARY
+ static char user[OLD_USERNAME_CHAR_LENGTH+1];
+ static char db[NAME_CHAR_LEN+1];
+ static char query[LARGE_BUFFER_SIZE*2];
+#endif
+ MYSQL* conn;
+
+ DBUG_ENTER("test_bug31669");
+ myheader("test_bug31669");
+
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, NULL, NULL, NULL);
+ DIE_UNLESS(rc);
+
+ rc= mysql_change_user(conn, "", "", "");
+ DIE_UNLESS(rc);
+
+ memset(buff, 'a', sizeof(buff) - 1);
+ buff[sizeof(buff) - 1]= 0;
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_change_user(conn, buff, buff, buff);
+ DIE_UNLESS(rc);
+
+ rc = mysql_change_user(conn, opt_user, opt_password, current_db);
+ DIE_UNLESS(!rc);
+
+#ifndef EMBEDDED_LIBRARY
+ memset(db, 'a', sizeof(db));
+ db[NAME_CHAR_LEN]= 0;
+ strxmov(query, "CREATE DATABASE IF NOT EXISTS ", db, NullS);
+ rc= mysql_query(conn, query);
+ myquery(rc);
+
+ memset(user, 'b', sizeof(user));
+ user[OLD_USERNAME_CHAR_LENGTH]= 0;
+ memset(buff, 'c', sizeof(buff));
+ buff[LARGE_BUFFER_SIZE]= 0;
+ strxmov(query, "GRANT ALL PRIVILEGES ON *.* TO '", user, "'@'%' IDENTIFIED BY "
+ "'", buff, "' WITH GRANT OPTION", NullS);
+ rc= mysql_query(conn, query);
+ myquery(rc);
+
+ strxmov(query, "GRANT ALL PRIVILEGES ON *.* TO '", user, "'@'localhost' IDENTIFIED BY "
+ "'", buff, "' WITH GRANT OPTION", NullS);
+ rc= mysql_query(conn, query);
+ myquery(rc);
+
+ rc= mysql_query(conn, "FLUSH PRIVILEGES");
+ myquery(rc);
+
+ rc= mysql_change_user(conn, user, buff, db);
+ DIE_UNLESS(!rc);
+
+ user[OLD_USERNAME_CHAR_LENGTH-1]= 'a';
+ rc= mysql_change_user(conn, user, buff, db);
+ DIE_UNLESS(rc);
+
+ user[OLD_USERNAME_CHAR_LENGTH-1]= 'b';
+ buff[LARGE_BUFFER_SIZE-1]= 'd';
+ rc= mysql_change_user(conn, user, buff, db);
+ DIE_UNLESS(rc);
+
+ buff[LARGE_BUFFER_SIZE-1]= 'c';
+ db[NAME_CHAR_LEN-1]= 'e';
+ rc= mysql_change_user(conn, user, buff, db);
+ DIE_UNLESS(rc);
+
+ mysql_close(conn);
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ db[NAME_CHAR_LEN-1]= 'a';
+ rc= mysql_change_user(conn, user, buff, db);
+ DIE_UNLESS(!rc);
+
+ rc= mysql_change_user(conn, user + 1, buff + 1, db + 1);
+ DIE_UNLESS(rc);
+
+ rc = mysql_change_user(conn, opt_user, opt_password, current_db);
+ DIE_UNLESS(!rc);
+
+ strxmov(query, "DROP DATABASE ", db, NullS);
+ rc= mysql_query(conn, query);
+ myquery(rc);
+
+ strxmov(query, "DELETE FROM mysql.user WHERE User='", user, "'", NullS);
+ rc= mysql_query(conn, query);
+ myquery(rc);
+ DIE_UNLESS(mysql_affected_rows(conn) == 2);
+#endif
+
+ mysql_close(conn);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Bug#28386 the general log is incomplete
+*/
+
+static void test_bug28386()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ MYSQL_BIND bind;
+ const char hello[]= "hello world!";
+
+ DBUG_ENTER("test_bug28386");
+ myheader("test_bug28386");
+
+ rc= mysql_query(mysql, "select @@global.log_output");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ DIE_UNLESS(result);
+
+ row= mysql_fetch_row(result);
+ if (! strstr(row[0], "TABLE"))
+ {
+ mysql_free_result(result);
+ if (! opt_silent)
+ printf("Skipping the test since logging to tables is not enabled\n");
+ /* Log output is not to tables */
+ DBUG_VOID_RETURN;
+ }
+ mysql_free_result(result);
+
+ enable_query_logs(1);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT ?");
+ check_stmt(stmt);
+
+ memset(&bind, 0, sizeof(bind));
+
+ bind.buffer_type= MYSQL_TYPE_STRING;
+ bind.buffer= (void *) hello;
+ bind.buffer_length= sizeof(hello);
+
+ mysql_stmt_bind_param(stmt, &bind);
+ mysql_stmt_send_long_data(stmt, 0, hello, sizeof(hello));
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ rc= mysql_stmt_reset(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_close(stmt);
+ DIE_UNLESS(!rc);
+
+ rc= mysql_query(mysql, "select * from mysql.general_log where "
+ "command_type='Close stmt' or "
+ "command_type='Reset stmt' or "
+ "command_type='Long Data'");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ DIE_UNLESS(mysql_num_rows(result) == 3);
+
+ mysql_free_result(result);
+
+ restore_query_logs();
+
+ DBUG_VOID_RETURN;
+}
+
+
+static void test_wl4166_1()
+{
+ MYSQL_STMT *stmt;
+ int int_data;
+ char str_data[50];
+ char tiny_data;
+ short small_data;
+ longlong big_data;
+ float real_data;
+ double double_data;
+ ulong length[7];
+ my_bool is_null[7];
+ MYSQL_BIND my_bind[7];
+ int rc;
+ int i;
+
+ myheader("test_wl4166_1");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS table_4166");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE table_4166(col1 tinyint NOT NULL, "
+ "col2 varchar(15), col3 int, "
+ "col4 smallint, col5 bigint, "
+ "col6 float, col7 double, "
+ "colX varchar(10) default NULL)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql,
+ "INSERT INTO table_4166(col1, col2, col3, col4, col5, col6, col7) "
+ "VALUES(?, ?, ?, ?, ?, ?, ?)");
+ check_stmt(stmt);
+
+ verify_param_count(stmt, 7);
+
+ bzero(my_bind, sizeof(my_bind));
+ /* tinyint */
+ my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+ my_bind[0].buffer= (void *)&tiny_data;
+ /* string */
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (void *)str_data;
+ my_bind[1].buffer_length= 1000; /* Max string length */
+ /* integer */
+ my_bind[2].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[2].buffer= (void *)&int_data;
+ /* short */
+ my_bind[3].buffer_type= MYSQL_TYPE_SHORT;
+ my_bind[3].buffer= (void *)&small_data;
+ /* bigint */
+ my_bind[4].buffer_type= MYSQL_TYPE_LONGLONG;
+ my_bind[4].buffer= (void *)&big_data;
+ /* float */
+ my_bind[5].buffer_type= MYSQL_TYPE_FLOAT;
+ my_bind[5].buffer= (void *)&real_data;
+ /* double */
+ my_bind[6].buffer_type= MYSQL_TYPE_DOUBLE;
+ my_bind[6].buffer= (void *)&double_data;
+
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].length= &length[i];
+ my_bind[i].is_null= &is_null[i];
+ is_null[i]= 0;
+ }
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ int_data= 320;
+ small_data= 1867;
+ big_data= 1000;
+ real_data= 2;
+ double_data= 6578.001;
+
+ /* now, execute the prepared statement to insert 10 records.. */
+ for (tiny_data= 0; tiny_data < 10; tiny_data++)
+ {
+ length[1]= sprintf(str_data, "MySQL%d", int_data);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ int_data += 25;
+ small_data += 10;
+ big_data += 100;
+ real_data += 1;
+ double_data += 10.09;
+ }
+
+ /* force a re-prepare with some DDL */
+
+ rc= mysql_query(mysql,
+ "ALTER TABLE table_4166 change colX colX varchar(20) default NULL");
+ myquery(rc);
+
+ /*
+ execute the prepared statement again,
+ without changing the types of parameters already bound.
+ */
+
+ for (tiny_data= 50; tiny_data < 60; tiny_data++)
+ {
+ length[1]= sprintf(str_data, "MySQL%d", int_data);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ int_data += 25;
+ small_data += 10;
+ big_data += 100;
+ real_data += 1;
+ double_data += 10.09;
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE table_4166");
+ myquery(rc);
+}
+
+
+static void test_wl4166_2()
+{
+ MYSQL_STMT *stmt;
+ int c_int;
+ MYSQL_TIME d_date;
+ MYSQL_BIND bind_out[2];
+ int rc;
+
+ myheader("test_wl4166_2");
+
+ rc= mysql_query(mysql, "SET SQL_MODE=''");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t1 (c_int int, d_date date)");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "insert into t1 (c_int, d_date) values (42, '1948-05-15')");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "select * from t1");
+ check_stmt(stmt);
+
+ bzero(bind_out, sizeof(bind_out));
+ bind_out[0].buffer_type= MYSQL_TYPE_LONG;
+ bind_out[0].buffer= (void*) &c_int;
+
+ bind_out[1].buffer_type= MYSQL_TYPE_DATE;
+ bind_out[1].buffer= (void*) &d_date;
+
+ rc= mysql_stmt_bind_result(stmt, bind_out);
+ check_execute(stmt, rc);
+
+ /* int -> varchar transition */
+
+ rc= mysql_query(mysql,
+ "alter table t1 change column c_int c_int varchar(11)");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(c_int == 42);
+ DIE_UNLESS(d_date.year == 1948);
+ DIE_UNLESS(d_date.month == 5);
+ DIE_UNLESS(d_date.day == 15);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ /* varchar to int retrieval with truncation */
+
+ rc= mysql_query(mysql, "update t1 set c_int='abcde'");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute_r(stmt, rc);
+
+ DIE_UNLESS(c_int == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ /* alter table and increase the number of columns */
+ rc= mysql_query(mysql, "alter table t1 add column d_int int");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute_r(stmt, rc);
+
+ rc= mysql_stmt_reset(stmt);
+ check_execute(stmt, rc);
+
+ /* decrease the number of columns */
+ rc= mysql_query(mysql, "alter table t1 drop d_date, drop d_int");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute_r(stmt, rc);
+
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/**
+ Test how warnings generated during assignment of parameters
+ are (currently not) preserve in case of reprepare.
+*/
+
+static void test_wl4166_3()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND my_bind[1];
+ MYSQL_TIME tm[1];
+
+ myheader("test_wl4166_3");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create table t1 (year datetime)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "insert into t1 (year) values (?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_DATETIME;
+ my_bind[0].buffer= &tm[0];
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ tm[0].year= 10000;
+ tm[0].month= 1; tm[0].day= 1;
+ tm[0].hour= 1; tm[0].minute= 1; tm[0].second= 1;
+ tm[0].second_part= 0; tm[0].neg= 0;
+
+ /* Cause a statement reprepare */
+ rc= mysql_query(mysql, "alter table t1 add column c int");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ /*
+ The warning about data truncation when assigning a parameter is lost.
+ This is a bug.
+ */
+ my_process_warnings(mysql, 0);
+
+ verify_col_data("t1", "year", "0000-00-00 00:00:00");
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+
+/**
+ Test that long data parameters, as well as parameters
+ that were originally in a different character set, are
+ preserved in case of reprepare.
+*/
+
+static void test_wl4166_4()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ const char *stmt_text;
+ MYSQL_BIND bind_array[2];
+
+ /* Represented as numbers to keep UTF8 tools from clobbering them. */
+ const char *koi8= "\xee\xd5\x2c\x20\xda\xc1\x20\xd2\xd9\xc2\xc1\xcc\xcb\xd5";
+ const char *cp1251= "\xcd\xf3\x2c\x20\xe7\xe0\x20\xf0\xfb\xe1\xe0\xeb\xea\xf3";
+ char buf1[16], buf2[16];
+ ulong buf1_len, buf2_len;
+
+ myheader("test_wl4166_4");
+
+ rc= mysql_query(mysql, "drop table if exists t1");
+ myquery(rc);
+
+ /*
+ Create table with binary columns, set session character set to cp1251,
+ client character set to koi8, and make sure that there is conversion
+ on insert and no conversion on select
+ */
+ rc= mysql_query(mysql,
+ "create table t1 (c1 varbinary(255), c2 varbinary(255))");
+ myquery(rc);
+ rc= mysql_query(mysql, "set character_set_client=koi8r, "
+ "character_set_connection=cp1251, "
+ "character_set_results=koi8r");
+ myquery(rc);
+
+ bzero((char*) bind_array, sizeof(bind_array));
+
+ bind_array[0].buffer_type= MYSQL_TYPE_STRING;
+
+ bind_array[1].buffer_type= MYSQL_TYPE_STRING;
+ bind_array[1].buffer= (void *) koi8;
+ bind_array[1].buffer_length= strlen(koi8);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ stmt_text= "insert into t1 (c1, c2) values (?, ?)";
+
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ mysql_stmt_bind_param(stmt, bind_array);
+
+ mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8));
+
+ /* Cause a reprepare at statement execute */
+ rc= mysql_query(mysql, "alter table t1 add column d int");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ stmt_text= "select c1, c2 from t1";
+
+ /* c1 and c2 are binary so no conversion will be done on select */
+ rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bind_array[0].buffer= buf1;
+ bind_array[0].buffer_length= sizeof(buf1);
+ bind_array[0].length= &buf1_len;
+
+ bind_array[1].buffer= buf2;
+ bind_array[1].buffer_length= sizeof(buf2);
+ bind_array[1].length= &buf2_len;
+
+ mysql_stmt_bind_result(stmt, bind_array);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(buf1_len == strlen(cp1251));
+ DIE_UNLESS(buf2_len == strlen(cp1251));
+ DIE_UNLESS(!memcmp(buf1, cp1251, buf1_len));
+ DIE_UNLESS(!memcmp(buf2, cp1251, buf1_len));
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "set names default");
+ myquery(rc);
+}
+
+/**
+ Bug#36004 mysql_stmt_prepare resets the list of warnings
+*/
+
+static void test_bug36004()
+{
+ int rc, warning_count= 0;
+ MYSQL_STMT *stmt;
+
+ DBUG_ENTER("test_bug36004");
+ myheader("test_bug36004");
+
+ rc= mysql_query(mysql, "drop table if exists inexistant");
+ myquery(rc);
+
+ DIE_UNLESS(mysql_warning_count(mysql) == 1);
+ query_int_variable(mysql, "@@warning_count", &warning_count);
+ DIE_UNLESS(warning_count);
+
+ stmt= mysql_simple_prepare(mysql, "select 1");
+ check_stmt(stmt);
+
+ DIE_UNLESS(mysql_warning_count(mysql) == 0);
+ query_int_variable(mysql, "@@warning_count", &warning_count);
+ DIE_UNLESS(warning_count);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(mysql_warning_count(mysql) == 0);
+ mysql_stmt_close(stmt);
+
+ query_int_variable(mysql, "@@warning_count", &warning_count);
+ DIE_UNLESS(warning_count);
+
+ stmt= mysql_simple_prepare(mysql, "drop table if exists inexistant");
+ check_stmt(stmt);
+
+ query_int_variable(mysql, "@@warning_count", &warning_count);
+ DIE_UNLESS(warning_count == 0);
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Test that COM_REFRESH issues a implicit commit.
+*/
+
+static void test_wl4284_1()
+{
+ int rc;
+ MYSQL_ROW row;
+ MYSQL_RES *result;
+
+ DBUG_ENTER("test_wl4284_1");
+ myheader("test_wl4284_1");
+
+ /* set AUTOCOMMIT to OFF */
+ rc= mysql_autocommit(mysql, FALSE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS trans");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE trans (a INT) ENGINE= InnoDB");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO trans VALUES(1)");
+ myquery(rc);
+
+ rc= mysql_refresh(mysql, REFRESH_GRANT | REFRESH_TABLES);
+ myquery(rc);
+
+ rc= mysql_rollback(mysql);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SELECT * FROM trans");
+ myquery(rc);
+
+ result= mysql_use_result(mysql);
+ mytest(result);
+
+ row= mysql_fetch_row(result);
+ mytest(row);
+
+ mysql_free_result(result);
+
+ /* set AUTOCOMMIT to ON */
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE trans");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+static void test_bug38486(void)
+{
+ MYSQL_STMT *stmt;
+ const char *stmt_text;
+ unsigned long type= CURSOR_TYPE_READ_ONLY;
+
+ DBUG_ENTER("test_bug38486");
+ myheader("test_bug38486");
+
+ stmt= mysql_stmt_init(mysql);
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*)&type);
+ stmt_text= "CREATE TABLE t1 (a INT)";
+ mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ mysql_stmt_execute(stmt);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_stmt_init(mysql);
+ mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*)&type);
+ stmt_text= "INSERT INTO t1 VALUES (1)";
+ mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
+ mysql_stmt_execute(stmt);
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Bug# 33831 mysql_real_connect() should fail if
+ given an already connected MYSQL handle.
+*/
+
+static void test_bug33831(void)
+{
+ MYSQL *l_mysql;
+
+ DBUG_ENTER("test_bug33831");
+
+ if (!(l_mysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ DIE_UNLESS(0);
+ }
+ if (!(mysql_real_connect(l_mysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ DIE_UNLESS(0);
+ }
+
+ if (mysql_real_connect(l_mysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0))
+ {
+ myerror("connection should have failed");
+ DIE_UNLESS(0);
+ }
+
+ mysql_close(l_mysql);
+
+ DBUG_VOID_RETURN;
+}
+
+
+static void test_bug40365(void)
+{
+ uint rc, i;
+ MYSQL_STMT *stmt= 0;
+ MYSQL_BIND my_bind[2];
+ my_bool is_null[2]= {0};
+ MYSQL_TIME tm[2];
+
+ DBUG_ENTER("test_bug40365");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1(c1 DATETIME, \
+ c2 DATE)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 VALUES(?, ?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 2);
+
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_DATETIME;
+ my_bind[1].buffer_type= MYSQL_TYPE_DATE;
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ my_bind[i].buffer= (void *) &tm[i];
+ my_bind[i].is_null= &is_null[i];
+ }
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ for (i= 0; i < (int) array_elements(my_bind); i++)
+ {
+ tm[i].neg= 0;
+ tm[i].second_part= 0;
+ tm[i].year= 2009;
+ tm[i].month= 2;
+ tm[i].day= 29;
+ tm[i].hour= 0;
+ tm[i].minute= 0;
+ tm[i].second= 0;
+ }
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT * FROM t1");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_bind_result(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ if (!opt_silent)
+ fprintf(stdout, "\n");
+
+ for (i= 0; i < array_elements(my_bind); i++)
+ {
+ if (!opt_silent)
+ fprintf(stdout, "\ntime[%d]: %02d-%02d-%02d ",
+ i, tm[i].year, tm[i].month, tm[i].day);
+ DIE_UNLESS(tm[i].year == 0);
+ DIE_UNLESS(tm[i].month == 0);
+ DIE_UNLESS(tm[i].day == 0);
+ }
+ mysql_stmt_close(stmt);
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Subtest for Bug#43560. Verifies that a loss of connection on the server side
+ is handled well by the mysql_stmt_execute() call, i.e., no SIGSEGV due to
+ a vio socket that is cleared upon closed connection.
+
+ Assumes the presence of the close_conn_after_stmt_execute debug feature in
+ the server. Verifies that it is connected to a debug server before proceeding
+ with the test.
+ */
+static void test_bug43560(void)
+{
+ MYSQL* conn;
+ uint rc;
+ MYSQL_STMT *stmt= 0;
+ MYSQL_BIND bind;
+ my_bool is_null= 0;
+ char buffer[256];
+ const uint BUFSIZE= sizeof(buffer);
+ const char* values[] = {"eins", "zwei", "drei", "viele", NULL};
+ const char insert_str[] = "INSERT INTO t1 (c2) VALUES (?)";
+ unsigned long length;
+ const unsigned int drop_db= opt_drop_db;
+
+ DBUG_ENTER("test_bug43560");
+ myheader("test_bug43560");
+
+ /* Make sure we only run against a debug server. */
+ if (!strstr(mysql->server_version, "debug"))
+ {
+ fprintf(stdout, "Skipping test_bug43560: server not DEBUG version\n");
+ DBUG_VOID_RETURN;
+ }
+ if (opt_unix_socket)
+ {
+ fprintf(stdout, "Skipping test_bug43560: connected via UNIX socket\n");
+ DBUG_VOID_RETURN;
+ }
+ /*
+ Set up a separate connection for this test to avoid messing up the
+ general MYSQL object used in other subtests. Use TCP protocol to avoid
+ problems with the buffer semantics of AF_UNIX, and turn off auto reconnect.
+ */
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+
+ rc= mysql_query(conn, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(conn,
+ "CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT, c2 CHAR(10))");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(conn);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, insert_str, strlen(insert_str));
+ check_execute(stmt, rc);
+
+ bind.buffer_type= MYSQL_TYPE_STRING;
+ bind.buffer_length= BUFSIZE;
+ bind.buffer= buffer;
+ bind.is_null= &is_null;
+ bind.length= &length;
+ rc= mysql_stmt_bind_param(stmt, &bind);
+ check_execute(stmt, rc);
+
+ /* First execute; should succeed. */
+ strncpy(buffer, values[0], BUFSIZE);
+ length= strlen(buffer);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /*
+ Set up the server to close this session's server-side socket after
+ next execution of prep statement.
+ */
+ rc= mysql_query(conn,"SET SESSION debug='+d,close_conn_after_stmt_execute'");
+ myquery(rc);
+
+ /* Second execute; should fail due to socket closed during execution. */
+ strncpy(buffer, values[1], BUFSIZE);
+ length= strlen(buffer);
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt) == CR_SERVER_LOST);
+
+ /*
+ Third execute; should fail (connection already closed), or SIGSEGV in
+ case of a Bug#43560 type regression in which case the whole test fails.
+ */
+ strncpy(buffer, values[2], BUFSIZE);
+ length= strlen(buffer);
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc && (mysql_stmt_errno(stmt) == CR_SERVER_LOST ||
+ mysql_stmt_errno(stmt) == CR_SERVER_GONE_ERROR));
+
+ opt_drop_db= 0;
+ client_disconnect(conn);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ opt_drop_db= drop_db;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Bug#36326: nested transaction and select
+*/
+
+static void test_bug36326()
+{
+ int rc;
+
+ DBUG_ENTER("test_bug36326");
+ myheader("test_bug36326");
+
+ if (! is_query_cache_available())
+ {
+ fprintf(stdout, "Skipping test_bug36326: Query cache not available.\n");
+ DBUG_VOID_RETURN;
+ }
+
+ rc= mysql_autocommit(mysql, TRUE);
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a INTEGER)");
+ myquery(rc);
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "set @save_query_cache_type="
+ "@@global.query_cache_type,"
+ "@save_query_cache_size="
+ "@@global.query_cache_size");
+ myquery(rc);
+ rc= mysql_query(mysql, "SET GLOBAL query_cache_type = 1");
+ myquery(rc);
+ rc= mysql_query(mysql, "SET LOCAL query_cache_type = 1");
+ myquery(rc);
+ rc= mysql_query(mysql, "SET GLOBAL query_cache_size = 1048576");
+ myquery(rc);
+ DIE_UNLESS(!(mysql->server_status & SERVER_STATUS_IN_TRANS));
+ DIE_UNLESS(mysql->server_status & SERVER_STATUS_AUTOCOMMIT);
+ rc= mysql_query(mysql, "BEGIN");
+ myquery(rc);
+ DIE_UNLESS(mysql->server_status & SERVER_STATUS_IN_TRANS);
+ rc= mysql_query(mysql, "SELECT * FROM t1");
+ myquery(rc);
+ rc= my_process_result(mysql);
+ DIE_UNLESS(rc == 1);
+ rc= mysql_rollback(mysql);
+ myquery(rc);
+ rc= mysql_query(mysql, "ROLLBACK");
+ myquery(rc);
+ DIE_UNLESS(!(mysql->server_status & SERVER_STATUS_IN_TRANS));
+ rc= mysql_query(mysql, "SELECT * FROM t1");
+ myquery(rc);
+ DIE_UNLESS(!(mysql->server_status & SERVER_STATUS_IN_TRANS));
+ rc= my_process_result(mysql);
+ DIE_UNLESS(rc == 1);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "SET GLOBAL query_cache_size = @save_query_cache_size");
+ rc= mysql_query(mysql, "SET GLOBAL query_cache_type = @save_query_cache_type");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Bug#41078: With CURSOR_TYPE_READ_ONLY mysql_stmt_fetch() returns short
+ string value.
+*/
+
+static void test_bug41078(void)
+{
+ uint rc;
+ MYSQL_STMT *stmt= 0;
+ MYSQL_BIND param, result;
+ ulong cursor_type= CURSOR_TYPE_READ_ONLY;
+ ulong len;
+ char str[64];
+ const char param_str[]= "abcdefghijklmn";
+ my_bool is_null, error;
+
+ DBUG_ENTER("test_bug41078");
+
+ rc= mysql_query(mysql, "SET NAMES UTF8");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT ?");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor_type);
+ check_execute(stmt, rc);
+
+ bzero(&param, sizeof(param));
+ param.buffer_type= MYSQL_TYPE_STRING;
+ param.buffer= (void *) param_str;
+ len= sizeof(param_str) - 1;
+ param.length= &len;
+
+ rc= mysql_stmt_bind_param(stmt, &param);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ bzero(&result, sizeof(result));
+ result.buffer_type= MYSQL_TYPE_STRING;
+ result.buffer= str;
+ result.buffer_length= sizeof(str);
+ result.is_null= &is_null;
+ result.length= &len;
+ result.error= &error;
+
+ rc= mysql_stmt_bind_result(stmt, &result);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ DIE_UNLESS(len == sizeof(param_str) - 1 && !strcmp(str, param_str));
+
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Bug#45010: invalid memory reads during parsing some strange statements
+*/
+static void test_bug45010()
+{
+ int rc;
+ const char query1[]= "select a.\x80",
+ query2[]= "describe `table\xef";
+
+ DBUG_ENTER("test_bug45010");
+ myheader("test_bug45010");
+
+ rc= mysql_query(mysql, "set names utf8");
+ myquery(rc);
+
+ /* \x80 (-128) could be used as a index of ident_map. */
+ rc= mysql_real_query(mysql, query1, sizeof(query1) - 1);
+ DIE_UNLESS(rc);
+
+ /* \xef (-17) could be used to skip 3 bytes past the buffer end. */
+ rc= mysql_real_query(mysql, query2, sizeof(query2) - 1);
+ DIE_UNLESS(rc);
+
+ rc= mysql_query(mysql, "set names default");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Bug#44495: Prepared Statement:
+ CALL p(<x>) - `thd->protocol == &thd->protocol_text' failed
+*/
+
+static void test_bug44495()
+{
+ int rc;
+ MYSQL con;
+ MYSQL_STMT *stmt;
+
+ DBUG_ENTER("test_bug44495");
+ myheader("test_44495");
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE PROCEDURE p1(IN arg VARCHAR(25))"
+ " BEGIN SET @stmt = CONCAT('SELECT \"', arg, '\"');"
+ " PREPARE ps1 FROM @stmt;"
+ " EXECUTE ps1;"
+ " DROP PREPARE ps1;"
+ "END;");
+ myquery(rc);
+
+ DIE_UNLESS(mysql_client_init(&con));
+
+ DIE_UNLESS(mysql_real_connect(&con, opt_host, opt_user, opt_password,
+ current_db, opt_port, opt_unix_socket,
+ CLIENT_MULTI_RESULTS));
+
+ stmt= mysql_simple_prepare(&con, "CALL p1('abc')");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+
+ mysql_close(&con);
+
+ rc= mysql_query(mysql, "DROP PROCEDURE p1");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+static void test_bug53371()
+{
+ int rc;
+ MYSQL_RES *result;
+
+ myheader("test_bug53371");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP DATABASE IF EXISTS bug53371");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP USER 'testbug'@localhost");
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a INT)");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE DATABASE bug53371");
+ myquery(rc);
+ rc= mysql_query(mysql, "GRANT SELECT ON bug53371.* to 'testbug'@localhost");
+ myquery(rc);
+
+ rc= mysql_change_user(mysql, "testbug", NULL, "bug53371");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SHOW COLUMNS FROM client_test_db.t1");
+ DIE_UNLESS(rc);
+ DIE_UNLESS(mysql_errno(mysql) == 1142);
+
+ result= mysql_list_fields(mysql, "../client_test_db/t1", NULL);
+ DIE_IF(result);
+
+ result= mysql_list_fields(mysql, "#mysql50#/../client_test_db/t1", NULL);
+ DIE_IF(result);
+
+ rc= mysql_change_user(mysql, opt_user, opt_password, current_db);
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP DATABASE bug53371");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP USER 'testbug'@localhost");
+ myquery(rc);
+}
+
+
+
+/**
+ Bug#42373: libmysql can mess a connection at connect
+*/
+static void test_bug42373()
+{
+ int rc;
+ MYSQL con;
+ MYSQL_STMT *stmt;
+
+ DBUG_ENTER("test_bug42373");
+ myheader("test_42373");
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE PROCEDURE p1()"
+ " BEGIN"
+ " SELECT 1;"
+ " INSERT INTO t1 VALUES (2);"
+ "END;");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a INT)");
+ myquery(rc);
+
+ /* Try with a stored procedure. */
+ DIE_UNLESS(mysql_client_init(&con));
+
+ mysql_options(&con, MYSQL_INIT_COMMAND, "CALL p1()");
+
+ DIE_UNLESS(mysql_real_connect(&con, opt_host, opt_user, opt_password,
+ current_db, opt_port, opt_unix_socket,
+ CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS));
+
+ stmt= mysql_simple_prepare(&con, "SELECT a FROM t1");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 1);
+
+ mysql_stmt_close(stmt);
+ mysql_close(&con);
+
+ /* Now try with a multi-statement. */
+ DIE_UNLESS(mysql_client_init(&con));
+
+ mysql_options(&con, MYSQL_INIT_COMMAND,
+ "SELECT 3; INSERT INTO t1 VALUES (4)");
+
+ DIE_UNLESS(mysql_real_connect(&con, opt_host, opt_user, opt_password,
+ current_db, opt_port, opt_unix_socket,
+ CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS));
+
+ stmt= mysql_simple_prepare(&con, "SELECT a FROM t1");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= my_process_stmt_result(stmt);
+ DIE_UNLESS(rc == 2);
+
+ mysql_stmt_close(stmt);
+ mysql_close(&con);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP PROCEDURE p1");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Bug#54041: MySQL 5.0.92 fails when tests from Connector/C suite run
+*/
+
+static void test_bug54041_impl()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind;
+
+ DBUG_ENTER("test_bug54041");
+ myheader("test_bug54041");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a INT)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "SELECT a FROM t1 WHERE a > ?");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ memset(&bind, 0, sizeof(bind));
+
+ /* Any type that does not support long data handling. */
+ bind.buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_bind_param(stmt, &bind);
+ check_execute(stmt, rc);
+
+ /*
+ Trick the client API into sending a long data packet for
+ the parameter. Long data is only supported for string and
+ binary types.
+ */
+ stmt->params[0].buffer_type= MYSQL_TYPE_STRING;
+
+ rc= mysql_stmt_send_long_data(stmt, 0, "data", 5);
+ check_execute(stmt, rc);
+
+ /* Undo API violation. */
+ stmt->params[0].buffer_type= MYSQL_TYPE_LONG;
+
+ rc= mysql_stmt_execute(stmt);
+ /* Incorrect arguments. */
+ check_execute_r(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Bug#54041: MySQL 5.0.92 fails when tests from Connector/C suite run
+*/
+
+static void test_bug54041()
+{
+ enable_query_logs(0);
+ test_bug54041_impl();
+ disable_query_logs();
+ test_bug54041_impl();
+ restore_query_logs();
+}
+
+
+/**
+ Bug#47485: mysql_store_result returns a result set for a prepared statement
+*/
+static void test_bug47485()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *res;
+ MYSQL_BIND bind[2];
+ int rc;
+ const char* sql_select = "SELECT 1, 'a'";
+ int int_data;
+ char str_data[16];
+ my_bool is_null[2];
+ my_bool error[2];
+ unsigned long length[2];
+
+ DBUG_ENTER("test_bug47485");
+ myheader("test_bug47485");
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, sql_select, strlen(sql_select));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ res = mysql_store_result(mysql);
+ DIE_UNLESS(res == NULL);
+
+ mysql_stmt_reset(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ res = mysql_use_result(mysql);
+ DIE_UNLESS(res == NULL);
+
+ mysql_stmt_reset(stmt);
+
+ memset(bind, 0, sizeof(bind));
+ 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];
+
+ bind[1].buffer_type= MYSQL_TYPE_STRING;
+ bind[1].buffer= (char *)str_data;
+ bind[1].buffer_length= sizeof(str_data);
+ bind[1].is_null= &is_null[1];
+ bind[1].length= &length[1];
+ bind[1].error= &error[1];
+
+ rc= mysql_stmt_bind_result(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ ;
+
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_reset(stmt);
+
+ memset(bind, 0, sizeof(bind));
+ 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];
+
+ bind[1].buffer_type= MYSQL_TYPE_STRING;
+ bind[1].buffer= (char *)str_data;
+ bind[1].buffer_length= sizeof(str_data);
+ bind[1].is_null= &is_null[1];
+ bind[1].length= &length[1];
+ bind[1].error= &error[1];
+
+ rc= mysql_stmt_bind_result(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ ;
+
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Bug#58036 client utf32, utf16, ucs2 should be disallowed, they crash server
+*/
+static void test_bug58036()
+{
+ MYSQL *conn;
+ DBUG_ENTER("test_bug58036");
+ myheader("test_bug58036");
+
+ /* Part1: try to connect with ucs2 client character set */
+ conn= mysql_client_init(NULL);
+ mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2");
+
+ if (mysql_real_connect(conn, opt_host, opt_user,
+ opt_password, opt_db ? opt_db : "test",
+ opt_port, opt_unix_socket, 0))
+ {
+ if (!opt_silent)
+ printf("mysql_real_connect() succeeded (failure expected)\n");
+ mysql_close(conn);
+ DIE("");
+ }
+
+ if (!opt_silent)
+ printf("Got mysql_real_connect() error (expected): %s (%d)\n",
+ mysql_error(conn), mysql_errno(conn));
+ DIE_UNLESS(mysql_errno(conn) == ER_WRONG_VALUE_FOR_VAR ||
+ mysql_errno(conn)== CR_CANT_READ_CHARSET);
+ mysql_close(conn);
+
+
+ /*
+ Part2:
+ - connect with latin1
+ - then change client character set to ucs2
+ - then try mysql_change_user()
+ */
+ conn= mysql_client_init(NULL);
+ mysql_options(conn, MYSQL_SET_CHARSET_NAME, "latin1");
+ if (!mysql_real_connect(conn, opt_host, opt_user,
+ opt_password, opt_db ? opt_db : "test",
+ opt_port, opt_unix_socket, 0))
+ {
+ if (!opt_silent)
+ printf("mysql_real_connect() failed: %s (%d)\n",
+ mysql_error(conn), mysql_errno(conn));
+ mysql_close(conn);
+ DIE("");
+ }
+
+ mysql_options(conn, MYSQL_SET_CHARSET_NAME, "ucs2");
+ if (!mysql_change_user(conn, opt_user, opt_password, NULL))
+ {
+ if (!opt_silent)
+ printf("mysql_change_user() succeeded, error expected!");
+ mysql_close(conn);
+ DIE("");
+ }
+
+ if (!opt_silent)
+ printf("Got mysql_change_user() error (expected): %s (%d)\n",
+ mysql_error(conn), mysql_errno(conn));
+ mysql_close(conn);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Bug#49972: Crash in prepared statements.
+
+ The following case lead to a server crash:
+ - Use binary protocol;
+ - Prepare a statement with OUT-parameter;
+ - Execute the statement;
+ - Cause re-prepare of the statement (change dependencies);
+ - Execute the statement again -- crash here.
+*/
+
+static void test_bug49972()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+
+ MYSQL_BIND in_param_bind;
+ MYSQL_BIND out_param_bind;
+ int int_data;
+ my_bool is_null;
+
+ DBUG_ENTER("test_bug49972");
+ myheader("test_bug49972");
+
+ rc= mysql_query(mysql, "DROP FUNCTION IF EXISTS f1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE FUNCTION f1() RETURNS INT RETURN 1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE PROCEDURE p1(IN a INT, OUT b INT) SET b = a");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(mysql, "CALL p1((SELECT f1()), ?)");
+ check_stmt(stmt);
+
+ bzero((char *) &in_param_bind, sizeof (in_param_bind));
+
+ in_param_bind.buffer_type= MYSQL_TYPE_LONG;
+ in_param_bind.buffer= (char *) &int_data;
+ int_data= 0;
+ in_param_bind.length= 0;
+ in_param_bind.is_null= 0;
+
+ rc= mysql_stmt_bind_param(stmt, &in_param_bind);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ {
+ bzero(&out_param_bind, sizeof (out_param_bind));
+
+ out_param_bind.buffer_type= MYSQL_TYPE_LONG;
+ out_param_bind.is_null= &is_null;
+ out_param_bind.buffer= &int_data;
+ out_param_bind.buffer_length= sizeof (int_data);
+
+ rc= mysql_stmt_bind_result(stmt, &out_param_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ rc= mysql_stmt_fetch(stmt);
+ DBUG_ASSERT(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_next_result(stmt);
+ mysql_stmt_fetch(stmt);
+ }
+
+ rc= mysql_query(mysql, "DROP FUNCTION f1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE FUNCTION f1() RETURNS INT RETURN 1");
+ myquery(rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ {
+ bzero(&out_param_bind, sizeof (out_param_bind));
+
+ out_param_bind.buffer_type= MYSQL_TYPE_LONG;
+ out_param_bind.is_null= &is_null;
+ out_param_bind.buffer= &int_data;
+ out_param_bind.buffer_length= sizeof (int_data);
+
+ rc= mysql_stmt_bind_result(stmt, &out_param_bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ rc= mysql_stmt_fetch(stmt);
+ DBUG_ASSERT(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_next_result(stmt);
+ mysql_stmt_fetch(stmt);
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP PROCEDURE p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP FUNCTION f1");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Bug #56976: Severe Denial Of Service in prepared statements
+*/
+static void test_bug56976()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ int rc;
+ const char* query = "SELECT LENGTH(?)";
+ char *long_buffer;
+ unsigned long i, packet_len = 256 * 1024L;
+ unsigned long dos_len = 35000000;
+
+ DBUG_ENTER("test_bug56976");
+ myheader("test_bug56976");
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_TINY_BLOB;
+
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ long_buffer= (char*) my_malloc(PSI_NOT_INSTRUMENTED, packet_len, MYF(0));
+ DIE_UNLESS(long_buffer);
+
+ memset(long_buffer, 'a', packet_len);
+
+ for (i= 0; i < dos_len / packet_len; i++)
+ {
+ rc= mysql_stmt_send_long_data(stmt, 0, long_buffer, packet_len);
+ check_execute(stmt, rc);
+ }
+
+ my_free(long_buffer);
+ rc= mysql_stmt_execute(stmt);
+
+ DIE_UNLESS(rc && mysql_stmt_errno(stmt) == ER_UNKNOWN_ERROR);
+
+ mysql_stmt_close(stmt);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Test that CLIENT_PROGRESS works.
+*/
+
+uint progress_stage, progress_max_stage, progress_count;
+
+static void report_progress(const MYSQL *mysql __attribute__((unused)),
+ uint stage, uint max_stage,
+ double progress __attribute__((unused)),
+ const char *proc_info __attribute__((unused)),
+ uint proc_info_length __attribute__((unused)))
+{
+ progress_stage= stage;
+ progress_max_stage= max_stage;
+ progress_count++;
+}
+
+
+static void test_progress_reporting()
+{
+ int rc, i;
+ MYSQL* conn;
+
+ /* Progress reporting doesn't work yet with embedded server */
+ if (embedded_server_arg_count)
+ return;
+
+ myheader("test_progress_reporting");
+
+
+ conn= client_connect(CLIENT_PROGRESS_OBSOLETE, MYSQL_PROTOCOL_TCP, 0);
+ DIE_UNLESS(conn->client_flag & CLIENT_PROGRESS_OBSOLETE);
+
+ mysql_options(conn, MYSQL_PROGRESS_CALLBACK, (void*) report_progress);
+ rc= mysql_query(conn, "set @save=@@global.progress_report_time");
+ myquery(rc);
+ rc= mysql_query(conn, "set @@global.progress_report_time=1");
+ myquery(rc);
+
+ rc= mysql_query(conn, "drop table if exists t1,t2");
+ myquery(rc);
+ rc= mysql_query(conn, "create table t1 (f2 varchar(255)) engine=aria");
+ myquery(rc);
+ rc= mysql_query(conn, "create table t2 like t1");
+ myquery(rc);
+ rc= mysql_query(conn, "insert into t1 (f2) values (repeat('a',100)),(repeat('b',200)),(repeat('c',202)),(repeat('d',202)),(repeat('e',202)),(repeat('f',202)),(repeat('g',23))");
+ myquery(rc);
+ for (i= 0 ; i < 5 ; i++)
+ {
+ rc= mysql_query(conn, "insert into t2 (f2) select f2 from t1");
+ myquery(rc);
+ rc= mysql_query(conn, "insert into t1 (f2) select f2 from t2");
+ myquery(rc);
+ }
+
+ progress_stage= progress_max_stage= progress_count= 0;
+ rc= mysql_query(conn, "alter table t1 add f1 int primary key auto_increment, order by f2");
+ myquery(rc);
+ if (!opt_silent)
+ printf("Got progress_count: %u stage: %u max_stage: %u\n",
+ progress_count, progress_stage, progress_max_stage);
+ DIE_UNLESS(progress_count > 0 && progress_stage >=2 && progress_max_stage == 3);
+
+ progress_stage= progress_max_stage= progress_count= 0;
+ rc= mysql_query(conn, "create index f2 on t1 (f2)");
+ myquery(rc);
+ if (!opt_silent)
+ printf("Got progress_count: %u stage: %u max_stage: %u\n",
+ progress_count, progress_stage, progress_max_stage);
+ DIE_UNLESS(progress_count > 0 && progress_stage >=2 && progress_max_stage == 2);
+
+ progress_stage= progress_max_stage= progress_count= 0;
+ rc= mysql_query(conn, "drop index f2 on t1");
+ myquery(rc);
+ if (!opt_silent)
+ printf("Got progress_count: %u stage: %u max_stage: %u\n",
+ progress_count, progress_stage, progress_max_stage);
+ DIE_UNLESS(progress_count > 0 && progress_stage >=2 && progress_max_stage == 2);
+
+ rc= mysql_query(conn, "set @@global.progress_report_time=@save");
+ myquery(rc);
+ mysql_close(conn);
+}
+
+/**
+ MDEV-3885 - connection suicide via mysql_kill() causes assertion in server
+*/
+
+static void test_mdev3885()
+{
+ int rc;
+ MYSQL *conn;
+
+ myheader("test_mdev3885");
+ conn= client_connect(0, MYSQL_PROTOCOL_TCP, 0);
+ rc= mysql_kill(conn, mysql_thread_id(conn));
+ DIE_UNLESS(rc);
+ mysql_close(conn);
+}
+
+
+/**
+ Bug#57058 SERVER_QUERY_WAS_SLOW not wired up.
+*/
+
+static void test_bug57058()
+{
+ MYSQL_RES *res;
+ int rc;
+
+ DBUG_ENTER("test_bug57058");
+ myheader("test_bug57058");
+
+ rc= mysql_query(mysql, "set @@session.long_query_time=0.1");
+ myquery(rc);
+
+ DIE_UNLESS(!(mysql->server_status & SERVER_QUERY_WAS_SLOW));
+
+ rc= mysql_query(mysql, "select sleep(1)");
+ myquery(rc);
+
+ /*
+ Important: the flag is sent in the last EOF packet of
+ the query, the one which ends the result. Read the
+ result to see the "slow" status.
+ */
+ res= mysql_store_result(mysql);
+
+ DIE_UNLESS(mysql->server_status & SERVER_QUERY_WAS_SLOW);
+
+ mysql_free_result(res);
+
+ rc= mysql_query(mysql, "set @@session.long_query_time=default");
+ myquery(rc);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Bug#11766854: 60075: MYSQL_LOAD_CLIENT_PLUGIN DOESN'T CLEAR ERROR
+*/
+
+static void test_bug11766854()
+{
+ struct st_mysql_client_plugin *plugin;
+
+ DBUG_ENTER("test_bug11766854");
+ if (!getenv("QA_AUTH_CLIENT_SO"))
+ DBUG_VOID_RETURN;
+
+ myheader("test_bug11766854");
+
+ plugin= mysql_load_plugin(mysql, "foo", -1, 0);
+ DIE_UNLESS(plugin == 0);
+
+ plugin= mysql_load_plugin(mysql, "qa_auth_client", -1, 0);
+ DIE_UNLESS(plugin != 0);
+ DIE_IF(mysql_errno(mysql));
+
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Bug#12337762: 60075: MYSQL_LIST_FIELDS() RETURNS WRONG CHARSET FOR
+ CHAR/VARCHAR/TEXT COLUMNS IN VIEWS
+*/
+static void test_bug12337762()
+{
+ int rc,i=0;
+ MYSQL_RES *result;
+ MYSQL_FIELD *field;
+ unsigned int tab_charsetnr[3]= {0};
+
+ DBUG_ENTER("test_bug12337762");
+ myheader("test_bug12337762");
+
+ /*
+ Creating table with specific charset.
+ */
+ rc= mysql_query(mysql, "drop table if exists charset_tab");
+ rc= mysql_query(mysql, "create table charset_tab("\
+ "txt1 varchar(32) character set Latin1,"\
+ "txt2 varchar(32) character set Latin1 collate latin1_bin,"\
+ "txt3 varchar(32) character set utf8 collate utf8_bin"\
+ ")");
+
+ DIE_UNLESS(rc == 0);
+ DIE_IF(mysql_errno(mysql));
+
+ /*
+ Creating view from table created earlier.
+ */
+ rc= mysql_query(mysql, "drop view if exists charset_view");
+ rc= mysql_query(mysql, "create view charset_view as "\
+ "select * from charset_tab;");
+ DIE_UNLESS(rc == 0);
+ DIE_IF(mysql_errno(mysql));
+
+ /*
+ Checking field information for table.
+ */
+ result= mysql_list_fields(mysql, "charset_tab", NULL);
+ DIE_IF(mysql_errno(mysql));
+ i=0;
+ while((field= mysql_fetch_field(result)))
+ {
+ printf("field name %s\n", field->name);
+ printf("field table %s\n", field->table);
+ printf("field type %d\n", field->type);
+ printf("field charset %d\n", field->charsetnr);
+ tab_charsetnr[i++]= field->charsetnr;
+ printf("\n");
+ }
+ mysql_free_result(result);
+
+ /*
+ Checking field information for view.
+ */
+ result= mysql_list_fields(mysql, "charset_view", NULL);
+ DIE_IF(mysql_errno(mysql));
+ i=0;
+ while((field= mysql_fetch_field(result)))
+ {
+ printf("field name %s\n", field->name);
+ printf("field table %s\n", field->table);
+ printf("field type %d\n", field->type);
+ printf("field charset %d\n", field->charsetnr);
+ printf("\n");
+ /*
+ charset value for field must be same for both, view and table.
+ */
+ DIE_UNLESS(field->charsetnr == tab_charsetnr[i++]);
+ }
+ mysql_free_result(result);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ MDEV-4603: mysql_stmt_reset doesn't clear
+ all result sets (from stored procedures).
+ This test requires also fix for MDEV-4604
+*/
+static void test_mdev4603()
+{
+ MYSQL *my;
+ MYSQL_STMT *stmt;
+ int i, rc;
+ int a[] = {10,20,30};
+ MYSQL_BIND bind[3];
+
+ myheader("test_mdev4603");
+ my= mysql_client_init(NULL);
+
+ if (!mysql_real_connect(my, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, CLIENT_MULTI_RESULTS))
+ DIE("mysql_real_connect failed");
+
+ /* 1st test:
+ use a procedure with out param
+ */
+ rc= mysql_query(my, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE PROCEDURE p1(OUT p_out VARCHAR(19), IN p_in INT, INOUT p_inout INT)"
+ "BEGIN "
+ " SET p_in = 300, p_out := 'This is OUT param', p_inout = 200; "
+ " SELECT p_inout, p_in, substring(p_out, 9);"
+ "END");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ DIE_UNLESS(stmt != NULL);
+
+ rc= mysql_stmt_prepare(stmt, "CALL P1(?,?,?)", 14);
+ DIE_UNLESS(rc == 0);
+
+ DIE_UNLESS(mysql_stmt_param_count(stmt) == 3);
+
+ memset(bind, 0, sizeof(MYSQL_BIND) * 3);
+ for (i=0; i < 3; i++)
+ {
+ bind[i].buffer= &a[i];
+ bind[i].buffer_type= MYSQL_TYPE_LONG;
+ }
+ bind[0].buffer_type= MYSQL_TYPE_NULL;
+ rc= mysql_stmt_bind_param(stmt, bind);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_reset(stmt);
+ DIE_UNLESS(rc == 0);
+
+ /*connection shouldn't be blocked now */
+
+ rc= mysql_query(mysql, "DROP PROCEDURE p1");
+ myquery(rc);
+
+ /* 2nd test:
+ reset all result sets */
+ rc= mysql_query(my, "CREATE PROCEDURE p1() "
+ "BEGIN"
+ " SELECT 1,2,3 FROM DUAL;"
+ " SELECT 'foo' FROM DUAL;"
+ "END");
+ myquery(rc);
+
+ rc= mysql_stmt_prepare(stmt, "CALL P1()", 9);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_reset(stmt);
+ DIE_UNLESS(rc == 0);
+
+ /* 3rd test:
+ mysql_stmt_close should also flush all pending
+ result sets
+ */
+
+ rc= mysql_stmt_prepare(stmt, "CALL P1()", 9);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_close(stmt);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_query(my, "DROP PROCEDURE p1");
+ myquery(rc);
+
+ mysql_close(my);
+}
+
+/*
+ BUG 11754979 - 46675: ON DUPLICATE KEY UPDATE AND UPDATECOUNT() POSSIBLY WRONG
+*/
+
+static void test_bug11754979()
+{
+ MYSQL* conn;
+ DBUG_ENTER("test_bug11754979");
+
+ myheader("test_bug11754979");
+ DIE_UNLESS((conn= mysql_client_init(NULL)));
+ DIE_UNLESS(mysql_real_connect(conn, opt_host, opt_user,
+ opt_password, opt_db ? opt_db:"test", opt_port,
+ opt_unix_socket, CLIENT_FOUND_ROWS));
+ myquery(mysql_query(conn, "DROP TABLE IF EXISTS t1"));
+ myquery(mysql_query(conn, "CREATE TABLE t1(id INT, label CHAR(1), PRIMARY KEY(id))"));
+ myquery(mysql_query(conn, "INSERT INTO t1(id, label) VALUES (1, 'a')"));
+ myquery(mysql_query(conn, "INSERT INTO t1(id, label) VALUES (1, 'a') "
+ "ON DUPLICATE KEY UPDATE id = 4"));
+ DIE_UNLESS(mysql_affected_rows(conn) == 2);
+ myquery(mysql_query(conn, "DROP TABLE t1"));
+ mysql_close(conn);
+
+ DBUG_VOID_RETURN;
+}
+
+static void test_ps_sp_out_params()
+{
+ MYSQL *my;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ char buffer[20];
+ int status, rc;
+
+ myheader("test_ps_sp_out_params");
+ my= mysql_client_init(NULL);
+
+ if (!mysql_real_connect(my, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, CLIENT_MULTI_RESULTS))
+ DIE("mysql_real_connect failed");
+
+ rc= mysql_query(my, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(my,
+ "CREATE PROCEDURE p1(OUT out_param VARCHAR(19)) "
+ "BEGIN"
+ " SELECT 'foo' FROM DUAL;"
+ " SET out_param='foo';"
+ " SELECT 'foo' FROM DUAL;"
+ "END");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(my);
+
+ rc= mysql_stmt_prepare(stmt, "CALL P1(?)", 10);
+ DIE_UNLESS(rc==0);
+
+ DIE_UNLESS(mysql_stmt_param_count(stmt) == 1);
+
+ memset(bind, 0, sizeof(MYSQL_BIND));
+ memset(buffer, 0, sizeof buffer);
+ bind[0].buffer= buffer;
+ bind[0].buffer_length= sizeof(buffer);
+ bind[0].buffer_type= MYSQL_TYPE_STRING;
+
+ mysql_stmt_bind_param(stmt, bind);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ do {
+ if (mysql_stmt_field_count(stmt))
+ {
+ /* since server sends a status packet at the end,
+ there must follow at least one additional packet */
+ DIE_UNLESS(mysql_more_results(stmt->mysql));
+
+ mysql_stmt_bind_result(stmt, bind);
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc== 0);
+
+ DIE_UNLESS(strcmp(buffer, "foo") == 0);
+ }
+ status= mysql_stmt_next_result(stmt);
+ } while (status == 0);
+
+ rc= mysql_stmt_reset(stmt);
+ DIE_UNLESS(rc== 0);
+
+ mysql_stmt_close(stmt);
+ mysql_close(my);
+
+ printf("end\n");
+}
+
+/*
+ Bug#13001491: MYSQL_REFRESH CRASHES WHEN STORED ROUTINES ARE RUN CONCURRENTLY.
+*/
+static void test_bug13001491()
+{
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+ MYSQL *c;
+
+ myheader("test_bug13001491");
+
+ my_snprintf(query, MAX_TEST_QUERY_LENGTH,
+ "GRANT ALL PRIVILEGES ON *.* TO mysqltest_u1@%s",
+ opt_host ? opt_host : "'localhost'");
+
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ my_snprintf(query, MAX_TEST_QUERY_LENGTH,
+ "GRANT RELOAD ON *.* TO mysqltest_u1@%s",
+ opt_host ? opt_host : "'localhost'");
+
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ c= mysql_client_init(NULL);
+
+ DIE_UNLESS(mysql_real_connect(c, opt_host, "mysqltest_u1", NULL,
+ current_db, opt_port, opt_unix_socket,
+ CLIENT_MULTI_STATEMENTS |
+ CLIENT_MULTI_RESULTS));
+
+ rc= mysql_query(c, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(c,
+ "CREATE PROCEDURE p1() "
+ "BEGIN "
+ " DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; "
+ " SELECT COUNT(*) "
+ " FROM INFORMATION_SCHEMA.PROCESSLIST "
+ " GROUP BY user "
+ " ORDER BY NULL "
+ " INTO @a; "
+ "END");
+ myquery(rc);
+
+ rc= mysql_query(c, "CALL p1()");
+ myquery(rc);
+
+ mysql_free_result(mysql_store_result(c));
+
+ /* Check that mysql_refresh() succeeds without REFRESH_LOG. */
+ rc= mysql_refresh(c, REFRESH_GRANT |
+ REFRESH_TABLES | REFRESH_HOSTS |
+ REFRESH_STATUS | REFRESH_THREADS);
+ myquery(rc);
+
+ /*
+ Check that mysql_refresh(REFRESH_LOG) does not crash the server even if it
+ fails. mysql_refresh(REFRESH_LOG) fails when error log points to unavailable
+ location.
+ */
+ mysql_refresh(c, REFRESH_LOG);
+
+ rc= mysql_query(c, "DROP PROCEDURE p1");
+ myquery(rc);
+
+ mysql_close(c);
+ c= NULL;
+
+ my_snprintf(query, MAX_TEST_QUERY_LENGTH,
+ "DROP USER mysqltest_u1@%s",
+ opt_host ? opt_host : "'localhost'");
+
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+}
+
+static void test_mdev4326()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind;
+ char query[]= "SELECT * FROM mysql.user LIMIT ?";
+ char str_data[]= "1";
+ unsigned long length= 0;
+ int int_data= 1;
+ int rc, count;
+ my_bool is_null= 0;
+ my_bool error= 0;
+ myheader("test_mdev4326");
+
+ rc= mysql_change_user(mysql, opt_user, opt_password, "mysql");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SET GLOBAL general_log = 1");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ /* Numeric parameter test */
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ memset((char *)&bind, 0, sizeof(bind));
+ bind.buffer_type= MYSQL_TYPE_LONG;
+ bind.buffer= (char *)&int_data;
+ bind.is_null= &is_null;
+ bind.length= &length;
+ bind.error= &error;
+
+ rc= mysql_stmt_bind_param(stmt, &bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ count= 0;
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ count++;
+ DIE_UNLESS(count == 1);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ count= 0;
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ count++;
+ DIE_UNLESS(count == 1);
+ int_data= 0;
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ count= 0;
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ count++;
+ DIE_UNLESS(count == 0);
+ rc= mysql_stmt_close(stmt);
+ check_execute(stmt, rc);
+
+ /* String parameter test */
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ memset((char *)&bind, 0, sizeof(bind));
+ bind.buffer_type= MYSQL_TYPE_STRING;
+ bind.buffer= (char *)str_data;
+ length= bind.buffer_length= sizeof(str_data);
+ bind.is_null= &is_null;
+ bind.length= &length;
+ bind.error= &error;
+
+ rc= mysql_stmt_bind_param(stmt, &bind);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ count= 0;
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ count++;
+ DIE_UNLESS(count == 1);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ count= 0;
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ count++;
+ DIE_UNLESS(count == 1);
+ str_data[0]= '0';
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ count= 0;
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ count++;
+ DIE_UNLESS(count == 0);
+ rc= mysql_stmt_close(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_change_user(mysql, opt_user, opt_password, current_db);
+ myquery(rc);
+}
+
+/* Test uses MYSQL_PROTOCOL_SOCKET, not on Windows */
+
+#ifndef _WIN32
+/**
+ BUG#17512527: LIST HANDLING INCORRECT IN MYSQL_PRUNE_STMT_LIST()
+*/
+static void test_bug17512527()
+{
+ MYSQL *conn;
+ MYSQL_STMT *stmt1, *stmt2;
+ unsigned long thread_id;
+ char query[MAX_TEST_QUERY_LENGTH];
+ int rc;
+
+ conn= client_connect(0, MYSQL_PROTOCOL_SOCKET, 1);
+
+ stmt1 = mysql_stmt_init(conn);
+ check_stmt(stmt1);
+ rc= mysql_stmt_prepare(stmt1, STRING_WITH_LEN("SELECT 1"));
+ check_execute(stmt1, rc);
+
+ stmt2 = mysql_stmt_init(conn);
+ check_stmt(stmt2);
+
+ thread_id= mysql_thread_id(conn);
+ sprintf(query, "KILL %lu", thread_id);
+ if (thread_query(query))
+ exit(1);
+
+ rc= mysql_stmt_prepare(stmt2, STRING_WITH_LEN("SELECT 2"));
+ check_execute(stmt2, rc);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute_r(stmt1, rc);
+
+ rc= mysql_stmt_execute(stmt2);
+ check_execute(stmt2, rc);
+
+ mysql_close(conn);
+
+ mysql_stmt_close(stmt2);
+ mysql_stmt_close(stmt1);
+}
+#endif
+
+
+/*
+ Check compressed protocol
+*/
+
+static void test_compressed_protocol()
+{
+ MYSQL *mysql_local;
+ MYSQL_STMT *stmt;
+ char query[4096], *end;
+ int i;
+ int rc;
+ myheader("test_compressed_protocol");
+
+ if (!(mysql_local= mysql_client_init(NULL)))
+ {
+ fprintf(stderr, "\n mysql_client_init() failed");
+ exit(1);
+ }
+
+ if (!(mysql_real_connect(mysql_local, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, CLIENT_COMPRESS)))
+ {
+ fprintf(stderr, "\n connection failed(%s)", mysql_error(mysql_local));
+ exit(1);
+ }
+ mysql_options(mysql_local,MYSQL_OPT_COMPRESS,NullS);
+
+ end= strmov(strfill(strmov(query, "select length(\""),1000,'a'),"\")");
+
+ for (i=0 ; i < 2 ; i++)
+ {
+ MYSQL_RES *res;
+ rc= mysql_real_query(mysql, query, (int) (end-query));
+ myquery(rc);
+ res= mysql_store_result(mysql);
+ DBUG_ASSERT(res != 0);
+ mysql_free_result(res);
+ }
+
+ /*
+ Special compression protocol feature - it can pack
+ multiple protocol commands inside the same compression packet.
+
+ mariadbclient does it when MYSQL_STMT is reused in multiple
+ mysql_stmt_prepare() calls. It sends then COM_STMT_CLOSE and
+ COM_STMT_PREPARE together in a single compression packet.
+
+ Let's test, how server can handle that. There can be bugs
+ (MDEV-28561)
+ */
+ stmt= mysql_stmt_init(mysql_local);
+ check_stmt(stmt);
+ for (i= 0; i < 2; i++)
+ {
+ rc= mysql_stmt_prepare(stmt, "DO 1", -1);
+ myquery(rc);
+ }
+ rc= mysql_stmt_close(stmt);
+ myquery(rc);
+
+ mysql_close(mysql_local);
+}
+
+/*
+ Check big packets
+*/
+
+static void test_big_packet()
+{
+ MYSQL *mysql_local;
+ char *query, *end;
+ /* We run the tests with a server with max packet size of 3200000 */
+ size_t big_packet= 31000000L;
+ int i;
+ MYSQL_PARAMETERS *mysql_params= mysql_get_parameters();
+ long org_max_allowed_packet= *mysql_params->p_max_allowed_packet;
+ long opt_net_buffer_length= *mysql_params->p_net_buffer_length;
+
+ myheader("test_big_packet");
+
+ query= (char*) my_malloc(PSI_NOT_INSTRUMENTED, big_packet+1024, MYF(MY_WME));
+ DIE_UNLESS(query);
+
+ if (!(mysql_local= mysql_client_init(NULL)))
+ {
+ fprintf(stderr, "\n mysql_client_init() failed");
+ exit(1);
+ }
+
+ if (!(mysql_real_connect(mysql_local, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ fprintf(stderr, "\n connection failed(%s)", mysql_error(mysql_local));
+ mysql_close(mysql_local);
+ exit(1);
+ }
+
+ *mysql_params->p_max_allowed_packet= big_packet+1000;
+ *mysql_params->p_net_buffer_length= 8L*256L*256L;
+
+ end= strmov(strfill(strmov(query, "select length(\""), big_packet,'a'),"\")");
+
+ for (i=0 ; i < 2 ; i++)
+ {
+ MYSQL_RES *res;
+ int rc= mysql_real_query(mysql, query, (int) (end-query));
+ myquery(rc);
+ res= mysql_store_result(mysql);
+ DBUG_ASSERT(res != 0);
+ mysql_free_result(res);
+ }
+
+ mysql_close(mysql_local);
+ my_free(query);
+
+ *mysql_params->p_max_allowed_packet= org_max_allowed_packet;
+ *mysql_params->p_net_buffer_length = opt_net_buffer_length;
+}
+
+
+static void test_prepare_analyze()
+{
+ MYSQL_STMT *stmt;
+ const char *query= "ANALYZE SELECT 1";
+ int rc;
+ myheader("test_prepare_analyze");
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ while (!(rc= mysql_stmt_fetch(stmt)))
+ ;
+
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_close(stmt);
+ check_execute(stmt, rc);
+}
+
+static void test_mdev12579()
+{
+ MYSQL_STMT *stmt= mysql_stmt_init(mysql);
+ MYSQL_BIND bind[2];
+ int rc;
+ long l=3;
+ const char *data = "123456";
+
+ rc= mysql_query(mysql, "CREATE TABLE mdev12579 (k integer,t LONGTEXT,b LONGBLOB,x integer)");
+ myquery(rc);
+
+ rc= mysql_stmt_prepare(stmt, "INSERT INTO mdev12579 VALUES (1,?,NULL,?)", -1);
+ myquery(rc);
+
+ rc= mysql_stmt_send_long_data(stmt, 0, data, 6);
+ rc= mysql_stmt_send_long_data(stmt, 0, data, 6);
+ rc= mysql_stmt_send_long_data(stmt, 0, data, 6);
+
+ memset(bind, 0, sizeof(MYSQL_BIND) * 2);
+ bind[0].buffer_type= MYSQL_TYPE_VAR_STRING;
+ bind[1].buffer_type= MYSQL_TYPE_LONG;
+ bind[1].buffer= &l;
+ mysql_stmt_bind_param(stmt, bind);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE mdev12579");
+ myquery(rc);
+}
+
+/* Test test_mdev14013 sql_mode=EMPTY_STRING_IS_NULL */
+
+static void test_mdev14013()
+{
+ MYSQL *lmysql;
+ MYSQL_STMT *stmt1;
+ MYSQL_BIND my_bind[2];
+ MYSQL_RES *result;
+ char str_data[20];
+ unsigned int count;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_mdev14013");
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a test connection ...");
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+ if (!(mysql_real_connect(lmysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ exit(1);
+ }
+ mysql_options(lmysql, MYSQL_OPT_RECONNECT, &my_true);
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+
+ /* set AUTOCOMMIT to ON*/
+ mysql_autocommit(lmysql, TRUE);
+
+ strmov(query, "SET SQL_MODE= \"EMPTY_STRING_IS_NULL\"");
+ if (!opt_silent)
+ fprintf(stdout, "\n With %s", query);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ rc= mysql_query(lmysql, "DROP TABLE IF EXISTS test_mdev14013");
+ myquery(rc);
+
+ rc= mysql_query(lmysql, "CREATE TABLE test_mdev14013(id int, val varchar(10))");
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_mdev14013(id,val) VALUES(?,?)");
+ stmt1= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt1);
+
+ verify_param_count(stmt1, 2);
+
+ /*
+ We need to bzero bind structure because mysql_stmt_bind_param checks all
+ its members.
+ */
+ bzero((char*) my_bind, sizeof(my_bind));
+
+ my_bind[0].buffer= (void *)&count;
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ count= 100;
+
+ strcpy(str_data,"");
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (char *) str_data;
+ my_bind[1].buffer_length= strlen(str_data);
+
+ rc= mysql_stmt_bind_param(stmt1, my_bind);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ verify_st_affected_rows(stmt1, 1);
+
+ rc= mysql_stmt_close(stmt1);
+
+ strmov(query, "SET SQL_MODE= default");
+ if (!opt_silent)
+ fprintf(stdout, "\n With %s\n", query);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ strmov(query, "INSERT INTO test_mdev14013(id,val) VALUES(?,?)");
+ stmt1= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt1);
+
+ count= 200;
+ rc= mysql_stmt_bind_param(stmt1, my_bind);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ verify_st_affected_rows(stmt1, 1);
+
+ rc= mysql_stmt_close(stmt1);
+ if (!opt_silent)
+ fprintf(stdout, "\n test_mdev14013(x) returned: %d", rc);
+ DIE_UNLESS( rc == 0);
+
+ rc= mysql_query(mysql, "SELECT id, val FROM test_mdev14013 order by id");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 2);
+ mysql_free_result(result);
+
+ rc= mysql_query(mysql, "SELECT id, val FROM test_mdev14013 where val is null");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+ mysql_free_result(result);
+
+ myquery(mysql_query(mysql, "drop table test_mdev14013"));
+ mysql_close(lmysql);
+}
+
+static void test_mdev14013_1()
+{
+ MYSQL *lmysql;
+ MYSQL_STMT *stmt1;
+ MYSQL_BIND my_bind[3];
+ char str_data[3][255];
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ myheader("test_mdev14013_1");
+
+ if (!opt_silent)
+ fprintf(stdout, "\n Establishing a test connection ...");
+ if (!(lmysql= mysql_client_init(NULL)))
+ {
+ myerror("mysql_client_init() failed");
+ exit(1);
+ }
+ if (!(mysql_real_connect(lmysql, opt_host, opt_user,
+ opt_password, current_db, opt_port,
+ opt_unix_socket, 0)))
+ {
+ myerror("connection failed");
+ exit(1);
+ }
+ mysql_options(lmysql, MYSQL_OPT_RECONNECT, &my_true);
+ if (!opt_silent)
+ fprintf(stdout, "OK");
+
+ /* set AUTOCOMMIT to ON*/
+ mysql_autocommit(lmysql, TRUE);
+
+ strmov(query, "SET SQL_MODE= \"EMPTY_STRING_IS_NULL\"");
+ if (!opt_silent)
+ fprintf(stdout, "\n With %s", query);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE OR REPLACE PROCEDURE test_mdev14013_p1("
+ " IN i1 VARCHAR(255) , "
+ " INOUT io1 VARCHAR(255), "
+ " OUT o2 VARBINARY(255)) "
+ "BEGIN "
+ " SET o2 = concat(concat(coalesce(i1,'i1 is null'),' - '),coalesce(i1,'io1 is null')); "
+ "END");
+ myquery(rc);
+
+ strmov(query, "CALL test_mdev14013_p1(?, ?, ?)");
+ stmt1= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt1);
+
+ /* Init PS-parameters. */
+
+ bzero((char *) my_bind, sizeof (my_bind));
+
+ strcpy(str_data[0],"");
+ my_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[0].buffer= (char *) str_data[0];
+ my_bind[0].buffer_length= strlen(str_data[0]);
+
+ strcpy(str_data[1],"");
+ my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[1].buffer= (char *) str_data[1];
+ my_bind[1].buffer_length= strlen(str_data[1]);
+
+ strcpy(str_data[2],"");
+ my_bind[2].buffer_type= MYSQL_TYPE_STRING;
+ my_bind[2].buffer= (char *) str_data[2];
+ my_bind[2].buffer_length= strlen(str_data[2]);
+
+ /* Bind parameters. */
+
+ rc= mysql_stmt_bind_param(stmt1, my_bind);
+ check_execute(stmt1, rc);
+ /* Execute */
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute(stmt1, rc);
+
+ my_bind[0].buffer_length= sizeof(str_data[0]);
+ my_bind[1].buffer_length= sizeof(str_data[1]);
+
+ mysql_stmt_bind_result(stmt1, my_bind);
+ rc= mysql_stmt_fetch(stmt1);
+
+ if (!opt_silent)
+ fprintf(stdout,"\nstr_data[1]=%s\n",str_data[1]);
+
+ DIE_UNLESS(strcmp(str_data[1], "i1 is null - io1 is null") == 0);
+
+ rc= mysql_stmt_close(stmt1);
+ DIE_UNLESS( rc == 0);
+
+ myquery(mysql_query(mysql, "drop procedure test_mdev14013_p1"));
+ mysql_close(lmysql);
+}
+
+
+static void test_mdev14454_internal(const char *init,
+ unsigned int csid,
+ const char *value)
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind;
+ const char *stmtstr= "CALL P1(?)";
+ char res[20];
+ int rc;
+
+ if ((rc= mysql_query_or_error(mysql, init)) ||
+ (rc= mysql_query_or_error(mysql, "DROP PROCEDURE IF EXISTS p1")) ||
+ (rc= mysql_query_or_error(mysql,
+ "CREATE PROCEDURE p1"
+ "("
+ " OUT param1 TEXT CHARACTER SET utf8"
+ ")"
+ "BEGIN "
+ " SET param1 = _latin1'test\xFF'; "
+ "END")))
+ DIE("Initiation failed");
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, stmtstr, strlen(stmtstr));
+ DIE_UNLESS(rc == 0);
+ DIE_UNLESS(mysql_stmt_param_count(stmt) == 1);
+
+ memset(&bind, 0, sizeof bind);
+ bind.buffer_type= MYSQL_TYPE_NULL;
+ rc= mysql_stmt_bind_param(stmt, &bind);
+ DIE_UNLESS(rc == 0);
+
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc == 0);
+
+ memset(res, 0, sizeof(res));
+ bind.buffer_type= MYSQL_TYPE_STRING;
+ bind.buffer_length= sizeof(res);
+ bind.buffer= res;
+
+ do {
+ if (mysql->server_status & SERVER_PS_OUT_PARAMS)
+ {
+ MYSQL_FIELD *field;
+ printf("\nOUT param result set:\n");
+ DIE_UNLESS(mysql_stmt_field_count(stmt) == 1);
+ field= &stmt->fields[0];
+ printf("Field: %s\n", field->name);
+ printf("Type: %d\n", field->type);
+ printf("Collation: %d\n", field->charsetnr);
+ printf("Length: %lu\n", field->length);
+ DIE_UNLESS(stmt->fields[0].charsetnr == csid);
+
+ rc= mysql_stmt_bind_result(stmt, &bind);
+ DIE_UNLESS(rc == 0);
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == 0);
+ printf("Value: %s\n", res);
+ DIE_UNLESS(strcmp(res, value) == 0);
+ }
+ else if (mysql_stmt_field_count(stmt))
+ {
+ printf("sp result set\n");
+ }
+ } while (mysql_stmt_next_result(stmt) == 0);
+
+ mysql_stmt_close(stmt);
+ DIE_UNLESS(mysql_query_or_error(mysql, "DROP PROCEDURE p1") == 0);
+}
+
+
+static void test_mdev14454()
+{
+ myheader("test_mdev14454");
+ test_mdev14454_internal("SET NAMES latin1", 8, "test\xFF");
+ test_mdev14454_internal("SET NAMES utf8", 33, "test\xC3\xBF");
+}
+
+
+typedef struct {
+ char sig[12];
+ char ver_cmd;
+ char fam;
+ short len;
+ union {
+ struct { /* for TCP/UDP over IPv4, len = 12 */
+ int src_addr;
+ int dst_addr;
+ short src_port;
+ short dst_port;
+ } ip4;
+ struct { /* for TCP/UDP over IPv6, len = 36 */
+ char src_addr[16];
+ char dst_addr[16];
+ short src_port;
+ short dst_port;
+ } ip6;
+ struct { /* for AF_UNIX sockets, len = 216 */
+ char src_addr[108];
+ char dst_addr[108];
+ } unx;
+ } addr;
+} v2_proxy_header;
+
+#ifndef EMBEDDED_LIBRARY
+static void test_proxy_header_tcp(const char *ipaddr, int port)
+{
+
+ int rc;
+ MYSQL_RES *result;
+ int family = (strchr(ipaddr,':') == NULL)?AF_INET:AF_INET6;
+ char query[256];
+ char text_header[256];
+ char addr_bin[16];
+ v2_proxy_header v2_header;
+ void *header_data[2];
+ size_t header_lengths[2];
+ int i;
+
+ // normalize IPv4-mapped IPv6 addresses, e.g ::ffff:127.0.0.2 to 127.0.0.2
+ const char *normalized_addr= strncmp(ipaddr, "::ffff:", 7)?ipaddr : ipaddr + 7;
+ myheader("test_proxy_header_tcp");
+
+ memset(&v2_header, 0, sizeof(v2_header));
+ sprintf(text_header,"PROXY %s %s %s %d 3306\r\n",family == AF_INET?"TCP4":"TCP6", ipaddr, ipaddr, port);
+
+ inet_pton(family,ipaddr,addr_bin);
+
+ memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
+ v2_header.ver_cmd = (0x2 << 4) | 0x1; /* Version (0x2) , Command = PROXY (0x1) */
+ if(family == AF_INET)
+ {
+ v2_header.fam= 0x11;
+ v2_header.len= htons(12);
+ v2_header.addr.ip4.src_port= htons(port);
+ v2_header.addr.ip4.dst_port= htons(3306);
+ memcpy(&v2_header.addr.ip4.src_addr,addr_bin, sizeof (v2_header.addr.ip4.src_addr));
+ memcpy(&v2_header.addr.ip4.dst_addr,addr_bin, sizeof (v2_header.addr.ip4.dst_addr));
+ }
+ else
+ {
+ v2_header.fam= 0x21;
+ v2_header.len= htons(36);
+ v2_header.addr.ip6.src_port= htons(port);
+ v2_header.addr.ip6.dst_port= htons(3306);
+ memcpy(v2_header.addr.ip6.src_addr,addr_bin, sizeof (v2_header.addr.ip6.src_addr));
+ memcpy(v2_header.addr.ip6.dst_addr,addr_bin, sizeof (v2_header.addr.ip6.dst_addr));
+ }
+
+ sprintf(query,"CREATE USER 'u'@'%s' IDENTIFIED BY 'password'",normalized_addr);
+ rc= mysql_query(mysql, query);
+ myquery(rc);
+
+ header_data[0]= text_header;
+ header_data[1]= &v2_header;
+
+ header_lengths[0]= strlen(text_header);
+ header_lengths[1]= family == AF_INET ? 28 : 52;
+
+ for (i = 0; i < 2; i++)
+ {
+ MYSQL *m;
+ size_t addrlen;
+ MYSQL_ROW row;
+ m = mysql_client_init(NULL);
+ DIE_UNLESS(m);
+ mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, header_data[i], header_lengths[i]);
+ if (!mysql_real_connect(m, opt_host, "u", "password", NULL, opt_port, opt_unix_socket, 0))
+ {
+ DIE_UNLESS(0);
+ }
+ rc= mysql_query(m, "select host from information_schema.processlist WHERE ID = connection_id()");
+ myquery(rc);
+ /* get the result */
+ result= mysql_store_result(m);
+ mytest(result);
+ row = mysql_fetch_row(result);
+ addrlen = strlen(normalized_addr);
+ printf("%.*s %.*s\n", (int)addrlen, row[0], (int)addrlen, normalized_addr);
+ DIE_UNLESS(strncmp(row[0], normalized_addr, addrlen) == 0);
+ DIE_UNLESS(atoi(row[0] + addrlen+1) == port);
+ mysql_free_result(result);
+ mysql_close(m);
+ }
+ sprintf(query,"DROP USER 'u'@'%s'",normalized_addr);
+ rc = mysql_query(mysql, query);
+ myquery(rc);
+}
+
+
+/* Test proxy protocol with AF_UNIX (localhost) */
+static void test_proxy_header_localhost()
+{
+ v2_proxy_header v2_header;
+ void *header_data = &v2_header;
+ size_t header_length= 216 + 16;
+ MYSQL *m;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ int rc;
+ myheader("test_proxy_header_localhost");
+
+ memset(&v2_header, 0, sizeof(v2_header));
+ memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
+ v2_header.ver_cmd = (0x2 << 4) | 0x1; /* Version (0x2) , Command = PROXY (0x1) */
+ v2_header.fam= 0x31;
+ v2_header.len= htons(216);
+ strcpy(v2_header.addr.unx.src_addr,"/tmp/mysql.sock");
+ strcpy(v2_header.addr.unx.dst_addr,"/tmp/mysql.sock");
+ rc = mysql_query(mysql, "CREATE USER 'u'@'localhost' IDENTIFIED BY 'password'");
+ myquery(rc);
+ m = mysql_client_init(NULL);
+ DIE_UNLESS(m != NULL);
+ mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, header_data, header_length);
+ DIE_UNLESS(mysql_real_connect(m, opt_host, "u", "password", NULL, opt_port, opt_unix_socket, 0) == m);
+ DIE_UNLESS(mysql_query(m, "select host from information_schema.processlist WHERE ID = connection_id()") == 0);
+ /* get the result */
+ result= mysql_store_result(m);
+ mytest(result);
+ row = mysql_fetch_row(result);
+ DIE_UNLESS(strcmp(row[0], "localhost") == 0);
+ mysql_free_result(result);
+ mysql_close(m);
+ rc = mysql_query(mysql, "DROP USER 'u'@'localhost'");
+ myquery(rc);
+}
+
+/* Proxy header ignoring */
+static void test_proxy_header_ignore()
+{
+ int rc;
+ MYSQL *m = mysql_client_init(NULL);
+ v2_proxy_header v2_header;
+ myheader("test_ps_params_in_ctes");
+ DIE_UNLESS(m != NULL);
+ mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, "PROXY UNKNOWN\r\n",15);
+ DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m);
+ mysql_close(m);
+
+ memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
+ v2_header.ver_cmd = (0x2 << 4) | 0x0; /* Version (0x2) , Command = LOCAL (0x0) */
+ v2_header.fam= 0x0; /* AF_UNSPEC*/
+ v2_header.len= htons(0);
+ m = mysql_client_init(NULL);
+ mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header,16);
+ DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m);
+ mysql_close(m);
+
+ /* test for connection denied with empty proxy_protocol_networks */
+ rc = mysql_query(mysql, "select @@proxy_protocol_networks into @sv_proxy_protocol_networks");
+ myquery(rc);
+ mysql_query(mysql, "set global proxy_protocol_networks=default");
+ myquery(rc);
+ m = mysql_client_init(NULL);
+ mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header,16);
+ DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == 0);
+ mysql_close(m);
+ mysql_query(mysql, "set global proxy_protocol_networks= @sv_proxy_protocol_networks");
+ myquery(rc);
+}
+
+
+static void test_proxy_header()
+{
+ myheader("test_proxy_header");
+ test_proxy_header_tcp("192.0.2.1",3333);
+ test_proxy_header_tcp("2001:db8:85a3::8a2e:370:7334",2222);
+ test_proxy_header_tcp("::ffff:192.0.2.1",2222);
+ test_proxy_header_localhost();
+ test_proxy_header_ignore();
+}
+
+
+static void test_bulk_autoinc()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ MYSQL_ROW row;
+ char indicator[]= {0, STMT_INDICATOR_NULL, 0/*STMT_INDICATOR_IGNORE*/};
+ my_bool error[1];
+ int i, id[]= {2, 3, 777}, count= sizeof(id)/sizeof(id[0]);
+ MYSQL_RES *result;
+
+ myheader("test_bulk_autoinc");
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS ai_field_value");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE ai_field_value (id int not null primary key auto_increment)");
+ myquery(rc);
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, "INSERT INTO ai_field_value(id) values(?)", -1);
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].buffer = (void *)id;
+ bind[0].buffer_length = 0;
+ bind[0].is_null = NULL;
+ bind[0].length = NULL;
+ bind[0].error = error;
+ bind[0].u.indicator= indicator;
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "SELECT id FROM ai_field_value");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ i= 0;
+ while ((row= mysql_fetch_row(result)))
+ {
+ DIE_IF(atoi(row[0]) != id[i++]);
+ }
+ mysql_free_result(result);
+ rc= mysql_query(mysql, "DROP TABLE ai_field_value");
+ myquery(rc);
+}
+
+static void test_bulk_delete()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[1];
+ MYSQL_ROW row;
+ char indicator[]= {0, 0, 0};
+ my_bool error[1];
+ int i, id[]= {1, 2, 4}, count= sizeof(id)/sizeof(id[0]);
+ MYSQL_RES *result;
+
+ myheader("test_bulk_delete");
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (id int not null primary key)");
+ myquery(rc);
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1),(2),(3),(4)");
+ myquery(rc);
+ verify_affected_rows(4);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, "DELETE FROM t1 where id=?", -1);
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].buffer = (void *)id;
+ bind[0].buffer_length = 0;
+ bind[0].is_null = NULL;
+ bind[0].length = NULL;
+ bind[0].error = error;
+ bind[0].u.indicator= indicator;
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ verify_affected_rows(3);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "SELECT id FROM t1");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ i= 0;
+ while ((row= mysql_fetch_row(result)))
+ {
+ i++;
+ DIE_IF(atoi(row[0]) != 3);
+ }
+ DIE_IF(i != 1);
+ mysql_free_result(result);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+static void test_bulk_replace()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[2];
+ MYSQL_ROW row;
+ int i,
+ id[]= {1, 2, 3, 4},
+ val[]= {1, 1, 1, 1},
+ count= sizeof(id)/sizeof(id[0]);
+ MYSQL_RES *result;
+
+ myheader("test_bulk_replace");
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (id int not null primary key, active int)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1, 0), (2, 0), (3, 0)");
+ myquery(rc);
+ verify_affected_rows(3);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, "replace into t1 (id, active) values (?, ?)", -1);
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].buffer = (void *)id;
+ bind[0].buffer_length = 0;
+ bind[1].buffer_type = MYSQL_TYPE_LONG;
+ bind[1].buffer = (void *)val;
+ bind[1].buffer_length = 0;
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "SELECT active FROM t1");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ i= 0;
+ while ((row= mysql_fetch_row(result)))
+ {
+ i++;
+ DIE_IF(atoi(row[0]) != 1);
+ }
+ DIE_IF(i != 4);
+ mysql_free_result(result);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_bulk_insert_returning()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[2], res_bind[2];
+ MYSQL_ROW row;
+ MYSQL_RES *result;
+ int i,
+ id[]= {1, 2, 3, 4},
+ val[]= {1, 1, 1, 1},
+ count= sizeof(id)/sizeof(id[0]);
+ unsigned long length[2];
+ my_bool is_null[2];
+ my_bool error[2];
+ int32 res[2];
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql,
+ "CREATE TABLE t1 (id int not null primary key, active int)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt,
+ "insert into t1 values (?, ?) returning id, active",
+ -1);
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].buffer = (void *)id;
+ bind[0].buffer_length = 0;
+ bind[1].buffer_type = MYSQL_TYPE_LONG;
+ bind[1].buffer = (void *)val;
+ bind[1].buffer_length = 0;
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ myquery(rc);
+
+ memset(res_bind, 0, sizeof(res_bind));
+ for (i= 0; i < 2; i++)
+ {
+ res_bind[i].buffer_type= MYSQL_TYPE_LONG;
+ res_bind[i].buffer= (char *)&res[i];
+ res_bind[i].is_null= &is_null[i];
+ res_bind[i].length= &length[i];
+ res_bind[i].error= &error[i];
+ }
+ rc= mysql_stmt_bind_result(stmt, res_bind);
+ myquery(rc);
+ rc= mysql_stmt_store_result(stmt);
+ myquery(rc);
+
+ i= 0;
+ while (!mysql_stmt_fetch(stmt))
+ {
+ i++;
+ DIE_IF(is_null[0] || is_null[1]);
+ DIE_IF(res[0] != i);
+ DIE_IF(res[1] != 1);
+ }
+ DIE_IF(i != 4);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "SELECT id,active FROM t1");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ i= 0;
+ while ((row= mysql_fetch_row(result)))
+ {
+ i++;
+ DIE_IF(atoi(row[0]) != i);
+ DIE_IF(atoi(row[1]) != 1);
+ }
+ DIE_IF(i != 4);
+ mysql_free_result(result);
+
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+static void test_bulk_delete_returning()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[2], res_bind[2];
+ MYSQL_ROW row;
+ MYSQL_RES *result;
+ int i,
+ id[]= {1, 2, 3, 4},
+ count= sizeof(id)/sizeof(id[0]);
+ unsigned long length[1];
+ my_bool is_null[1];
+ my_bool error[1];
+ int32 res[1];
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (id int not null primary key)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1), (2), (3), (4)");
+ myquery(rc);
+ verify_affected_rows(4);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, "DELETE FROM t1 WHERE id=? RETURNING id", -1);
+ check_execute(stmt, rc);
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].buffer = (void *)id;
+ bind[0].buffer_length = 0;
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, (void*)&count);
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ myquery(rc);
+
+ memset(res_bind, 0, sizeof(res_bind));
+ res_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ res_bind[0].buffer= (char *)&res[0];
+ res_bind[0].is_null= &is_null[0];
+ res_bind[0].length= &length[0];
+ res_bind[0].error= &error[0];
+ rc= mysql_stmt_bind_result(stmt, res_bind);
+ myquery(rc);
+ rc= mysql_stmt_store_result(stmt);
+ myquery(rc);
+
+ i= 0;
+ while (!mysql_stmt_fetch(stmt))
+ {
+ i++;
+ DIE_IF(is_null[0]);
+ DIE_IF(res[0] != i);
+ }
+ DIE_IF(i != 4);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "SELECT id FROM t1");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ i= 0;
+ while ((row= mysql_fetch_row(result)))
+ {
+ i++;
+ printf("\nResult (SHOULD NOT BE HERE!!!) %d %s \n", i, row[0]);
+ }
+ DIE_IF(i != 0 );
+ mysql_free_result(result);
+
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+#endif
+
+
+static void test_ps_params_in_ctes()
+{
+ int rc;
+ const char *query;
+ MYSQL_BIND ps_params[1];
+ int int_data[1];
+ MYSQL_STMT *stmt;
+
+ myheader("test_ps_params_in_ctes");
+ rc= mysql_query(mysql, "create table t1(a int, b int, key(a))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "insert into t1 (a) values "
+ "(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)");
+ myquery(rc);
+
+ query=
+ "explain "
+ "with T as "
+ "( "
+ " select * from t1 where t1.a=? limit 2 "
+ ") "
+ "select * from T as TA, T as TB;";
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_prepare(stmt, query, (uint) strlen(query));
+ check_execute(stmt, rc);
+
+ int_data[0]=2;
+
+ memset(ps_params, 0, sizeof ps_params);
+ ps_params[0].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[0].buffer= (char *) &int_data[0];
+ ps_params[0].length= 0;
+ ps_params[0].is_null= 0;
+
+ rc= mysql_stmt_bind_param(stmt, ps_params);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "drop table t1");
+ myquery(rc);
+}
+
+void display_result_metadata(MYSQL_FIELD *field,
+ uint num_fields)
+{
+ MYSQL_FIELD* field_end;
+
+ mct_log("Catalog\tDatabase\tTable\tTable_alias\tColumn\t"
+ "Column_alias\tType\tLength\tMax length\tIs_null\t"
+ "Flags\tDecimals\tCharsetnr\n");
+ for (field_end= field+num_fields; field < field_end; field++)
+ {
+ mct_log("%s\t", field->catalog);
+ mct_log("%s\t", field->db);
+ mct_log("%s\t", field->org_table);
+ mct_log("%s\t", field->table);
+ mct_log("%s\t", field->org_name);
+ mct_log("%s\t", field->name);
+ mct_log("%u\t", field->type);
+ mct_log("%lu\t", field->length);
+ mct_log("%lu\t", field->max_length);
+ mct_log("%s\t", (IS_NOT_NULL(field->flags) ? "N" : "Y"));
+ mct_log("%u\t", field->flags);
+ mct_log("%u\t", field->decimals);
+ mct_log("%u\n", field->charsetnr);
+ }
+}
+
+static void test_mdev_26145()
+{
+ MYSQL_STMT *stmt;
+ MYSQL_RES *result;
+ MYSQL_FIELD *fields;
+ int rc, num_fields;
+
+ myheader("test_mdev_26145");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(a INT)");
+ myquery(rc);
+
+ stmt= mysql_simple_prepare(
+ mysql, "(SELECT MAX(a) FROM t1) UNION (SELECT MAX(a) FROM t1)");
+ check_stmt(stmt);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ result= mysql_stmt_result_metadata(stmt);
+ DIE_UNLESS(result);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ fields= mysql_fetch_fields(result);
+
+ mct_start_logging("test_mdev26145");
+ display_result_metadata(fields, num_fields);
+ mct_close_log();
+
+ mysql_free_result(result);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+
+ myquery(rc);
+}
+
+static void test_mdev24827()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ unsigned long cursor = CURSOR_TYPE_READ_ONLY;
+ const char* query=
+ "SELECT t2.c1 AS c1 FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1 "
+ "WHERE EXISTS (SELECT 1 FROM t1 WHERE c2 = -1) ORDER BY c1";
+
+ myheader("test_mdev24827");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 INT, "
+ "KEY idx_c2(c2))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 (c1, c2) "
+ "SELECT seq, seq FROM seq_1_to_10000");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t2 (c1, c2) "
+ "SELECT seq, seq FROM seq_1_to_20000");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE t2");
+ myquery(rc);
+}
+
+static void test_mdev_20516()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ unsigned long cursor= CURSOR_TYPE_READ_ONLY;
+ const char* query=
+ "CREATE VIEW v1 AS SELECT * FROM t1";
+
+ myheader("test_mdev_20516");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(a INT)");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+static void print_metadata(MYSQL_RES *rs_metadata, int num_fields)
+{
+ int i;
+ MYSQL_FIELD *fields= mysql_fetch_fields(rs_metadata);
+
+ for (i = 0; i < num_fields; ++i)
+ {
+ mct_log(" - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
+ "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
+ "type: %d; decimals: %d\n",
+ (int) i,
+ (const char *) fields[i].name,
+ (const char *) fields[i].org_name,
+ (const char *) fields[i].table,
+ (const char *) fields[i].org_table,
+ (const char *) fields[i].db,
+ (const char *) fields[i].catalog,
+ (int) fields[i].length,
+ (int) fields[i].max_length,
+ (int) fields[i].type,
+ (int) fields[i].decimals);
+
+ }
+}
+
+static void test_explain_meta()
+{
+ MYSQL_STMT *stmt;
+ int num_fields;
+ char query[MAX_TEST_QUERY_LENGTH];
+ MYSQL_RES *rs_metadata;
+ int rc;
+
+ myheader("test_explain_meta");
+ mct_start_logging("test_explain_meta");
+
+ strmov(query, "SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("SELECT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json SELECT 1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int)");
+ myquery(rc);
+
+ strmov(query, "EXPLAIN INSERT INTO t1 values (1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json INSERT INTO t1 values(1)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON INSERT number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "EXPLAIN UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json UPDATE t1 set a=2");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON UPDATE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "EXPLAIN DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 10)
+ {
+ mct_close_log();
+ DIE("num_fields != 10");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "EXPLAIN format=json DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("EXPALIN JSON DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+
+ strmov(query, "ANALYZE DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 13)
+ {
+ mct_close_log();
+ DIE("num_fields != 13");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ strmov(query, "ANALYZE format=json DELETE FROM t1");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ rs_metadata= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_stmt_field_count(stmt);
+ mct_log("ANALYZE JSON DELETE number of fields: %d\n", (int) num_fields);
+ if (num_fields != 1)
+ {
+ mct_close_log();
+ DIE("num_fields != 1");
+ }
+ print_metadata(rs_metadata, num_fields);
+ mysql_free_result(rs_metadata);
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ mct_close_log();
+}
+
+static void test_mdev_16128()
+{
+ int rc, res;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind, bind_res;
+ char bind_arg_1[]="d", bind_arg_2[]="b";
+ ulong length= 0;
+ const char *query=
+ "SELECT 300 FROM t1 WHERE EXISTS (SELECT 100 FROM t2 WHERE t2.b = ?)";
+
+ myheader("test_mdev_16128");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a VARCHAR(10))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t2 (b VARCHAR(10) CHARACTER SET utf8)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES('b')");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t2 VALUES('d')");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ memset(&bind, 0, sizeof(bind));
+ bind.buffer_type= MYSQL_TYPE_STRING;
+ bind.buffer_length= strlen(bind_arg_1);
+ bind.buffer= bind_arg_1;
+
+ rc= mysql_stmt_bind_param(stmt, &bind);
+ check_execute(stmt, rc);
+
+ memset(&bind_res, 0, sizeof(bind_res));
+ bind_res.buffer_type= MYSQL_TYPE_LONG;
+ bind_res.buffer= &res;
+ bind_res.is_null= NULL;
+ bind_res.length= &length;
+
+ rc= mysql_stmt_bind_result(stmt, &bind_res);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+
+ /**
+ It's expected that the query
+ SELECT 300 FROM t1 WHERE EXISTS (SELECT 100 FROM t2 WHERE t2.b = ?)"
+ executed in PS-mode and bound with the value 'd' returns exactly
+ one row containing the value (300).
+ */
+ check_execute(stmt, rc);
+ DIE_UNLESS(bind_res.buffer_type == MYSQL_TYPE_LONG);
+ DIE_UNLESS(res == 300);
+
+ memset(&bind, 0, sizeof(bind));
+ bind.buffer_type= MYSQL_TYPE_STRING;
+ bind.buffer_length= strlen(bind_arg_2);
+ bind.buffer= bind_arg_2;
+
+ rc= mysql_stmt_bind_param(stmt, &bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_fetch(stmt);
+ /**
+ It's expected that the query
+ SELECT 300 FROM t1 WHERE EXISTS (SELECT 100 FROM t2 WHERE t2.b = ?)"
+ executed in PS-mode and bound with the value 'd' returns empty result set.
+ */
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1, t2");
+ myquery(rc);
+}
+
+#ifndef EMBEDDED_LIBRARY
+#define MDEV19838_MAX_PARAM_COUNT 32
+#define MDEV19838_FIELDS_COUNT 17
+static void test_mdev19838()
+{
+ int rc;
+ MYSQL_BIND bind[MDEV19838_MAX_PARAM_COUNT];
+ unsigned int i, paramCount = 1;
+ char charvalue[] = "012345678901234567890123456789012345";
+ MYSQL_STMT *stmt;
+
+ myheader("test_mdev19838");
+
+ rc = mysql_query(mysql, "CREATE TABLE mdev19838("
+ "f1 char(36),"
+ "f2 char(36),"
+ "f3 char(36),"
+ "f4 char(36),"
+ "f5 char(36),"
+ "f6 char(36),"
+ "f7 char(36),"
+ "f8 char(36),"
+ "f9 char(36),"
+ "f10 char(36),"
+ "f11 char(36),"
+ "f12 char(36),"
+ "f13 char(36),"
+ "f14 char(36),"
+ "f15 char(36),"
+ "f16 char(36),"
+ "f17 char(36)"
+ ")");
+ myquery(rc);
+
+ stmt = mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ memset(bind, 0, sizeof(bind));
+
+ for (i = 0; i < MDEV19838_MAX_PARAM_COUNT; ++i)
+ {
+ bind[i].buffer = charvalue;
+ bind[i].buffer_type = MYSQL_TYPE_STRING;
+ bind[i].buffer_length = strlen(charvalue) + 1;
+ bind[i].length = &bind[i].length_value;
+ bind[i].length_value = bind[i].buffer_length - 1;
+ }
+
+ for (paramCount = 1; paramCount < MDEV19838_FIELDS_COUNT; ++paramCount)
+ {
+ mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, &paramCount);
+
+ rc = mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc = mariadb_stmt_execute_direct(stmt, "INSERT INTO mdev19838"
+ "(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17)"
+ " VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", -1);
+
+ /* Expecting an error */
+ DIE_UNLESS(rc != 0);
+
+ mysql_stmt_close(stmt);
+ stmt = mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ }
+
+ paramCount = 0;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, &paramCount);
+ rc = mariadb_stmt_execute_direct(stmt, "INSERT INTO mdev19838(f1)"
+ " VALUES (?)", -1);
+ /* Expecting an error */
+ DIE_UNLESS(rc != 0);
+ mysql_stmt_close(stmt);
+
+ stmt = mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ /* Correct number of parameters */
+ paramCount = MDEV19838_FIELDS_COUNT;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, &paramCount);
+ mysql_stmt_bind_param(stmt, bind);
+
+ rc = mariadb_stmt_execute_direct(stmt, "INSERT INTO mdev19838"
+ "(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17)"
+ " VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", -1);
+ check_execute(stmt, rc);
+
+ /* MYSQL_TYPE_TINY = 1. This parameter byte can be read as "parameters send" flag byte.
+ Checking that wrong packet is still detected */
+ bind[0].buffer_type = MYSQL_TYPE_TINY;
+ bind[0].length_value = 1;
+ bind[0].buffer_length = 1;
+
+ for (paramCount = 8; paramCount > 0; --paramCount)
+ {
+ mysql_stmt_close(stmt);
+ stmt = mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, &paramCount);
+
+ rc = mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc = mariadb_stmt_execute_direct(stmt, "INSERT INTO mdev19838"
+ "(f1, f2, f3, f4, f5, f6, f7, f8, f9)"
+ " VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?)", -1);
+
+ /* Expecting an error */
+ DIE_UNLESS(rc != 0);
+ }
+
+ /* Test of query w/out parameters, with parameter sent and not sent */
+ for (paramCount = MDEV19838_MAX_PARAM_COUNT; paramCount != (unsigned int)-1; --paramCount)
+ {
+ mysql_stmt_close(stmt);
+ stmt = mysql_stmt_init(mysql);
+ check_stmt(stmt);
+
+ mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, &paramCount);
+
+ if (paramCount > 0)
+ {
+ rc = mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+ }
+
+ rc = mariadb_stmt_execute_direct(stmt, "INSERT INTO mdev19838"
+ "(f1)"
+ " VALUES "
+ "(0x1111111111111111)", -1);
+
+ /*
+ We allow junk at the end of the packet in case of
+ no parameters. So it will succeed.
+ */
+ DIE_UNLESS(rc == 0);
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc = mysql_query(mysql, "drop table mdev19838");
+ myquery(rc);
+}
+#endif // EMBEDDED_LIBRARY
+
+
+/*
+ MDEV-20261 NULL passed to String::eq, SEGV, server crash, regression in 10.4
+*/
+static void test_mdev20261()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+ MYSQL_BIND param[1];
+ const char *query= "SELECT * FROM t1 WHERE f = ? OR f = 'foo'";
+ char val[]= "";
+ my_bool is_null= TRUE;
+
+ myheader("test_mdev20261");
+
+ rc= mysql_query(mysql, "CREATE OR REPLACE TABLE t1 (f varchar(64)) ENGINE=MyISAM");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ verify_param_count(stmt, 1);
+
+ bzero((char*) param, sizeof(param));
+
+ param[0].buffer= &val;
+ param[0].buffer_type= MYSQL_TYPE_STRING;
+ param[0].is_null= &is_null;
+
+ rc= mysql_stmt_bind_param(stmt, param);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+static void test_mdev_30159()
+{
+ MYSQL_RES *result;
+ int rc;
+
+ myheader("test_mdev_30159");
+
+ rc= mysql_query(mysql, "create table t1 ("
+ " name varchar(100),"
+ " typ varchar(100)"
+ ")");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),"
+ "(6,6),(7,7),(8,8),(9,9),(10,10)");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t1 values ('', 'value'),('', 'value')");
+ myquery(rc);
+ rc= mysql_query(mysql, "create table t2 ("
+ " servername varchar(100)"
+ ")");
+ myquery(rc);
+ rc= mysql_query(mysql, "insert into t2 values (1),(2),(3),(4),(5),"
+ "(6),(7),(8),(9),(10)");
+ myquery(rc);
+ rc= mysql_query(mysql, "create view v1 as"
+ " select * from t2"
+ " where"
+ " `t2`.`servername` regexp ( select"
+ " group_concat(`t1`.`name` separator '|')"
+ " from `t1`"
+ " where `t1`.`typ`"
+ " like 'value')");
+ myquery(rc);
+
+ result= mysql_list_fields(mysql, "v1", NULL);
+ mytest(result);
+ mysql_free_result(result);
+
+ rc= mysql_query(mysql, "drop view v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "drop table t1, t2");
+ myquery(rc);
+}
+
+static void test_execute_direct()
+{
+#ifndef EMBEDDED_LIBRARY
+ MYSQL_STMT* stmt= mysql_stmt_init(mysql);
+ int rc= mariadb_stmt_execute_direct(stmt,"do 1",-1);
+ myquery(rc);
+ mysql_stmt_close(stmt);
+#endif
+}
+
+
+static void assert_metadata_skipped_count_equals(MYSQL *mysql, int val)
+{
+ MYSQL_ROW row;
+ MYSQL_RES *result;
+ int rc= mysql_query(mysql, "SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE "
+ "VARIABLE_NAME='Resultset_metadata_skipped'");
+ myquery(rc);
+ result= mysql_use_result(mysql);
+ mytest(result);
+ row= mysql_fetch_row(result);
+ DIE_UNLESS(atoi(row[0]) == val);
+ mysql_free_result(result);
+}
+
+
+static void flush_session_status(MYSQL* mysql)
+{
+ int rc= mysql_query(mysql, "flush status");
+ myquery(rc);
+}
+
+
+static void exec_stmt(MYSQL_STMT* stmt)
+{
+ int rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ rc= mysql_stmt_store_result(stmt);
+ check_execute(stmt, rc);
+}
+
+
+
+static void test_cache_metadata()
+{
+ char char_val[]= "blah";
+ int int_val = 1;
+ int rc;
+ MYSQL_BIND param= {0};
+ my_bool is_null= FALSE;
+
+
+ MYSQL_STMT* stmt= mysql_stmt_init(mysql);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, "SELECT ?", -1);
+ myquery(rc);
+
+ param.buffer= char_val;
+ param.buffer_type= MYSQL_TYPE_STRING;
+ param.is_null= &is_null;
+ param.buffer_length = 4;
+
+ rc= mysql_stmt_bind_param(stmt,&param);
+ exec_stmt(stmt);
+
+ flush_session_status(mysql);
+ /* Execute the statement again, check that metadata is skipped*/
+ exec_stmt(stmt);
+ assert_metadata_skipped_count_equals(mysql, 1);
+
+ flush_session_status(mysql);
+ /*
+ Execute the statement again, such that metadata changes,
+ (using LONG parameter in bind for "SELECT ?", instead of string.
+ Check that metadata is NOT skipped.
+ */
+ param.buffer= &int_val;
+ param.buffer_type= MYSQL_TYPE_LONG;
+ param.is_null= &is_null;
+ rc= mysql_stmt_bind_param(stmt, &param);
+ exec_stmt(stmt);
+ assert_metadata_skipped_count_equals(mysql, 0);
+ mysql_stmt_close(stmt);
+
+
+ /*
+ Test with real table, and DDL which causes column info to be
+ changed.
+ */
+ stmt= mysql_stmt_init(mysql);
+
+ rc= mysql_query(
+ mysql, "CREATE OR REPLACE TABLE t1 (a int, b bigint) engine=memory");
+ myquery(rc);
+
+ flush_session_status(mysql);
+ check_stmt(stmt);
+ rc= mysql_stmt_prepare(stmt, "SELECT * from t1", -1);
+ myquery(rc);
+
+ exec_stmt(stmt);
+ /* Metadata skipped, since already sent with COM_STMT_PREPARE result.*/
+ assert_metadata_skipped_count_equals(mysql, 1);
+
+ flush_session_status(mysql);
+ exec_stmt(stmt);
+ /* Metadata skipped again*/
+ assert_metadata_skipped_count_equals(mysql, 1);
+
+ rc= mysql_query(mysql, "ALTER TABLE t1 MODIFY b CHAR(10)");
+ myquery(rc);
+
+ /* Column metadata WILL change for the next execution due to DDL*/
+ flush_session_status(mysql);
+ exec_stmt(stmt);
+ assert_metadata_skipped_count_equals(mysql, 0);
+
+ /* On reexecution, PS column metadata will NOT change. */
+ flush_session_status(mysql);
+ exec_stmt(stmt);
+ assert_metadata_skipped_count_equals(mysql, 1);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+ mysql_stmt_close(stmt);
+}
+
+void test_mdev_10075()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ MYSQL_RES *result;
+ MYSQL_BIND my_bind[1];
+ MYSQL_BIND my_bind2[1];
+
+ struct st_data {
+ unsigned long id;
+ char id_ind;
+ };
+
+ struct st_data data[]= {
+ {0, STMT_INDICATOR_NONE},
+ {1, STMT_INDICATOR_NONE},
+ {2, STMT_INDICATOR_NONE}
+ };
+
+ struct st_data data2[]= {
+ {3, STMT_INDICATOR_NONE},
+ {2, STMT_INDICATOR_NONE},
+ {4, STMT_INDICATOR_NONE}
+ };
+
+ myheader("test_mdev_10075");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(id INT PRIMARY KEY)");
+ myquery(rc);
+
+ /* insert by prepare */
+ stmt= mysql_simple_prepare(mysql,
+ "INSERT INTO t1 VALUES(?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ /* bzero bind structure */
+ bzero((char*) my_bind, sizeof(my_bind));
+ my_bind[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind[0].buffer= (void *)&data[0].id;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind);
+ check_execute(stmt, rc);
+
+ /* Set array size, row size and bind the parameter */
+ mysql_stmt_bind_param(stmt, my_bind);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ stmt= mysql_simple_prepare(mysql,
+ "INSERT IGNORE INTO t1 VALUES(?)");
+ check_stmt(stmt);
+ verify_param_count(stmt, 1);
+
+ /* bzero bind structure */
+ bzero((char*) my_bind2, sizeof(my_bind2));
+ my_bind2[0].buffer_type= MYSQL_TYPE_LONG;
+ my_bind2[0].buffer= (void *)&data2[0].id;
+
+ rc= mysql_stmt_bind_param(stmt, my_bind2);
+ check_execute(stmt, rc);
+
+ mysql_stmt_bind_param(stmt, my_bind2);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "GET DIAGNOSTICS CONDITION 1 @var1 = ROW_NUMBER");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "SELECT @var1");
+ myquery(rc);
+
+ result= mysql_store_result(mysql);
+ mytest(result);
+
+ rc= my_process_result_set(result);
+ DIE_UNLESS(rc == 1);
+
+ mysql_free_result(result);
+}
+
+static struct my_tests_st my_tests[]= {
+ { "test_mdev_20516", test_mdev_20516 },
+ { "test_mdev24827", test_mdev24827 },
+ { "test_mdev_26145", test_mdev_26145 },
+ { "disable_query_logs", disable_query_logs },
+ { "test_view_sp_list_fields", test_view_sp_list_fields },
+ { "client_query", client_query },
+ { "test_prepare_insert_update", test_prepare_insert_update},
+#ifdef EMBEDDED_LIBRARY
+ { "test_embedded_start_stop", test_embedded_start_stop },
+#endif
+#ifdef NOT_YET_WORKING
+ { "test_drop_temp", test_drop_temp },
+#endif
+ { "test_fetch_seek", test_fetch_seek },
+ { "test_fetch_nobuffs", test_fetch_nobuffs },
+ { "test_open_direct", test_open_direct },
+ { "test_fetch_null", test_fetch_null },
+ { "test_ps_null_param", test_ps_null_param },
+ { "test_fetch_date", test_fetch_date },
+ { "test_fetch_str", test_fetch_str },
+ { "test_fetch_long", test_fetch_long },
+ { "test_fetch_short", test_fetch_short },
+ { "test_fetch_tiny", test_fetch_tiny },
+ { "test_fetch_bigint", test_fetch_bigint },
+ { "test_fetch_float", test_fetch_float },
+ { "test_fetch_double", test_fetch_double },
+ { "test_bind_result_ext", test_bind_result_ext },
+ { "test_bind_result_ext1", test_bind_result_ext1 },
+ { "test_select_direct", test_select_direct },
+ { "test_select_prepare", test_select_prepare },
+ { "test_select", test_select },
+ { "test_select_version", test_select_version },
+ { "test_ps_conj_select", test_ps_conj_select },
+ { "test_select_show_table", test_select_show_table },
+ { "test_func_fields", test_func_fields },
+ { "test_long_data", test_long_data },
+ { "test_insert", test_insert },
+ { "test_set_variable", test_set_variable },
+ { "test_select_show", test_select_show },
+ { "test_prepare_noparam", test_prepare_noparam },
+ { "test_bind_result", test_bind_result },
+ { "test_prepare_simple", test_prepare_simple },
+ { "test_prepare", test_prepare },
+ { "test_null", test_null },
+ { "test_debug_example", test_debug_example },
+ { "test_update", test_update },
+ { "test_simple_update", test_simple_update },
+ { "test_simple_delete", test_simple_delete },
+ { "test_double_compare", test_double_compare },
+ { "client_store_result", client_store_result },
+ { "client_use_result", client_use_result },
+ { "test_tran_bdb", test_tran_bdb },
+ { "test_tran_innodb", test_tran_innodb },
+ { "test_prepare_ext", test_prepare_ext },
+ { "test_prepare_syntax", test_prepare_syntax },
+ { "test_field_names", test_field_names },
+ { "test_field_flags", test_field_flags },
+ { "test_long_data_str", test_long_data_str },
+ { "test_long_data_str1", test_long_data_str1 },
+ { "test_long_data_bin", test_long_data_bin },
+ { "test_warnings", test_warnings },
+ { "test_errors", test_errors },
+ { "test_prepare_resultset", test_prepare_resultset },
+ { "test_stmt_close", test_stmt_close },
+ { "test_prepare_field_result", test_prepare_field_result },
+ { "test_multi_stmt", test_multi_stmt },
+ { "test_multi_statements", test_multi_statements },
+ { "test_prepare_multi_statements", test_prepare_multi_statements },
+ { "test_store_result", test_store_result },
+ { "test_store_result1", test_store_result1 },
+ { "test_store_result2", test_store_result2 },
+ { "test_subselect", test_subselect },
+ { "test_date", test_date },
+ { "test_simple_temporal", test_simple_temporal },
+ { "test_date_date", test_date_date },
+ { "test_date_time", test_date_time },
+ { "test_date_ts", test_date_ts },
+ { "test_date_dt", test_date_dt },
+ { "test_prepare_alter", test_prepare_alter },
+ { "test_manual_sample", test_manual_sample },
+ { "test_pure_coverage", test_pure_coverage },
+ { "test_buffers", test_buffers },
+ { "test_ushort_bug", test_ushort_bug },
+ { "test_sshort_bug", test_sshort_bug },
+ { "test_stiny_bug", test_stiny_bug },
+ { "test_field_misc", test_field_misc },
+ { "test_set_option", test_set_option },
+#ifndef EMBEDDED_LIBRARY
+ { "test_prepare_grant", test_prepare_grant },
+
+#endif
+ { "test_frm_bug", test_frm_bug },
+ { "test_explain_bug", test_explain_bug },
+ { "test_decimal_bug", test_decimal_bug },
+ { "test_nstmts", test_nstmts },
+ { "test_logs;", test_logs },
+ { "test_cuted_rows", test_cuted_rows },
+ { "test_fetch_offset", test_fetch_offset },
+ { "test_fetch_column", test_fetch_column },
+ { "test_mem_overun", test_mem_overun },
+ { "test_list_fields", test_list_fields },
+ { "test_list_information_schema_fields", test_list_information_schema_fields },
+ { "test_list_fields_blob", test_list_fields_blob },
+ { "test_list_fields_default", test_list_fields_default },
+ { "test_free_result", test_free_result },
+ { "test_free_store_result", test_free_store_result },
+ { "test_sqlmode", test_sqlmode },
+ { "test_ts", test_ts },
+ { "test_bug1115", test_bug1115 },
+ { "test_bug1180", test_bug1180 },
+ { "test_bug1500", test_bug1500 },
+ { "test_bug1644", test_bug1644 },
+ { "test_bug1946", test_bug1946 },
+ { "test_bug2248", test_bug2248 },
+ { "test_parse_error_and_bad_length", test_parse_error_and_bad_length },
+ { "test_bug2247", test_bug2247 },
+ { "test_subqueries", test_subqueries },
+ { "test_bad_union", test_bad_union },
+ { "test_distinct", test_distinct },
+ { "test_subqueries_ref", test_subqueries_ref },
+ { "test_union", test_union },
+ { "test_bug3117", test_bug3117 },
+ { "test_join", test_join },
+ { "test_selecttmp", test_selecttmp },
+ { "test_create_drop", test_create_drop },
+ { "test_rename", test_rename },
+ { "test_do_set", test_do_set },
+ { "test_multi", test_multi },
+ { "test_insert_select", test_insert_select },
+ { "test_bind_nagative", test_bind_nagative },
+ { "test_derived", test_derived },
+ { "test_xjoin", test_xjoin },
+ { "test_bug3035", test_bug3035 },
+ { "test_union2", test_union2 },
+ { "test_bug1664", test_bug1664 },
+ { "test_union_param", test_union_param },
+ { "test_order_param", test_order_param },
+ { "test_ps_i18n", test_ps_i18n },
+ { "test_bug3796", test_bug3796 },
+ { "test_bug4026", test_bug4026 },
+ { "test_bug4079", test_bug4079 },
+ { "test_bug4236", test_bug4236 },
+ { "test_bug4030", test_bug4030 },
+ { "test_bug5126", test_bug5126 },
+ { "test_bug4231", test_bug4231 },
+ { "test_bug5399", test_bug5399 },
+ { "test_bug5194", test_bug5194 },
+ { "test_bug5315", test_bug5315 },
+ { "test_bug6049", test_bug6049 },
+ { "test_bug6058", test_bug6058 },
+ { "test_bug6059", test_bug6059 },
+ { "test_bug6046", test_bug6046 },
+ { "test_bug6081", test_bug6081 },
+ { "test_bug6096", test_bug6096 },
+ { "test_datetime_ranges", test_datetime_ranges },
+ { "test_datetime_ranges_mdev15289", test_datetime_ranges_mdev15289 },
+ { "test_bug4172", test_bug4172 },
+ { "test_conversion", test_conversion },
+ { "test_rewind", test_rewind },
+ { "test_bug6761", test_bug6761 },
+ { "test_view", test_view },
+ { "test_view_where", test_view_where },
+ { "test_view_2where", test_view_2where },
+ { "test_view_star", test_view_star },
+ { "test_view_insert", test_view_insert },
+ { "test_left_join_view", test_left_join_view },
+ { "test_view_insert_fields", test_view_insert_fields },
+ { "test_basic_cursors", test_basic_cursors },
+ { "test_cursors_with_union", test_cursors_with_union },
+ { "test_cursors_with_procedure", test_cursors_with_procedure },
+ { "test_truncation", test_truncation },
+ { "test_truncation_option", test_truncation_option },
+ { "test_client_character_set", test_client_character_set },
+ { "test_bug8330", test_bug8330 },
+ { "test_bug7990", test_bug7990 },
+ { "test_bug8378", test_bug8378 },
+ { "test_bug8722", test_bug8722 },
+ { "test_bug8880", test_bug8880 },
+ { "test_open_cursor_prepared_statement_query_cache",
+ test_open_cursor_prepared_statement_query_cache },
+ { "test_bug9159", test_bug9159 },
+ { "test_bug9520", test_bug9520 },
+ { "test_bug9478", test_bug9478 },
+ { "test_bug9643", test_bug9643 },
+ { "test_bug10729", test_bug10729 },
+ { "test_bug11111", test_bug11111 },
+ { "test_bug9992", test_bug9992 },
+ { "test_bug10736", test_bug10736 },
+ { "test_bug10794", test_bug10794 },
+ { "test_bug11172", test_bug11172 },
+ { "test_bug11656", test_bug11656 },
+ { "test_bug10214", test_bug10214 },
+ { "test_bug9735", test_bug9735 },
+ { "test_bug11183", test_bug11183 },
+ { "test_bug11037", test_bug11037 },
+ { "test_bug10760", test_bug10760 },
+ { "test_bug12001", test_bug12001 },
+ { "test_bug11718", test_bug11718 },
+ { "test_bug12925", test_bug12925 },
+ { "test_bug11909", test_bug11909 },
+ { "test_bug11901", test_bug11901 },
+ { "test_bug11904", test_bug11904 },
+ { "test_bug12243", test_bug12243 },
+ { "test_bug14210", test_bug14210 },
+ { "test_bug13488", test_bug13488 },
+ { "test_bug13524", test_bug13524 },
+ { "test_bug14845", test_bug14845 },
+ { "test_opt_reconnect", test_opt_reconnect },
+ { "test_bug15510", test_bug15510},
+#ifndef EMBEDDED_LIBRARY
+ { "test_bug12744", test_bug12744 },
+#endif
+ { "test_bug16143", test_bug16143 },
+ { "test_bug16144", test_bug16144 },
+ { "test_bug15613", test_bug15613 },
+ { "test_bug20152", test_bug20152 },
+ { "test_bug14169", test_bug14169 },
+ { "test_bug17667", test_bug17667 },
+ { "test_bug15752", test_bug15752 },
+ { "test_mysql_insert_id", test_mysql_insert_id },
+ { "test_bug19671", test_bug19671 },
+ { "test_bug21206", test_bug21206 },
+ { "test_bug21726", test_bug21726 },
+ { "test_bug15518", test_bug15518 },
+ { "test_bug23383", test_bug23383 },
+ { "test_bug32265", test_bug32265 },
+ { "test_bug21635", test_bug21635 },
+ { "test_status", test_status },
+ { "test_bug24179", test_bug24179 },
+ { "test_ps_query_cache", test_ps_query_cache },
+ { "test_bug28075", test_bug28075 },
+ { "test_bug27876", test_bug27876 },
+ { "test_bug28505", test_bug28505 },
+ { "test_bug28934", test_bug28934 },
+ { "test_bug27592", test_bug27592 },
+ { "test_bug29687", test_bug29687 },
+ { "test_bug29692", test_bug29692 },
+ { "test_bug29306", test_bug29306 },
+ { "test_change_user", test_change_user },
+ { "test_bug30472", test_bug30472 },
+ { "test_bug20023", test_bug20023 },
+ { "test_bug45010", test_bug45010 },
+ { "test_bug53371", test_bug53371 },
+ { "test_bug31418", test_bug31418 },
+ { "test_bug31669", test_bug31669 },
+ { "test_bug28386", test_bug28386 },
+ { "test_wl4166_1", test_wl4166_1 },
+ { "test_wl4166_2", test_wl4166_2 },
+ { "test_wl4166_3", test_wl4166_3 },
+ { "test_wl4166_4", test_wl4166_4 },
+ { "test_bug36004", test_bug36004 },
+ { "test_wl4284_1", test_wl4284_1 },
+ { "test_wl4435", test_wl4435 },
+ { "test_wl4435_2", test_wl4435_2 },
+ { "test_wl4435_3", test_wl4435_3 },
+ { "test_bug38486", test_bug38486 },
+ { "test_bug33831", test_bug33831 },
+ { "test_bug40365", test_bug40365 },
+ { "test_bug43560", test_bug43560 },
+ { "test_bug36326", test_bug36326 },
+ { "test_bug41078", test_bug41078 },
+ { "test_bug44495", test_bug44495 },
+ { "test_bug49972", test_bug49972 },
+ { "test_bug42373", test_bug42373 },
+ { "test_bug54041", test_bug54041 },
+ { "test_bug47485", test_bug47485 },
+ { "test_bug58036", test_bug58036 },
+ { "test_bug57058", test_bug57058 },
+ { "test_bug56976", test_bug56976 },
+ { "test_mdev3885", test_mdev3885 },
+ { "test_mdev4603", test_mdev4603 },
+ { "test_bug11766854", test_bug11766854 },
+ { "test_bug12337762", test_bug12337762 },
+ { "test_progress_reporting", test_progress_reporting },
+ { "test_bug11754979", test_bug11754979 },
+ { "test_bug13001491", test_bug13001491 },
+ { "test_mdev4326", test_mdev4326 },
+ { "test_ps_sp_out_params", test_ps_sp_out_params },
+#ifndef _WIN32
+ { "test_bug17512527", test_bug17512527},
+#endif
+ { "test_compressed_protocol", test_compressed_protocol },
+ { "test_big_packet", test_big_packet },
+ { "test_prepare_analyze", test_prepare_analyze },
+ { "test_mdev12579", test_mdev12579 },
+ { "test_mdev14013", test_mdev14013 },
+ { "test_mdev14013_1", test_mdev14013_1 },
+ { "test_mdev14454", test_mdev14454 },
+#ifndef EMBEDDED_LIBRARY
+ { "test_proxy_header", test_proxy_header},
+ { "test_bulk_autoinc", test_bulk_autoinc},
+ { "test_bulk_delete", test_bulk_delete },
+ { "test_bulk_replace", test_bulk_replace },
+ { "test_bulk_insert_returning", test_bulk_insert_returning },
+ { "test_bulk_delete_returning", test_bulk_delete_returning },
+#endif
+ { "test_ps_params_in_ctes", test_ps_params_in_ctes },
+ { "test_explain_meta", test_explain_meta },
+#ifndef EMBEDDED_LIBRARY
+ { "test_mdev19838", test_mdev19838 },
+#endif
+ { "test_mdev_16128", test_mdev_16128 },
+ { "test_mdev18408", test_mdev18408 },
+ { "test_mdev20261", test_mdev20261 },
+ { "test_mdev_30159", test_mdev_30159 },
+ { "test_execute_direct", test_execute_direct },
+ { "test_cache_metadata", test_cache_metadata},
+ { "test_mdev_10075", test_mdev_10075},
+ { 0, 0 }
+};
+
+
+static struct my_tests_st *get_my_tests() { return my_tests; }
diff --git a/tests/nonblock-wrappers.h b/tests/nonblock-wrappers.h
new file mode 100644
index 00000000..19737c8d
--- /dev/null
+++ b/tests/nonblock-wrappers.h
@@ -0,0 +1,479 @@
+/*
+ Copyright 2011 Kristian Nielsen and Monty Program Ab
+
+ This file is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Wrappers that re-implement the normal blocking libmysql API calls in terms
+ of the non-blocking API calls and explicit waiting.
+
+ Used to test the non-blocking calls using mysql_client_test.
+*/
+
+#ifndef _WIN32
+#include <poll.h>
+#else
+#include <WinSock2.h>
+#endif
+
+/*
+ Run the appropriate poll() syscall to wait for the event that libmysql
+ requested. Return which event(s) occurred.
+*/
+static int
+wait_for_mysql(MYSQL *mysql, int status)
+{
+#ifdef _WIN32
+ fd_set rs, ws, es;
+ int res;
+ struct timeval tv, *timeout;
+ my_socket s= mysql_get_socket(mysql);
+ FD_ZERO(&rs);
+ FD_ZERO(&ws);
+ FD_ZERO(&es);
+ if (status & MYSQL_WAIT_READ)
+ FD_SET(s, &rs);
+ if (status & MYSQL_WAIT_WRITE)
+ FD_SET(s, &ws);
+ if (status & MYSQL_WAIT_EXCEPT)
+ FD_SET(s, &es);
+ if (status & MYSQL_WAIT_TIMEOUT)
+ {
+ tv.tv_sec= mysql_get_timeout_value(mysql);
+ tv.tv_usec= 0;
+ timeout= &tv;
+ }
+ else
+ timeout= NULL;
+ res= select(1, &rs, &ws, &es, timeout);
+ if (res == 0)
+ return MYSQL_WAIT_TIMEOUT;
+ else if (res == SOCKET_ERROR)
+ return MYSQL_WAIT_TIMEOUT;
+ else
+ {
+ int status= 0;
+ if (FD_ISSET(s, &rs))
+ status|= MYSQL_WAIT_READ;
+ if (FD_ISSET(s, &ws))
+ status|= MYSQL_WAIT_WRITE;
+ if (FD_ISSET(s, &es))
+ status|= MYSQL_WAIT_EXCEPT;
+ return status;
+ }
+#else
+ struct pollfd pfd;
+ int timeout;
+ int res;
+
+ pfd.fd= mysql_get_socket(mysql);
+ pfd.events=
+ (status & MYSQL_WAIT_READ ? POLLIN : 0) |
+ (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) |
+ (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0);
+ if (status & MYSQL_WAIT_TIMEOUT)
+ timeout= 1000*mysql_get_timeout_value(mysql);
+ else
+ timeout= -1;
+ do {
+ res= poll(&pfd, 1, timeout);
+ /*
+ In a real event framework, we should re-compute the timeout on getting
+ EINTR to account for the time elapsed before the interruption.
+ */
+ } while (res < 0 && errno == EINTR);
+ if (res == 0)
+ return MYSQL_WAIT_TIMEOUT;
+ else if (res < 0)
+ return MYSQL_WAIT_TIMEOUT;
+ else
+ {
+ int status= 0;
+ if (pfd.revents & POLLIN)
+ status|= MYSQL_WAIT_READ;
+ if (pfd.revents & POLLOUT)
+ status|= MYSQL_WAIT_WRITE;
+ if (pfd.revents & POLLPRI)
+ status|= MYSQL_WAIT_EXCEPT;
+ return status;
+ }
+#endif
+}
+
+
+/*
+ If WRAP_NONBLOCK_ENABLED is defined, it is a variable that can be used to
+ enable or disable the use of non-blocking API wrappers. If true the
+ non-blocking API will be used, if false the normal blocking API will be
+ called directly.
+*/
+#ifdef WRAP_NONBLOCK_ENABLED
+#define USE_BLOCKING(name__, invoke_blocking__) \
+ if (!(WRAP_NONBLOCK_ENABLED)) return name__ invoke_blocking__;
+#define USE_BLOCKING_VOID_RETURN(name__, invoke__) \
+ if (!(WRAP_NONBLOCK_ENABLED)) { name__ invoke__; return; }
+#else
+#define USE_BLOCKING(name__, invoke_blocking__)
+#define USE_BLOCKING_VOID_RETURN(name__, invoke__)
+#endif
+
+/*
+ I would preferably have declared the wrappers static.
+ However, if we do so, compilers will warn about definitions not used, and
+ with -Werror this breaks compilation :-(
+*/
+#define MK_WRAPPER(ret_type__, name__, decl__, invoke__, invoke_blocking__, cont_arg__, mysql__) \
+ret_type__ wrap_ ## name__ decl__ \
+{ \
+ ret_type__ res; \
+ int status; \
+ USE_BLOCKING(name__, invoke_blocking__) \
+ status= name__ ## _start invoke__; \
+ while (status) \
+ { \
+ status= wait_for_mysql(mysql__, status); \
+ status= name__ ## _cont(&res, cont_arg__, status); \
+ } \
+ return res; \
+}
+
+#define MK_WRAPPER_VOID_RETURN(name__, decl__, invoke__, cont_arg__, mysql__) \
+void wrap_ ## name__ decl__ \
+{ \
+ int status; \
+ USE_BLOCKING_VOID_RETURN(name__, invoke__) \
+ status= name__ ## _start invoke__; \
+ while (status) \
+ { \
+ status= wait_for_mysql(mysql__, status); \
+ status= name__ ## _cont(cont_arg__, status); \
+ } \
+}
+
+MK_WRAPPER(
+ MYSQL *,
+ mysql_real_connect,
+ (MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag),
+ (&res, mysql, host, user, passwd, db, port, unix_socket, clientflag),
+ (mysql, host, user, passwd, db, port, unix_socket, clientflag),
+ mysql,
+ mysql)
+
+
+MK_WRAPPER(
+ int,
+ mysql_real_query,
+ (MYSQL *mysql, const char *stmt_str, unsigned long length),
+ (&res, mysql, stmt_str, length),
+ (mysql, stmt_str, length),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ MYSQL_ROW,
+ mysql_fetch_row,
+ (MYSQL_RES *result),
+ (&res, result),
+ (result),
+ result,
+ result->handle)
+
+MK_WRAPPER(
+ int,
+ mysql_set_character_set,
+ (MYSQL *mysql, const char *csname),
+ (&res, mysql, csname),
+ (mysql, csname),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_select_db,
+ (MYSQL *mysql, const char *db),
+ (&res, mysql, db),
+ (mysql, db),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_send_query,
+ (MYSQL *mysql, const char *q, unsigned long length),
+ (&res, mysql, q, length),
+ (mysql, q, length),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ MYSQL_RES *,
+ mysql_store_result,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+MK_WRAPPER_VOID_RETURN(
+ mysql_free_result,
+ (MYSQL_RES *result),
+ (result),
+ result,
+ result->handle)
+
+MK_WRAPPER_VOID_RETURN(
+ mysql_close,
+ (MYSQL *sock),
+ (sock),
+ sock,
+ sock)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_change_user,
+ (MYSQL *mysql, const char *user, const char *passwd, const char *db),
+ (&res, mysql, user, passwd, db),
+ (mysql, user, passwd, db),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_query,
+ (MYSQL *mysql, const char *q),
+ (&res, mysql, q),
+ (mysql, q),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_shutdown,
+ (MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level),
+ (&res, mysql, shutdown_level),
+ (mysql, shutdown_level),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_dump_debug_info,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_refresh,
+ (MYSQL *mysql, unsigned int refresh_options),
+ (&res, mysql, refresh_options),
+ (mysql, refresh_options),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_kill,
+ (MYSQL *mysql, unsigned long pid),
+ (&res, mysql, pid),
+ (mysql, pid),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_set_server_option,
+ (MYSQL *mysql, enum enum_mysql_set_option option),
+ (&res, mysql, option),
+ (mysql, option),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_ping,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ const char *,
+ mysql_stat,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_read_query_result,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_stmt_prepare,
+ (MYSQL_STMT *stmt, const char *query, unsigned long length),
+ (&res, stmt, query, length),
+ (stmt, query, length),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_stmt_execute,
+ (MYSQL_STMT *stmt),
+ (&res, stmt),
+ (stmt),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_stmt_fetch,
+ (MYSQL_STMT *stmt),
+ (&res, stmt),
+ (stmt),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_stmt_store_result,
+ (MYSQL_STMT *stmt),
+ (&res, stmt),
+ (stmt),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_stmt_close,
+ (MYSQL_STMT *stmt),
+ (&res, stmt),
+ (stmt),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_stmt_reset,
+ (MYSQL_STMT *stmt),
+ (&res, stmt),
+ (stmt),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_stmt_free_result,
+ (MYSQL_STMT *stmt),
+ (&res, stmt),
+ (stmt),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_stmt_send_long_data,
+ (MYSQL_STMT *stmt, unsigned int param_number, const char *data, unsigned long length),
+ (&res, stmt, param_number, data, length),
+ (stmt, param_number, data, length),
+ stmt,
+ stmt->mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_commit,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_rollback,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ my_bool,
+ mysql_autocommit,
+ (MYSQL *mysql, my_bool auto_mode),
+ (&res, mysql, auto_mode),
+ (mysql, auto_mode),
+ mysql,
+ mysql)
+
+MK_WRAPPER(
+ int,
+ mysql_next_result,
+ (MYSQL *mysql),
+ (&res, mysql),
+ (mysql),
+ mysql,
+ mysql)
+
+#undef USE_BLOCKING
+#undef MK_WRAPPER
+#undef MK_WRAPPER_VOID_RETURN
+
+
+#define mysql_real_connect wrap_mysql_real_connect
+#define mysql_real_query wrap_mysql_real_query
+#define mysql_fetch_row wrap_mysql_fetch_row
+#define mysql_set_character_set wrap_mysql_set_character_set
+#define mysql_select_db wrap_mysql_select_db
+#define mysql_send_query wrap_mysql_send_query
+#define mysql_store_result wrap_mysql_store_result
+#define mysql_free_result wrap_mysql_free_result
+#define mysql_close wrap_mysql_close
+#define mysql_change_user wrap_mysql_change_user
+#define mysql_query wrap_mysql_query
+#define mysql_shutdown wrap_mysql_shutdown
+#define mysql_dump_debug_info wrap_mysql_dump_debug_info
+#define mysql_refresh wrap_mysql_refresh
+#define mysql_kill wrap_mysql_kill
+#define mysql_set_server_option wrap_mysql_set_server_option
+#define mysql_ping wrap_mysql_ping
+#define mysql_stat wrap_mysql_stat
+#define mysql_list_dbs wrap_mysql_list_dbs
+#define mysql_list_tables wrap_mysql_list_tables
+#define mysql_list_processes wrap_mysql_list_processes
+#define mysql_read_query_result wrap_mysql_read_query_result
+#define mysql_stmt_prepare wrap_mysql_stmt_prepare
+#define mysql_stmt_execute wrap_mysql_stmt_execute
+#define mysql_stmt_fetch wrap_mysql_stmt_fetch
+#define mysql_stmt_store_result wrap_mysql_stmt_store_result
+#define mysql_stmt_close wrap_mysql_stmt_close
+#define mysql_stmt_reset wrap_mysql_stmt_reset
+#define mysql_stmt_free_result wrap_mysql_stmt_free_result
+#define mysql_stmt_send_long_data wrap_mysql_stmt_send_long_data
+#define mysql_commit wrap_mysql_commit
+#define mysql_rollback wrap_mysql_rollback
+#define mysql_autocommit wrap_mysql_autocommit
+#define mysql_next_result wrap_mysql_next_result
diff --git a/tests/pmail.pl b/tests/pmail.pl
new file mode 100755
index 00000000..441051de
--- /dev/null
+++ b/tests/pmail.pl
@@ -0,0 +1,339 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000, 2005 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# Prints mails to standard output
+#
+####
+#### Standard inits and get options
+####
+
+use DBI;
+use Getopt::Long;
+
+$VER="2.0";
+
+@fldnms= ("mail_from","mail_to","cc","date","time_zone","file","sbj","txt");
+my $fields= 0;
+my $base_q= "";
+my $mail_count= 0;
+
+$opt_user= $opt_password= "";
+$opt_socket= "/tmp/mysql.sock";
+$opt_port= 3306;
+$opt_db="mail";
+$opt_table="my_mail";
+$opt_help=$opt_count=0;
+$opt_thread= 0;
+$opt_host= "";
+$opt_message_id= 0;
+
+GetOptions("help","count","port=i","db=s","table=s","host=s","password=s",
+ "user=s","socket=s", "thread","message_id") || usage();
+
+if ($opt_host eq '')
+{
+ $opt_host = "localhost";
+}
+
+if ($opt_help || !$ARGV[0])
+{
+ usage();
+}
+
+####
+#### Connect and parsing the query to MySQL
+####
+
+$dbh= DBI->connect("DBI:MariaDB:$opt_db:$opt_host:port=$opt_port:mariadb_socket=$opt_socket", $opt_user,$opt_password, { PrintError => 0})
+|| die $DBI::errstr;
+
+main();
+
+####
+#### main
+####
+
+sub main
+{
+ my ($row, $val, $q, $mail, $sth);
+
+ if ($opt_count)
+ {
+ count_mails();
+ }
+
+ $base_q= "SELECT ";
+ foreach $val (@fldnms)
+ {
+ if (!$fields)
+ {
+ $base_q.= "$val";
+ }
+ else
+ {
+ $base_q.= ",$val";
+ }
+ $fields++;
+ }
+ $base_q.= ",message_id" if ($opt_thread || $opt_message_id);
+ $base_q.= " FROM $opt_table";
+ $q= " WHERE $ARGV[0]";
+
+ $sth= $dbh->prepare($base_q . $q);
+ if (!$sth->execute)
+ {
+ print "$DBI::errstr\n";
+ $sth->finish;
+ die;
+ }
+ for (; ($row= $sth->fetchrow_arrayref); $mail_count++)
+ {
+ for ($i= 0; $i < $fields; $i++)
+ {
+ if ($opt_message_id)
+ {
+ $mail[$fields][$mail_count]= $row->[$fields];
+ $mail[$fields][$mail_count].= "\nNumber of Replies: " . get_nr_replies($row->[$fields]);
+ }
+ $mail[$i][$mail_count]= $row->[$i];
+ }
+ if ($opt_thread)
+ {
+ get_mail_by_message_id($row->[$fields], $mail);
+ }
+ }
+ print_mails($mail);
+}
+
+####
+#### Function, which fetches mail by searching in-reply-to with
+#### a given message_id. Saves the value (mail) in mail variable.
+#### Returns the message id of the mail found and searches again
+#### and saves, until no more mails are found with that message_id.
+####
+
+sub get_mail_by_message_id
+{
+ my ($message_id, $mail)= @_;
+ my ($q, $query, $i, $row, $sth);
+
+ $q= " WHERE in_reply_to = \"$message_id\"";
+ $query= $base_q . $q;
+ $sth= $dbh->prepare($query);
+ if (!$sth->execute)
+ {
+ print "QUERY: $query\n$DBI::errstr\n";
+ $sth->finish;
+ die;
+ }
+ while (($row= $sth->fetchrow_arrayref))
+ {
+ $mail_count++;
+ for ($i= 0; $i < $fields; $i++)
+ {
+ if ($opt_message_id)
+ {
+ $mail[$fields][$mail_count]= $row->[$fields];
+ $mail[$fields][$mail_count].= "\nNumber of Replies: " . get_nr_replies($row->[$fields]);
+ }
+ $mail[$i][$mail_count]= $row->[$i];
+ }
+ $new_message_id= $row->[$fields];
+ if (defined($new_message_id) && length($new_message_id))
+ {
+ get_mail_by_message_id($new_message_id, $mail);
+ }
+ }
+ return;
+}
+
+####
+#### Get number of replies for a given message_id
+####
+
+sub get_nr_replies
+{
+ my ($message_id)= @_;
+ my ($sth, $sth2, $q, $row, $row2, $nr_replies);
+
+ $nr_replies= 0;
+ $q= "SELECT COUNT(*) FROM my_mail WHERE in_reply_to=\"$message_id\"";
+ $sth= $dbh->prepare($q);
+ if (!$sth->execute)
+ {
+ print "QUERY: $q\n$DBI::errstr\n";
+ $sth->finish;
+ die;
+ }
+ while (($row= $sth->fetchrow_arrayref))
+ {
+ if (($nr_replies= $row->[0]))
+ {
+ $q= "SELECT message_id FROM my_mail WHERE in_reply_to=\"$message_id\"";
+ $sth2= $dbh->prepare($q);
+ if (!$sth2->execute)
+ {
+ print "QUERY: $q\n$DBI::errstr\n";
+ $sth->finish;
+ die;
+ }
+ while (($row2= $sth2->fetchrow_arrayref))
+ {
+ # There may be several replies to the same mail. Also the
+ # replies to the 'parent' mail may contain several replies
+ # and so on. Thus we need to calculate it recursively.
+ $nr_replies+= get_nr_replies($row2->[0]);
+ }
+ }
+ return $nr_replies;
+ }
+}
+
+####
+#### Print mails
+####
+
+sub print_mails
+{
+ my ($mail)= @_;
+ my ($i);
+
+ for ($i=0; $mail[0][$i]; $i++)
+ {
+ print "#" x 33;
+ print " " . ($i+1) . ". Mail ";
+ print "#" x 33;
+ print "\n";
+ if ($opt_message_id)
+ {
+ print "Msg ID: $mail[$fields][$i]\n";
+ }
+ print "From: $mail[0][$i]\n";
+ print "To: $mail[1][$i]\n";
+ print "Cc:" . (defined($mail[2][$i]) ? $mail[2][$i] : "") . "\n";
+ print "Date: $mail[3][$i]\n";
+ print "Timezone: $mail[4][$i]\n";
+ print "File: $mail[5][$i]\n";
+ print "Subject: $mail[6][$i]\n";
+ print "Message:\n$mail[7][$i]\n";
+ }
+ print "#" x 20;
+ print " Summary: ";
+ if ($i == 1)
+ {
+ print "$i Mail ";
+ print "matches the query ";
+ }
+ else
+ {
+ print "$i Mails ";
+ print "match the query ";
+ }
+ print "#" x 20;
+ print "\n";
+}
+
+####
+#### Count mails that matches the query, but don't show them
+####
+
+sub count_mails
+{
+ my ($sth);
+
+ $sth= $dbh->prepare("select count(*) from $opt_table where $ARGV[0]");
+ if (!$sth->execute)
+ {
+ print "$DBI::errstr\n";
+ $sth->finish;
+ die;
+ }
+ while (($row= $sth->fetchrow_arrayref))
+ {
+ $mail_count= $row->[0];
+ }
+ if ($mail_count == 1)
+ {
+ print "$mail_count Mail matches the query.\n";
+ }
+ else
+ {
+ print "$mail_count Mails match the query.\n";
+ }
+ exit;
+}
+
+####
+#### Usage
+####
+
+sub usage
+{
+ print <<EOF;
+ pmail version $VER by Jani Tolonen
+
+ Usage: pmail [options] "SQL where clause"
+ Options:
+ --help show this help
+ --count Shows how many mails matches the query, but not the mails.
+ --db= database to use (Default: $opt_db)
+ --host= Hostname which to connect (Default: $opt_host)
+ --socket= Unix socket to be used for connection (Default: $opt_socket)
+ --password= Password to use for mysql
+ --user= User to be used for mysql connection, if not current user
+ --port= mysql port to be used (Default: $opt_port)
+ --thread Will search for possible replies to emails found by the search
+ criteria. Replies, if found, will be displayed right after the
+ original mail.
+ --message_id Display message_id on top of each mail. Useful when searching
+ email threads with --thread. On the second line is the number
+ of replies to the same thread, starting counting from that
+ mail (excluding possible parent mails).
+ "SQL where clause" is the end of the select clause,
+ where the condition is expressed. The result will
+ be the mail(s) that matches the condition and
+ will be displayed with the fields:
+ - From
+ - To
+ - Cc
+ - Date
+ - Timezone
+ - File (Where from the current mail was loaded into the database)
+ - Subject
+ - Message text
+ The field names that can be used in the where clause are:
+ Field Type
+ - message_id varchar(255) # Use with --thread and --message_id
+ - in_reply_to varchar(255) # Internally used by --thread
+ - mail_from varchar(120)
+ - date datetime
+ - sbj varchar(200)
+ - txt mediumtext
+ - cc text
+ - mail_to text
+ - time_zone varchar(6)
+ - reply varchar(120)
+ - file varchar(32)
+ - hash int(11)
+ An example of pmail:
+ pmail "txt like '%libmysql.dll%' and sbj like '%delphi%'"
+ NOTE: the txt field is NOT case sensitive!
+EOF
+ exit(0);
+}
diff --git a/tests/rename_test.pl b/tests/rename_test.pl
new file mode 100755
index 00000000..25bc2a3e
--- /dev/null
+++ b/tests/rename_test.pl
@@ -0,0 +1,221 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000, 2001 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# This is a test with uses processes to insert, select and drop tables.
+#
+
+$opt_loop_count=100000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_delete=$opt_skip_flush=0;
+$opt_host=""; $opt_db="test";
+
+GetOptions("host=s","db=s","loop-count=i","skip-create","skip-delete",
+"skip-flush") || die "Aborted";
+
+print "Testing 5 multiple connections to a server with 1 insert, 1 rename\n";
+print "1 select and 1 flush thread\n";
+
+$firsttable = "bench_f1";
+
+####
+#### Start timing and start test
+####
+
+$start_time=new Benchmark;
+if (!$opt_skip_create)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table if exists $firsttable, ${firsttable}_1, ${firsttable}_2");
+
+ print "Creating table $firsttable in database $opt_db\n";
+ $dbh->do("create table $firsttable (id int(6) not null, info varchar(32), marker char(1), primary key(id))") || die $DBI::errstr;
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+test_insert() if (($pid=fork()) == 0); $work{$pid}="insert";
+test_rename(1) if (($pid=fork()) == 0); $work{$pid}="rename 1";
+test_rename(2) if (($pid=fork()) == 0); $work{$pid}="rename 2";
+test_select() if (($pid=fork()) == 0); $work{$pid}="select";
+if (!$opt_skip_flush)
+{
+ test_flush() if (($pid=fork()) == 0); $work{$pid}="flush";
+}
+$errors=0;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ $errors++ if ($ret != 0);
+}
+
+if (!$opt_skip_delete && !$errors)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $dbh->do("drop table $firsttable");
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+print ($errors ? "Test failed\n" :"Test ok\n");
+
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+#
+# Insert records in the table. Delete table when test is finished
+#
+
+sub test_insert
+{
+ my ($dbh,$i,$error);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ if (!$dbh->do("insert into $firsttable values ($i,'This is entry $i','')"))
+ {
+ $error=$dbh->errstr;
+ $dbh->do("drop table ${firsttable}"); # End other threads
+ die "Warning; Got error on insert: " . $error . "\n";
+ }
+ }
+ sleep(1);
+ $dbh->do("drop table ${firsttable}") || die "Got error on drop table: " . $dbh->errstr . "\n";
+ $dbh->disconnect; $dbh=0;
+ print "Test_insert: Inserted $i rows\n";
+ exit(0);
+}
+
+
+sub test_rename
+{
+ my ($id) = @_;
+ my ($dbh,$i,$error_counter,$sleep_time);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+ $error_counter=0;
+ $sleep_time=2;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ sleep($sleep_time);
+ $dbh->do("create table ${firsttable}_$id (id int(6) not null, info varchar(32), marker char(1), primary key(id))") || die $DBI::errstr;
+ if (!$dbh->do("rename table $firsttable to ${firsttable}_${id}_1, ${firsttable}_$id to ${firsttable}"))
+ {
+ last if ($dbh->errstr =~ /^Can\'t find/);
+ die "Got error on rename: " . $dbh->errstr . "\n";
+ }
+ $dbh->do("drop table ${firsttable}_${id}_1") || die "Got error on drop table: " . $dbh->errstr . "\n";
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_drop: Did a drop $i times\n";
+ exit(0);
+}
+
+
+#
+# select records
+#
+
+sub test_select
+{
+ my ($dbh,$i,$sth,@row,$sleep_time);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $sleep_time=3;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ sleep($sleep_time);
+ $sth=$dbh->prepare("select sum(t.id) from $firsttable as t,$firsttable as t2") || die "Got error on select: $dbh->errstr;\n";
+ if ($sth->execute)
+ {
+ @row = $sth->fetchrow_array();
+ $sth->finish;
+ }
+ else
+ {
+ $sth->finish;
+ last if (! ($dbh->errstr =~ /doesn\'t exist/));
+ die "Got error on select: " . $dbh->errstr . "\n";
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: ok\n";
+ exit(0);
+}
+
+#
+# flush records
+#
+
+sub test_flush
+{
+ my ($dbh,$i,$sth,@row,$error_counter,$sleep_time);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ $error_counter=0;
+ $sleep_time=5;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ sleep($sleep_time);
+ $sth=$dbh->prepare("select count(*) from $firsttable") || die "Got error on prepar: $dbh->errstr;\n";
+ if ($sth->execute)
+ {
+ @row = $sth->fetchrow_array();
+ $sth->finish;
+ $sleep_time=5;
+ $dbh->do("flush tables $firsttable") || die "Got error on flush table: " . $dbh->errstr . "\n";
+ }
+ else
+ {
+ last if (! ($dbh->errstr =~ /doesn\'t exist/));
+ die "Got error on flush: " . $dbh->errstr . "\n";
+ }
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_select: ok\n";
+ exit(0);
+}
diff --git a/tests/restore-lock.smack b/tests/restore-lock.smack
new file mode 100755
index 00000000..a591ab2d
--- /dev/null
+++ b/tests/restore-lock.smack
@@ -0,0 +1,63 @@
+#! /usr/local/bin/mysql-super-smack
+
+query "select"
+ {
+ type "select";
+ query "select count(*) from w";
+ has_result_set "y";
+ parsed "n";
+ }
+
+query "restore"
+ {
+ type "restore";
+ query "restore table w from '/tmp'";
+ has_result_set "y";
+ parsed "n";
+ }
+
+query "drop"
+ {
+ type "drop";
+ query "drop table if exists w";
+ has_result_set "n";
+ parsed "n";
+ }
+
+
+
+client "restorer"
+ {
+ user "root";
+ pass "";
+ host "localhost";
+ db "test";
+ query_barrel "1 drop 1 restore";
+ }
+
+client "selector"
+ {
+ user "root";
+ pass "";
+ host "localhost";
+ db "test";
+ query_barrel "-3 1 select";
+ }
+
+main
+ {
+ selector.init();
+ restorer.init();
+ selector.create_threads(1);
+ restorer.create_threads(1);
+ selector.set_num_rounds(1);
+ restorer.set_num_rounds(1);
+ selector.connect();
+ restorer.connect();
+ restorer.unload_query_barrel();
+ selector.unload_query_barrel();
+ restorer.collect_threads();
+ selector.collect_threads();
+ selector.disconnect();
+ restorer.disconnect();
+ } \ No newline at end of file
diff --git a/tests/select_test.c b/tests/select_test.c
new file mode 100644
index 00000000..648a8613
--- /dev/null
+++ b/tests/select_test.c
@@ -0,0 +1,74 @@
+/* Copyright (c) 2000, 2002-2004 MySQL AB
+ Use is subject to license terms
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#if defined(_WIN32) || defined(_WIN64)
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql.h"
+
+#define SELECT_QUERY "select name from test where num = %d"
+
+
+int main(int argc, char **argv)
+{
+ int count, num;
+ MYSQL mysql,*sock;
+ MYSQL_RES *res;
+ char qbuf[160];
+
+ if (argc != 3)
+ {
+ fprintf(stderr,"usage : select_test <dbname> <num>\n\n");
+ exit(1);
+ }
+
+ mysql_init(&mysql);
+ if (!(sock = mysql_real_connect(&mysql,NULL,0,0,argv[1],0,NULL,0)))
+ {
+ fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql));
+ perror("");
+ exit(1);
+ }
+ mysql.reconnect= 1;
+
+ count = 0;
+ num = atoi(argv[2]);
+ while (count < num)
+ {
+ sprintf(qbuf,SELECT_QUERY,count);
+ if(mysql_query(sock,qbuf))
+ {
+ fprintf(stderr,"Query failed (%s)\n",mysql_error(sock));
+ exit(1);
+ }
+ if (!(res=mysql_store_result(sock)))
+ {
+ fprintf(stderr,"Couldn't get result from %s\n",
+ mysql_error(sock));
+ exit(1);
+ }
+#ifdef TEST
+ printf("number of fields: %d\n",mysql_num_fields(res));
+#endif
+ mysql_free_result(res);
+ count++;
+ }
+ mysql_close(sock);
+ exit(0);
+ return 0; /* Keep some compilers happy */
+}
diff --git a/tests/showdb_test.c b/tests/showdb_test.c
new file mode 100644
index 00000000..e6b8989e
--- /dev/null
+++ b/tests/showdb_test.c
@@ -0,0 +1,67 @@
+/* Copyright (c) 2000, 2003, 2004 MySQL AB
+ Use is subject to license terms
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql.h"
+
+#define SELECT_QUERY "select name from test where num = %d"
+
+
+int main(int argc, char **argv)
+{
+ int count, num;
+ MYSQL mysql,*sock;
+ MYSQL_RES *res;
+ char qbuf[160];
+
+ if (argc != 3)
+ {
+ fprintf(stderr,"usage : select_test <dbname> <num>\n\n");
+ exit(1);
+ }
+
+ mysql_init(&mysql);
+ if (!(sock = mysql_real_connect(&mysql,NULL,0,0,argv[1],0,NULL,0)))
+ {
+ fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql));
+ perror("");
+ exit(1);
+ }
+ mysql.reconnect= 1;
+
+ count = 0;
+ num = atoi(argv[2]);
+ while (count < num)
+ {
+ sprintf(qbuf,SELECT_QUERY,count);
+ if(!(res=mysql_list_dbs(sock,NULL)))
+ {
+ fprintf(stderr,"Query failed (%s)\n",mysql_error(sock));
+ exit(1);
+ }
+ printf("number of fields: %d\n",mysql_num_rows(res));
+ mysql_free_result(res);
+ count++;
+ }
+ mysql_close(sock);
+ exit(0);
+ return 0; /* Keep some compilers happy */
+}
diff --git a/tests/ssl_test.c b/tests/ssl_test.c
new file mode 100644
index 00000000..d15d553b
--- /dev/null
+++ b/tests/ssl_test.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2000, 2003, 2004, 2007 MySQL AB
+ Use is subject to license terms
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "mysql.h"
+#include "config.h"
+#define SELECT_QUERY "select name from test where num = %d"
+
+
+int main(int argc, char **argv)
+{
+#ifdef HAVE_OPENSSL
+ int count, num;
+ MYSQL mysql,*sock;
+ MYSQL_RES *res;
+ char qbuf[160];
+
+ if (argc != 3)
+ {
+ fprintf(stderr,"usage : ssl_test <dbname> <num>\n\n");
+ exit(1);
+ }
+
+ mysql_init(&mysql);
+#ifdef HAVE_OPENSSL
+ mysql_ssl_set(&mysql,"../SSL/MySQL-client-key.pem",
+ "../SSL/MySQL-client-cert.pem",
+ "../SSL/MySQL-ca-cert.pem", 0, 0);
+#endif
+ if (!(sock = mysql_real_connect(&mysql,"127.0.0.1",0,0,argv[1],MYSQL_PORT,NULL,0)))
+ {
+ fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql));
+ perror("");
+ exit(1);
+ }
+ mysql.reconnect= 1;
+ count = 0;
+ num = atoi(argv[2]);
+ while (count < num)
+ {
+ sprintf(qbuf,SELECT_QUERY,count);
+ if(mysql_query(sock,qbuf))
+ {
+ fprintf(stderr,"Query failed (%s)\n",mysql_error(sock));
+ exit(1);
+ }
+ if (!(res=mysql_store_result(sock)))
+ {
+ fprintf(stderr,"Couldn't get result from query failed (%s)\n",
+ mysql_error(sock));
+ exit(1);
+ }
+#ifdef TEST
+ printf("number of fields: %d\n",mysql_num_fields(res));
+#endif
+ mysql_free_result(res);
+ count++;
+ }
+ mysql_close(sock);
+#else /* HAVE_OPENSSL */
+ printf("ssl_test: SSL not configured.\n");
+#endif /* HAVE_OPENSSL */
+ exit(0);
+ return 0; /* Keep some compilers happy */
+}
diff --git a/tests/table_types.pl b/tests/table_types.pl
new file mode 100755
index 00000000..782b8f25
--- /dev/null
+++ b/tests/table_types.pl
@@ -0,0 +1,232 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000, 2003 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+use DBI;
+use Benchmark;
+
+$opt_loop_count=100000; # number of rows/3
+$small_loop_count=10; # Loop for full table retrieval
+$range_loop_count=$small_loop_count*50;
+$many_keys_loop_count=$opt_loop_count;
+
+chomp($pwd = `pwd`); $pwd = "." if ($pwd eq '');
+require "$pwd/bench-init.pl" || die "Can't read Configuration file: $!\n";
+
+if ($opt_loop_count < 256)
+{
+ $opt_loop_count=256; # Some tests must have some data to work!
+}
+
+if ($opt_small_test)
+{
+ $opt_loop_count/=100;
+ $range_loop_count/=10;
+ $many_keys_loop_count=$opt_loop_count/10;
+}
+elsif ($opt_small_tables)
+{
+ $opt_loop_count=10000; # number of rows/3
+ $many_keys_loop_count=$opt_loop_count;
+}
+elsif ($opt_small_key_tables)
+{
+ $many_keys_loop_count/=10;
+}
+
+print "Testing the speed difference between some table types\n";
+
+####
+#### Generating random keys
+####
+
+print "Generating random keys\n";
+$random[$opt_loop_count]=0;
+for ($i=0 ; $i < $opt_loop_count ; $i++)
+{
+ $random[$i]=$i+$opt_loop_count;
+}
+
+my $tmpvar=1;
+for ($i=0 ; $i < $opt_loop_count ; $i++)
+{
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % $opt_loop_count);
+ $swap=$tmpvar % $opt_loop_count;
+ $tmp=$random[$i]; $random[$i]=$random[$swap]; $random[$swap]=$tmp;
+}
+
+$total_rows=$opt_loop_count*3;
+
+####
+#### Connect and start timeing
+####
+$start_time=new Benchmark;
+$dbh = $server->connect();
+####
+#### Create needed tables
+####
+
+$table_name="bench1";
+test($table_name,"type=isam","char");
+test($table_name,"type=myisam pack_keys=0","char");
+test($table_name,"type=myisam pack_keys=0","char");
+test($table_name,"type=myisam pack_keys=0 checksum=1","char");
+test($table_name,"type=myisam pack_keys=1","char");
+
+test($table_name,"type=isam","varchar");
+test($table_name,"type=myisam pack_keys=1","varchar");
+test($table_name,"type=myisam pack_keys=0","varchar");
+test($table_name,"type=myisam pack_keys=0 checksum=1","varchar");
+
+#test("type=heap","char"); # The default table sizes is a bit big for this one
+
+$dbh->disconnect;
+exit (0);
+
+sub test {
+ my ($name,$options,$chartype)=@_;
+
+ print "\nTesting with options: '$options'\n";
+ $dbh->do("drop table $name");
+ do_many($dbh,$server->create("$name",
+ ["id int NOT NULL",
+ "id2 int NOT NULL",
+ "id3 int NOT NULL",
+ "dummy1 $chartype(30)"],
+ ["primary key (id,id2)",
+ "index index_id3 (id3)"],
+ $options));
+
+ if ($opt_lock_tables)
+ {
+ $sth = $dbh->do("LOCK TABLES $name WRITE") || die $DBI::errstr;
+ }
+
+ if ($opt_fast && defined($server->{vacuum}))
+ {
+ $server->vacuum(\$dbh,1);
+ }
+
+ ####
+ #### Insert $total_rows records in order, in reverse order and random.
+ ####
+
+ $loop_time=new Benchmark;
+
+ if ($opt_fast_insert)
+ {
+ $query="insert into $name values ";
+ }
+ else
+ {
+ $query="insert into $name (id,id2,id3,dummy1) values ";
+ }
+
+ if (($opt_fast || $opt_fast_insert) && $limits->{'multi_value_insert'})
+ {
+ $query_size=$server->{'limits'}->{'query_size'};
+
+ print "Inserting $opt_loop_count multiple-value rows in order\n";
+ $res=$query;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmp= "($i,$i,$i,'ABCDEFGHIJ'),";
+ if (length($tmp)+length($res) < $query_size)
+ {
+ $res.= $tmp;
+ }
+ else
+ {
+ $sth = $dbh->do(substr($res,0,length($res)-1)) or die $DBI::errstr;
+ $res=$query . $tmp;
+ }
+ }
+ print "Inserting $opt_loop_count multiple-value rows in reverse order\n";
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmp= "(" . ($total_rows-1-$i) . "," .($total_rows-1-$i) .
+ "," .($total_rows-1-$i) . ",'BCDEFGHIJK'),";
+ if (length($tmp)+length($res) < $query_size)
+ {
+ $res.= $tmp;
+ }
+ else
+ {
+ $sth = $dbh->do(substr($res,0,length($res)-1)) or die $DBI::errstr;
+ $res=$query . $tmp;
+ }
+ }
+ print "Inserting $opt_loop_count multiple-value rows in random order\n";
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmp= "(" . $random[$i] . "," . $random[$i] . "," . $random[$i] .
+ ",'CDEFGHIJKL')," or die $DBI::errstr;
+ if (length($tmp)+length($res) < $query_size)
+ {
+ $res.= $tmp;
+ }
+ else
+ {
+ $sth = $dbh->do(substr($res,0,length($res)-1)) or die $DBI::errstr;
+ $res=$query . $tmp;
+ }
+ }
+ $sth = $dbh->do(substr($res,0,length($res)-1)) or die $DBI::errstr;
+ }
+ else
+ {
+ print "Inserting $opt_loop_count rows in order\n";
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $sth = $dbh->do($query . "($i,$i,$i,'ABCDEFGHIJ')") or die $DBI::errstr;
+ }
+
+ print "Inserting $opt_loop_count rows in reverse order\n";
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $sth = $dbh->do($query . "(" . ($total_rows-1-$i) . "," .
+ ($total_rows-1-$i) . "," .
+ ($total_rows-1-$i) . ",'BCDEFGHIJK')")
+ or die $DBI::errstr;
+ }
+
+ print "Inserting $opt_loop_count rows in random order\n";
+
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $sth = $dbh->do($query . "(". $random[$i] . "," . $random[$i] .
+ "," . $random[$i] . ",'CDEFGHIJKL')") or die $DBI::errstr;
+ }
+ }
+
+ $end_time=new Benchmark;
+ print "Time for insert (" . ($total_rows) . "): " .
+ timestr(timediff($end_time, $loop_time),"all") . "\n\n";
+
+ if ($opt_fast && defined($server->{vacuum}))
+ {
+ $server->vacuum(\$dbh,1);
+ }
+
+ $sth=$dbh->prepare("show table status like '$name'");
+ $sth->execute || die "Show table status returned error: $DBI::errstr\n";
+ while (@row = $sth->fetchrow_array)
+ {
+ print join("| ",@row) . " |\n";
+ }
+ $dbh->do("drop table $name") if (!$opt_skip_delete);
+}
diff --git a/tests/test_delayed_insert.pl b/tests/test_delayed_insert.pl
new file mode 100755
index 00000000..fdcd10e8
--- /dev/null
+++ b/tests/test_delayed_insert.pl
@@ -0,0 +1,381 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2000, 2001 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+# This is a test for INSERT DELAYED
+#
+
+$opt_loop_count=10000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+ $opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_host=$opt_user=$opt_password=""; $opt_db="test";
+
+GetOptions("host=s","db=s","loop-count=i","skip-create","skip-in","skip-delete",
+"verbose","fast-insert","lock-tables","debug","fast","force") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+print "Testing 8 multiple connections to a server with 1 insert, 2 delayed\n";
+print "insert, 1 update, 1 delete, 1 flush tables and 3 select connections.\n";
+
+$firsttable = "bench_f1";
+$secondtable = "bench_f2";
+
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+if (!$opt_skip_create)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $Mysql::QUIET = 1;
+ $dbh->do("drop table if exists $firsttable,$secondtable");
+ $Mysql::QUIET = 0;
+
+ print "Creating tables $firsttable and $secondtable in database $opt_db\n";
+ $dbh->do("create table $firsttable (id int(6) not null, info varchar(32), marker char(1), primary key(id))") or die $DBI::errstr;
+ $dbh->do("create table $secondtable (id int(6) not null, row int(3) not null,value double, primary key(id,row))") or die $DBI::errstr;
+
+ $dbh->disconnect;
+}
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+test_1() if (($pid=fork()) == 0); $work{$pid}="insert";
+test_delayed_1() if (($pid=fork()) == 0); $work{$pid}="delayed_insert1";
+test_delayed_2() if (($pid=fork()) == 0); $work{$pid}="delayed_insert2";
+test_2() if (($pid=fork()) == 0); $work{$pid}="update";
+test_3() if (($pid=fork()) == 0); $work{$pid}="select1";
+test_4() if (($pid=fork()) == 0); $work{$pid}="select2";
+test_5() if (($pid=fork()) == 0); $work{$pid}="select3";
+test_del() if (($pid=fork()) == 0); $work{$pid}="delete";
+test_flush() if (($pid=fork()) == 0); $work{$pid}="flush";
+
+$errors=0;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ $errors++ if ($ret != 0);
+}
+
+if (!$opt_skip_delete && !$errors)
+{
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $dbh->do("drop table $firsttable");
+ $dbh->do("drop table $secondtable");
+}
+print ($errors ? "Test failed\n" :"Test ok\n");
+
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+#
+# Insert records in the two tables
+#
+
+sub test_1
+{
+ my ($dbh,$tmpvar,$rows,$found,$i);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $tmpvar=1;
+ $rows=$found=0;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % 100000);
+ $dbh->do("insert into $firsttable values ($i,'This is entry $i','')") || die "Got error on insert: $DBI::errstr\n";
+ $row_count=($i % 7)+1;
+ $rows+=1+$row_count;
+ for ($j=0 ; $j < $row_count; $j++)
+ {
+ $dbh->do("insert into $secondtable values ($i,$j,0)") || die "Got error on insert: $DBI::errstr\n";
+ }
+ }
+ $dbh->disconnect;
+ print "Test_1: Inserted $rows rows\n";
+ exit(0);
+}
+
+
+sub test_delayed_1
+{
+ my ($dbh,$tmpvar,$rows,$found,$i,$id);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $tmpvar=1;
+ $rows=$found=0;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % 100000);
+ $id=$i+$opt_loop_count;
+ $dbh->do("insert delayed into $firsttable values ($id,'This is entry $id','')") || die "Got error on insert: $DBI::errstr\n";
+ $row_count=($i % 7)+1;
+ $rows+=1+$row_count;
+ for ($j=0 ; $j < $row_count; $j++)
+ {
+ $dbh->do("insert into $secondtable values ($id,$j,0)") || die "Got error on insert: $DBI::errstr\n";
+ }
+ if (($tmpvar % 100) == 0)
+ {
+ $dbh->do("select max(info) from $firsttable") || die "Got error on select max(info): $DBI::errstr\n";
+ $dbh->do("select max(value) from $secondtable") || die "Got error on select max(info): $DBI::errstr\n";
+ $found+=2;
+ }
+ }
+ $dbh->disconnect;
+ print "Test_1: Inserted delayed $rows rows, found $found rows\n";
+ exit(0);
+}
+
+
+sub test_delayed_2
+{
+ my ($dbh,$tmpvar,$rows,$found,$i,$id);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $tmpvar=1;
+ $rows=$found=0;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % 100000);
+ $id=$i+$opt_loop_count*2;
+ $dbh->do("insert delayed into $firsttable values ($id,'This is entry $id','')") || die "Got error on insert: $DBI::errstr\n";
+ $row_count=($i % 7)+1;
+ $rows+=1+$row_count;
+ for ($j=0 ; $j < $row_count; $j++)
+ {
+ $dbh->do("insert delayed into $secondtable values ($id,$j,0)") || die "Got error on insert: $DBI::errstr\n";
+ }
+ if (($tmpvar % 100) == 0)
+ {
+ $dbh->do("select max(info) from $firsttable") || die "Got error on select max(info): $DBI::errstr\n";
+ $dbh->do("select max(value) from $secondtable") || die "Got error on select max(info): $DBI::errstr\n";
+ $found+=2;
+ }
+ }
+ $dbh->disconnect;
+ print "Test_1: Inserted delayed $rows rows, found $found rows\n";
+ exit(0);
+}
+
+#
+# Update records in both tables
+#
+
+sub test_2
+{
+ my ($dbh,$id,$tmpvar,$rows,$found,$i,$max_id,$tmp,$sth,$count);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $tmpvar=111111;
+ $rows=$found=$max_id=$id=0;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmp=(($tmpvar + 63) + $i)*3;
+ $tmp=$tmp-int($tmp/100000)*100000;
+ $tmpvar^= $tmp;
+ $tmp=$tmpvar - int($tmpvar/10)*10;
+ if ($max_id*$tmp == 0)
+ {
+ $max_id=0;
+ $sth=$dbh->prepare("select max(id) from $firsttable where marker=''");
+ $sth->execute() || die "Got error select max: $DBI::errstr\n";
+ if ((@row = $sth->fetchrow_array()) && defined($row[0]))
+ {
+ $found++;
+ $max_id=$id=$row[0];
+ }
+ $sth->finish;
+ }
+ else
+ {
+ $id= $tmpvar % ($max_id-1)+1;
+ }
+ if ($id)
+ {
+ ($count=$dbh->do("update $firsttable set marker='x' where id=$id")) || die "Got error update $firsttable: $DBI::errstr\n";
+ $rows+=$count;
+ if ($count > 0)
+ {
+ $count=$dbh->do("update $secondtable set value=$i where id=$id") || die "Got error update $firsttable: $DBI::errstr\n";
+ $rows+=$count;
+ }
+ }
+ }
+ $dbh->disconnect;
+ print "Test_2: Found $found rows, Updated $rows rows\n";
+ exit(0);
+}
+
+
+#
+# select records
+#
+
+sub test_3
+{
+ my ($dbh,$id,$tmpvar,$rows,$i,$count);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $tmpvar=222222;
+ $rows=0;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % 100000);
+ $id=$tmpvar % $opt_loop_count;
+ $count=$dbh->do("select id from $firsttable where id=$id") || die "Got error on select from $firsttable: $DBI::errstr\n";
+ $rows+=$count;
+ }
+ $dbh->disconnect;
+ print "Test_3: Found $rows rows\n";
+ exit(0);
+}
+
+
+#
+# Note that this uses row=1 and in some cases won't find any matching
+# records
+#
+
+sub test_4
+{
+ my ($dbh,$id,$tmpvar,$rows,$i,$count);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $tmpvar=333333;
+ $rows=0;
+ for ($i=0 ; $i < $opt_loop_count; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % 100000);
+ $id=$tmpvar % $opt_loop_count;
+ $count=$dbh->do("select id from $secondtable where id=$id") || die "Got error on select from $secondtable: $DBI::errstr\n";
+ $rows+=$count;
+ }
+ $dbh->disconnect;
+ print "Test_4: Found $rows rows\n";
+ exit(0);
+}
+
+
+sub test_5
+{
+ my ($dbh,$id,$tmpvar,$rows,$i,$max_id,$count,$sth);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $tmpvar=444444;
+ $rows=$max_id=0;
+ for ($i=0 ; $i < $opt_loop_count ; $i++)
+ {
+ $tmpvar^= ((($tmpvar + 63) + $i)*3 % 100000);
+ if ($max_id == 0 || ($tmpvar % 10 == 0))
+ {
+ $sth=$dbh->prepare("select max(id) from $firsttable");
+ $sth->execute() || die "Got error select max: $DBI::errstr\n";
+ if ((@row = $sth->fetchrow_array()) && defined($row[0]))
+ {
+ $max_id=$id=$row[0];
+ }
+ else
+ {
+ $id=0;
+ }
+ $sth->finish;
+ }
+ else
+ {
+ $id= $tmpvar % $max_id;
+ }
+ $count=$dbh->do("select value from $firsttable,$secondtable where $firsttable.id=$id and $secondtable.id=$firsttable.id") || die "Got error on select from $secondtable: $DBI::errstr\n";
+ $rows+=$count;
+ }
+ $dbh->disconnect;
+ print "Test_5: Found $rows rows\n";
+ exit(0);
+}
+
+
+#
+# Delete the smallest row
+#
+
+sub test_del
+{
+ my ($dbh,$min_id,$i,$sth,$rows);
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host") || die $DBI::errstr;
+ $rows=0;
+ for ($i=0 ; $i < $opt_loop_count/3; $i++)
+ {
+ $sth=$dbh->prepare("select min(id) from $firsttable");
+ $sth->execute() || die "Got error on select from $firsttable: $DBI::errstr\n";
+ if ((@row = $sth->fetchrow_array()) && defined($row[0]))
+ {
+ $min_id=$row[0];
+ }
+ $sth->finish;
+ $dbh->do("delete from $firsttable where id = $min_id") || die "Got error on DELETE from $firsttable: $DBI::errstr\n";
+ $rows++;
+ }
+ $dbh->disconnect;
+ print "Test_del: Deleted $rows rows\n";
+ exit(0);
+}
+
+
+#
+# Do a flush tables once in a while
+#
+
+sub test_flush
+{
+ my ($dbh,$sth,$found1,$last_found1,$i,@row);
+ $found1=0; $last_found1=-1;
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($i=0; $found1 != $last_found1 ; $i++)
+ {
+ $sth=$dbh->prepare("flush tables") || die "Got error on prepare: $dbh->errstr\n";
+ $sth->execute || die $dbh->errstr;
+ $sth->finish;
+
+ $sth=$dbh->prepare("select count(*) from $firsttable") || die "Got error on prepare: $dbh->errstr\n";
+ $sth->execute || die $dbh->errstr;
+ @row = $sth->fetchrow_array();
+ $last_found1=$found1;
+ $found1= $row[0];
+ $sth->finish;
+ sleep(5);
+ }
+ $dbh->disconnect; $dbh=0;
+ print "flush: Did $i repair/checks\n";
+ exit(0);
+}
diff --git a/tests/thread_test.c b/tests/thread_test.c
new file mode 100644
index 00000000..8242811c
--- /dev/null
+++ b/tests/thread_test.c
@@ -0,0 +1,248 @@
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include <my_global.h>
+
+#include <my_sys.h>
+#include <my_pthread.h>
+#include "mysql.h"
+#include <my_getopt.h>
+
+static my_bool version, verbose, tty_password= 0;
+static uint thread_count,number_of_tests=1000,number_of_threads=2;
+static pthread_cond_t COND_thread_count;
+static pthread_mutex_t LOCK_thread_count;
+
+static char *database,*host,*user,*password,*unix_socket,*query;
+uint tcp_port;
+
+#ifndef _WIN32
+void *test_thread(void *arg __attribute__((unused)))
+#else
+unsigned __stdcall test_thread(void *arg __attribute__((unused)))
+#endif
+{
+ MYSQL *mysql;
+ uint count;
+
+ mysql=mysql_init(NULL);
+ if (!mysql_real_connect(mysql,host,user,password,database,tcp_port,
+ unix_socket,0))
+ {
+ fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(mysql));
+ perror("");
+ goto end;
+ }
+ mysql.reconnect= 1;
+ if (verbose) { putchar('*'); fflush(stdout); }
+ for (count=0 ; count < number_of_tests ; count++)
+ {
+ MYSQL_RES *res;
+ if (mysql_query(mysql,query))
+ {
+ fprintf(stderr,"Query failed (%s)\n",mysql_error(mysql));
+ goto end;
+ }
+ if (!(res=mysql_store_result(mysql)))
+ {
+ fprintf(stderr,"Couldn't get result from %s\n", mysql_error(mysql));
+ goto end;
+ }
+ mysql_free_result(res);
+ if (verbose) { putchar('.'); fflush(stdout); }
+ }
+end:
+ if (verbose) { putchar('#'); fflush(stdout); }
+ mysql_close(mysql);
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ pthread_cond_signal(&COND_thread_count); /* Tell main we are ready */
+ pthread_mutex_unlock(&LOCK_thread_count);
+ pthread_exit(0);
+ return 0;
+}
+
+
+static struct my_option my_long_options[] =
+{
+ {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
+ 0, 0, 0, 0, 0},
+ {"database", 'D', "Database to use", &database, &database,
+ 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"host", 'h', "Connect to host", &host, &host, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"password", 'p',
+ "Password to use when connecting to server. If password is not given it's asked from the tty.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"user", 'u', "User for login if not current user", &user,
+ &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V', "Output version information and exit",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose", 'v', "Write some progress indicators", &verbose,
+ &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"query", 'Q', "Query to execute in each threads", &query,
+ &query, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"port", 'P', "Port number to use for connection or 0 for default to, in "
+ "order of preference, my.cnf, $MYSQL_TCP_PORT, "
+#if MYSQL_PORT_DEFAULT == 0
+ "/etc/services, "
+#endif
+ "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
+ &tcp_port,
+ &tcp_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0},
+ {"socket", 'S', "Socket file to use for connection", &unix_socket,
+ &unix_socket, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"test-count", 'c', "Run test count times (default %d)",
+ &number_of_tests, &number_of_tests, 0, GET_UINT,
+ REQUIRED_ARG, 1000, 0, 0, 0, 0, 0},
+ {"thread-count", 't', "Number of threads to start",
+ &number_of_threads, &number_of_threads, 0, GET_UINT,
+ REQUIRED_ARG, 2, 0, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+static const char *load_default_groups[]=
+{ "client", "client-server", "client-mariadb", 0 };
+
+static void usage()
+{
+ printf("Connection to a mysql server with multiple threads\n");
+ if (version)
+ return;
+ puts("This software comes with ABSOLUTELY NO WARRANTY.\n");
+ printf("Usage: %s [OPTIONS] [database]\n", my_progname);
+
+ my_print_help(my_long_options);
+ print_defaults("my",load_default_groups);
+ my_print_variables(my_long_options);
+ printf("\nExample usage:\n\n\
+%s -Q 'select * from mysql.user' -c %d -t %d\n",
+ my_progname, number_of_tests, number_of_threads);
+}
+
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ switch (optid) {
+ case 'p':
+ if (argument)
+ {
+ my_free(password);
+ password= my_strdup(argument, MYF(MY_FAE));
+ while (*argument) *argument++= 'x'; /* Destroy argument */
+ }
+ else
+ tty_password= 1;
+ break;
+ case 'V':
+ version= 1;
+ usage();
+ exit(0);
+ break;
+ case '?':
+ case 'I': /* Info */
+ usage();
+ exit(1);
+ break;
+ }
+ return 0;
+}
+
+
+static void get_options(int argc, char **argv)
+{
+ int ho_error;
+
+ load_defaults_or_exit("my", load_default_groups, &argc, &argv);
+ if ((ho_error= handle_options(&argc, &argv, my_long_options, get_one_option)))
+ exit(ho_error);
+
+ free_defaults(argv);
+ if (tty_password)
+ password=my_get_tty_password(NullS);
+ return;
+}
+
+
+int main(int argc, char **argv)
+{
+ pthread_t tid;
+ pthread_attr_t thr_attr;
+ uint i;
+ int error;
+ MY_INIT(argv[0]);
+ get_options(argc,argv);
+
+ if ((error=pthread_cond_init(&COND_thread_count,NULL)))
+ {
+ fprintf(stderr,"Got error: %d from pthread_cond_init (errno: %d)",
+ error,errno);
+ exit(1);
+ }
+ pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST);
+
+ if ((error=pthread_attr_init(&thr_attr)))
+ {
+ fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)",
+ error,errno);
+ exit(1);
+ }
+ if ((error=pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED)))
+ {
+ fprintf(stderr,
+ "Got error: %d from pthread_attr_setdetachstate (errno: %d)",
+ error,errno);
+ exit(1);
+ }
+
+ printf("Init ok. Creating %d threads\n",number_of_threads);
+ for (i=1 ; i <= number_of_threads ; i++)
+ {
+ int *param= &i;
+
+ if (verbose) { putchar('+'); fflush(stdout); }
+ pthread_mutex_lock(&LOCK_thread_count);
+ if ((error=pthread_create(&tid,&thr_attr,test_thread,(void*) param)))
+ {
+ fprintf(stderr,"\nGot error: %d from pthread_create (errno: %d) when creating thread: %i\n",
+ error,errno,i);
+ pthread_mutex_unlock(&LOCK_thread_count);
+ exit(1);
+ }
+ thread_count++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+ }
+
+ printf("Waiting for threads to finnish\n");
+ error=pthread_mutex_lock(&LOCK_thread_count);
+ while (thread_count)
+ {
+ if ((error=pthread_cond_wait(&COND_thread_count,&LOCK_thread_count)))
+ fprintf(stderr,"\nGot error: %d from pthread_cond_wait\n",error);
+ }
+ pthread_mutex_unlock(&LOCK_thread_count);
+ pthread_attr_destroy(&thr_attr);
+ printf("\nend\n");
+
+ my_end(0);
+ return 0;
+
+ exit(0);
+ return 0; /* Keep some compilers happy */
+}
+
diff --git a/tests/truncate.pl b/tests/truncate.pl
new file mode 100755
index 00000000..d79167dd
--- /dev/null
+++ b/tests/truncate.pl
@@ -0,0 +1,142 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2002 MySQL AB
+# Use is subject to license terms
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+#
+# This is a test with uses many processes to test a MySQL server.
+#
+
+$opt_loop_count=10000; # Change this to make test harder/easier
+
+##################### Standard benchmark inits ##############################
+
+use DBI;
+use Getopt::Long;
+use Benchmark;
+
+package main;
+
+$opt_skip_create=$opt_skip_in=$opt_verbose=$opt_fast_insert=
+$opt_lock_tables=$opt_debug=$opt_skip_delete=$opt_fast=$opt_force=0;
+$opt_threads=2;
+$opt_host=$opt_user=$opt_password=""; $opt_db="test";
+
+GetOptions("host=s","db=s","user=s","password=s","loop-count=i","skip-create","skip-in","skip-delete","verbose","fast-insert","lock-tables","debug","fast","force","threads=i") || die "Aborted";
+$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these
+
+print "Testing truncate from $opt_threads multiple connections $opt_loop_count times\n";
+
+@testtables = ( ["bench_f31", "type=heap"]);
+
+####
+#### Start timeing and start test
+####
+
+$start_time=new Benchmark;
+$dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+if (!$opt_skip_create)
+{
+ my $table_def;
+ foreach $table_def (@testtables)
+ {
+ my ($table,$extra)= ($table_def->[0], $table_def->[1]);
+ print "Creating table $table in database $opt_db\n";
+ $dbh->do("drop table if exists $table");
+ $dbh->do("create table $table".
+ " (id int(6) not null,".
+ " info varchar(32)," .
+ " marker timestamp," .
+ " flag int not null," .
+ " primary key(id)) $extra")
+
+ or die $DBI::errstr;
+ }
+}
+
+$dbh->disconnect; $dbh=0; # Close handler
+$|= 1; # Autoflush
+
+####
+#### Start the tests
+####
+
+for ($i=0 ; $i < $opt_threads ; $i ++)
+{
+ test_truncate() if (($pid=fork()) == 0); $work{$pid}="truncate";
+}
+
+print "Started $opt_threads threads\n";
+
+$errors=0;
+$running_insert_threads=$opt_threads;
+while (($pid=wait()) != -1)
+{
+ $ret=$?/256;
+ print "thread '" . $work{$pid} . "' finished with exit code $ret\n";
+ --$running_insert_threads;
+ $errors++ if ($ret != 0);
+}
+
+#
+# Cleanup
+#
+
+if (!$opt_skip_delete && !$errors)
+{
+ my $table_def;
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ foreach $table_def (@testtables)
+ {
+ $dbh->do("drop table " . $table_def->[0]);
+ }
+ $dbh->disconnect; $dbh=0; # Close handler
+}
+
+print ($errors ? "Test failed\n" :"Test ok\n");
+$end_time=new Benchmark;
+print "Total time: " .
+ timestr(timediff($end_time, $start_time),"noc") . "\n";
+
+exit(0);
+
+
+#
+# Insert records in the table
+#
+
+sub test_truncate
+{
+ my ($dbh,$i,$j,$count,$table_def,$table);
+
+ $dbh = DBI->connect("DBI:MariaDB:$opt_db:$opt_host",
+ $opt_user, $opt_password,
+ { PrintError => 0}) || die $DBI::errstr;
+
+ for ($count=0; $count < $opt_loop_count ; $count++)
+ {
+ my ($table)= ($testtables[0]->[0]);
+ $dbh->do("truncate table $table") || die "Got error on truncate: $DBI::errstr\n";
+ }
+ $dbh->disconnect; $dbh=0;
+ print "Test_truncate: Run $count times\n";
+ exit(0);
+}