From 3f619478f796eddbba6e39502fe941b285dd97b1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 20:00:34 +0200 Subject: Adding upstream version 1:10.11.6. Signed-off-by: Daniel Baumann --- tests/CMakeLists.txt | 44 + tests/async_queries.c | 435 + tests/auto_increment.res | 114 + tests/auto_increment.tst | 27 + tests/big_record.pl | 111 + tests/bug25714.c | 78 + tests/check_async_queries.pl | 73 + tests/code_quality/cppcheck_ignorelist.txt | 251 + tests/code_quality/flawfinder_ignorelist.json | 622 + tests/connect_test.c | 66 + tests/consistent_snapshot.pl | 107 + tests/deadlock_test.c | 239 + tests/drop_test.pl | 237 + tests/export.pl | 180 + tests/fork2_test.pl | 257 + tests/fork_big.pl | 610 + tests/fork_big2.pl | 754 + tests/function.res | 258 + tests/function.tst | 80 + tests/grant.res | 616 + tests/index_corrupt.pl | 229 + tests/insert_and_repair.pl | 197 + tests/insert_test.c | 60 + tests/list_test.c | 71 + tests/lock_test.pl | 110 + tests/lock_test.res | 25 + tests/mail_to_db.pl | 624 + tests/myisam-big-rows.tst | 72 + tests/mysql_client_fw.c | 1509 ++ tests/mysql_client_test.c | 22495 ++++++++++++++++++++++++ tests/nonblock-wrappers.h | 479 + tests/pmail.pl | 339 + tests/rename_test.pl | 221 + tests/restore-lock.smack | 63 + tests/select_test.c | 74 + tests/showdb_test.c | 67 + tests/ssl_test.c | 83 + tests/table_types.pl | 232 + tests/test_delayed_insert.pl | 381 + tests/thread_test.c | 248 + tests/truncate.pl | 142 + 41 files changed, 32880 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/async_queries.c create mode 100644 tests/auto_increment.res create mode 100644 tests/auto_increment.tst create mode 100755 tests/big_record.pl create mode 100644 tests/bug25714.c create mode 100644 tests/check_async_queries.pl create mode 100644 tests/code_quality/cppcheck_ignorelist.txt create mode 100644 tests/code_quality/flawfinder_ignorelist.json create mode 100644 tests/connect_test.c create mode 100755 tests/consistent_snapshot.pl create mode 100644 tests/deadlock_test.c create mode 100755 tests/drop_test.pl create mode 100755 tests/export.pl create mode 100755 tests/fork2_test.pl create mode 100755 tests/fork_big.pl create mode 100644 tests/fork_big2.pl create mode 100644 tests/function.res create mode 100644 tests/function.tst create mode 100644 tests/grant.res create mode 100755 tests/index_corrupt.pl create mode 100755 tests/insert_and_repair.pl create mode 100644 tests/insert_test.c create mode 100644 tests/list_test.c create mode 100755 tests/lock_test.pl create mode 100644 tests/lock_test.res create mode 100755 tests/mail_to_db.pl create mode 100644 tests/myisam-big-rows.tst create mode 100644 tests/mysql_client_fw.c create mode 100644 tests/mysql_client_test.c create mode 100644 tests/nonblock-wrappers.h create mode 100755 tests/pmail.pl create mode 100755 tests/rename_test.pl create mode 100755 tests/restore-lock.smack create mode 100644 tests/select_test.c create mode 100644 tests/showdb_test.c create mode 100644 tests/ssl_test.c create mode 100755 tests/table_types.pl create mode 100755 tests/test_delayed_insert.pl create mode 100644 tests/thread_test.c create mode 100755 tests/truncate.pl (limited to 'tests') 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 . +*/ + + +/* + 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + + +#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 +#include +#include +#include +#include + +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 \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 () { + 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 +#include +#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(<do(<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 +#include +#include +#include +#include +#include + +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),(10,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 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 +#include +#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 \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 +#endif +#include +#include +#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 \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= <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 () + { + 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 < 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + 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 +#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: /.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 *)# + 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 *)¶m; + 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(¶m, 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, ¶m); + 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() - `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, ¶mCount); + + 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, ¶mCount); + 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, ¶mCount); + 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, ¶mCount); + + 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, ¶mCount); + + 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,¶m); + 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, ¶m); + 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 . +*/ + +/* + 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 +#else +#include +#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 <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 +#endif +#include +#include +#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 \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 +#endif +#include +#include +#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 \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 +#endif +#include +#include +#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 \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 + +#include +#include +#include "mysql.h" +#include + +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); +} -- cgit v1.2.3