diff options
Diffstat (limited to 'libmariadb/unittest')
36 files changed, 31643 insertions, 0 deletions
diff --git a/libmariadb/unittest/libmariadb/CMakeLists.txt b/libmariadb/unittest/libmariadb/CMakeLists.txt new file mode 100644 index 00000000..876e5cfc --- /dev/null +++ b/libmariadb/unittest/libmariadb/CMakeLists.txt @@ -0,0 +1,67 @@ +# Copyright (C) 2008 Sun Microsystems, Inc. +# +# 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-1301 USA + +IF(SKIP_TESTS) + RETURN() +ENDIF() + +ENABLE_TESTING() + + +INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include + ${CC_BINARY_DIR}/include + ${CC_SOURCE_DIR}/unittest/mytap + ${CC_SOURCE_DIR}/unittest/libmariadb) +ADD_DEFINITIONS(-DLIBMARIADB) + +SET(API_TESTS "conc336" "bulk1" "performance" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs" "sp" "result" "connection" "misc" "ps_new" "thread" "features-10_2") +IF(WITH_DYNCOL) + SET(API_TESTS ${API_TESTS} "dyncol") +ENDIF() + +SET(API_TESTS ${API_TESTS} "async") + +#exclude following tests from ctests, since we need to run them manually with different credentials +SET(MANUAL_TESTS "t_conc173" "rpl_api") +# Get finger print from server certificate +IF(WITH_SSL) + IF(CERT_PATH AND NOT DEFINED ENV{TRAVIS}) + SET(API_TESTS ${API_TESTS} "ssl") + IF(WIN32) + STRING(REPLACE "\\" "\\\\" CERT_PATH ${CERT_PATH}) + ENDIF() + ADD_DEFINITIONS(-DCERT_PATH="${CERT_PATH}") + ENDIF() +ENDIF() + +ADD_LIBRARY(ma_getopt ma_getopt.c) + +FOREACH(API_TEST ${API_TESTS}) + IF (NOT TARGET ${API_TEST}) + ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c) + ENDIF() + TARGET_LINK_LIBRARIES(${API_TEST} cctap ma_getopt mariadbclient) + ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST}) + IF(${API_TEST} STREQUAL "cursor" OR ${API_TEST} STREQUAL "ps_new") + SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 360) + ELSE() + SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 180) + ENDIF() +ENDFOREACH(API_TEST) + +FOREACH(API_TEST ${MANUAL_TESTS}) + ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c) + TARGET_LINK_LIBRARIES(${API_TEST} cctap ma_getopt mariadbclient) +ENDFOREACH() diff --git a/libmariadb/unittest/libmariadb/async.c b/libmariadb/unittest/libmariadb/async.c new file mode 100644 index 00000000..bc6beaff --- /dev/null +++ b/libmariadb/unittest/libmariadb/async.c @@ -0,0 +1,321 @@ +/* + Copyright 2011 Kristian Nielsen and Monty Program Ab. + + This file is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "my_test.h" +#include "ma_common.h" + + +#ifndef _WIN32 +#include <poll.h> +#else +#include <winsock2.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <mysql.h> + +my_bool skip_async= 0; + +static int test_async(MYSQL *mysql) +{ + int type; + mariadb_get_info(mysql, MARIADB_CONNECTION_PVIO_TYPE, &type); + if (type > MARIADB_CONNECTION_TCP) + { + skip_async= 1; + diag("Async IO not supported"); + } + return OK; +} + +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) + { + /* + In a real event framework, we should handle errors and re-try the select. + */ + 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= -1; + + 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= mysql_get_timeout_value_ms(mysql); + } + else + timeout= -1; + do { + res= poll(&pfd, 1, timeout); + } while (res == -1 && errno == EINTR); + if (res == 0) + return MYSQL_WAIT_TIMEOUT; + else if (res < 0) + { + /* + In a real event framework, we should handle EINTR and re-try the poll. + */ + 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 +} + +static int async1(MYSQL *unused __attribute__((unused))) +{ + int err= 0, rc; + MYSQL mysql, *ret; + MYSQL_RES *res; + MYSQL_ROW row; + int status; + uint default_timeout; + int i; + + if (skip_async) + return SKIP; + + for (i=0; i < 100; i++) + { + + mysql_init(&mysql); + rc= mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0); + check_mysql_rc(rc, (MYSQL *)&mysql); + + /* set timeouts to 300 microseconds */ + default_timeout= 3; + mysql_options(&mysql, MYSQL_OPT_READ_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_OPT_WRITE_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp"); + if (force_tls) + mysql_ssl_set(&mysql, NULL, NULL, NULL, NULL,NULL); + + /* Returns 0 when done, else flag for what to wait for when need to block. */ + status= mysql_real_connect_start(&ret, &mysql, hostname, username, password, schema, port, socketname, 0); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_real_connect_cont(&ret, &mysql, status); + } + if (!ret) + { + diag("Error: %s", mysql_error(&mysql)); + FAIL_IF(!ret, "Failed to mysql_real_connect()"); + } + + if (force_tls && !mysql_get_ssl_cipher(&mysql)) + { + diag("Error: No tls connection"); + return FAIL; + } + + status= mysql_real_query_start(&err, &mysql, SL("SHOW STATUS")); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_real_query_cont(&err, &mysql, status); + } + FAIL_IF(err, "mysql_real_query() returns error"); + + /* This method cannot block. */ + res= mysql_use_result(&mysql); + FAIL_IF(!res, "mysql_use_result() returns error"); + + for (;;) + { + status= mysql_fetch_row_start(&row, res); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_fetch_row_cont(&row, res, status); + } + if (!row) + break; + } + FAIL_IF(mysql_errno(&mysql), "Got error while retrieving rows"); + mysql_free_result(res); + + /* + mysql_close() sends a COM_QUIT packet, and so in principle could block + waiting for the socket to accept the data. + In practise, for many applications it will probably be fine to use the + blocking mysql_close(). + */ + status= mysql_close_start(&mysql); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_close_cont(&mysql, status); + } + } + return OK; +} + +static int test_conc131(MYSQL *unused __attribute__((unused))) +{ + int rc; + /* this test needs to run under valgrind */ + MYSQL *mysql; + + if (skip_async) + return SKIP; + + mysql= mysql_init(NULL); + rc= mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0); + check_mysql_rc(rc, mysql); + mysql_close(mysql); + return OK; +} + +static int test_conc129(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (skip_async) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(mysql_close_start(mysql), "No error expected"); + return OK; +} + +static int test_conc622(MYSQL *my __attribute__((unused))) +{ + int rc; + MYSQL mysql, *ret; + int status; + uint default_timeout; + int i; + const char *invalid_host= "1.0.0.0"; + + if (skip_async) + return SKIP; + + for (i=0; i < 5; i++) + { + mysql_init(&mysql); + rc= mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0); + check_mysql_rc(rc, (MYSQL *)&mysql); + + /* set timeouts to 300 microseconds */ + default_timeout= 3; + mysql_options(&mysql, MYSQL_OPT_READ_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_OPT_WRITE_TIMEOUT, &default_timeout); + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp"); + + /* Returns 0 when done, else flag for what to wait for when need to block. */ + status= mysql_real_connect_start(&ret, &mysql, invalid_host, username, password, schema, port, socketname, 0); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_real_connect_cont(&ret, &mysql, status); + } + if (!ret) + { + status= mysql_close_start(&mysql); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_close_cont(&mysql, status); + } + } else { + diag("Expected error when connecting to host '%s'", invalid_host); + diag("Connected to %s", mysql_get_server_info(&mysql)); + return FAIL; + } + } + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_async", test_async, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"async1", async1, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc131", test_conc131, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc129", test_conc129, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc622", test_conc622, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/basic-t.c b/libmariadb/unittest/libmariadb/basic-t.c new file mode 100644 index 00000000..40a44962 --- /dev/null +++ b/libmariadb/unittest/libmariadb/basic-t.c @@ -0,0 +1,885 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ + + +/** + Some basic tests of the client API. +*/ + +#include "my_test.h" +#include "ma_common.h" + +static int test_conc75(MYSQL *my) +{ + int rc; + MYSQL *mysql; + int i; + my_bool reconnect= 1; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + SKIP_XPAND; + + mysql= mysql_init(NULL); + + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS a"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE a (a varchar(200))"); + check_mysql_rc(rc, mysql); + + rc= mysql_set_character_set(mysql, "utf8"); + check_mysql_rc(rc, mysql); + + for (i=0; i < 10; i++) + { + ulong thread_id= mysql_thread_id(mysql); + /* force reconnect */ + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + diag("killing connection"); + mysql_kill(my, thread_id); + mysql_ping(mysql); + rc= mysql_query(mysql, "load data local infile './nonexistingfile.csv' into table a (`a`)"); + FAIL_IF(!test(mysql->options.client_flag | CLIENT_LOCAL_FILES), "client_flags not correct"); + diag("thread1: %ld %ld", thread_id, mysql_thread_id(mysql)); + FAIL_IF(thread_id == mysql_thread_id(mysql), "new thread id expected"); + //diag("cs: %s", mysql->charset->csname); + //FAIL_IF(strcmp(mysql->charset->csname, "utf8"), "wrong character set"); + } + rc= mysql_query(mysql, "DROP TABLE IF EXISTS a"); + check_mysql_rc(rc, mysql); + mysql_close(mysql); + return OK; +} + + +static int test_conc74(MYSQL *unused __attribute__((unused))) +{ + int rc; + MYSQL *mysql; + + mysql= mysql_init(NULL); + + + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0| CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS a"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE a (a varchar(200))"); + check_mysql_rc(rc, mysql); + + mysql->options.client_flag&= ~CLIENT_LOCAL_FILES; + + rc= mysql_query(mysql, "load data local infile './nonexistingfile.csv' into table a (`a`)"); + FAIL_IF(!rc, "Error expected"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS a"); + check_mysql_rc(rc, mysql); + + mysql_close(mysql); + return OK; +} + + +static int test_conc71(MYSQL *my) +{ + int rc; + MYSQL *mysql; + + /* uncomment if you want to test manually */ + return SKIP; + + mysql= mysql_init(NULL); + + + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8"); + mysql_options(mysql, MYSQL_OPT_COMPRESS, 0); + mysql_options(mysql, MYSQL_INIT_COMMAND, "/*!40101 SET SQL_MODE='' */"); + mysql_options(mysql, MYSQL_INIT_COMMAND, "/*!40101 set @@session.wait_timeout=28800 */"); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + diag("kill server"); + + rc= mysql_query(mysql, "SELECT 'foo' FROM DUAL"); + check_mysql_rc(rc, mysql); + + mysql_close(mysql); + return OK; +} + +static int test_conc70(MYSQL *my) +{ + int rc; + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL *mysql; + + SKIP_CONNECTION_HANDLER; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + mysql= mysql_init(NULL); + + rc= mysql_query(my, "SET @a:=@@max_allowed_packet"); + check_mysql_rc(rc, my); + + mysql_query(my, "SET global max_allowed_packet=1024*1024*22"); + check_mysql_rc(rc, my); + + mysql_options(mysql, MYSQL_OPT_COMPRESS, (void *)1); + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a LONGBLOB) engine=MyISAM"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (REPEAT('A', 1024 * 1024 * 20))"); + check_mysql_rc(rc, mysql); + + if (mysql_warning_count(mysql)) + { + diag("server doesn't accept package size"); + return SKIP; + } + + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + if (!(res= mysql_store_result(mysql))) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + + row= mysql_fetch_row(res); + diag("Length: %ld", (long)strlen(row[0])); + FAIL_IF(strlen(row[0]) != 1024 * 1024 * 20, "Wrong length"); + + mysql_free_result(res); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + mysql_close(mysql); + + rc= mysql_query(my, "SET global max_allowed_packet=@a"); + check_mysql_rc(rc, my); + + return OK; +} + +static int test_conc68(MYSQL *my) +{ + int rc; + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL *mysql; + + SKIP_CONNECTION_HANDLER; + SKIP_SKYSQL; + SKIP_MAXSCALE; + + mysql= mysql_init(NULL); + + rc= mysql_query(my, "SET @a:=@@max_allowed_packet"); + check_mysql_rc(rc, my); + + mysql_query(my, "SET global max_allowed_packet=1024*1024*22"); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a LONGBLOB) ENGINE=MyISAM"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (REPEAT('A', 1024 * 1024 * 20))"); + check_mysql_rc(rc, mysql); + if (mysql_warning_count(mysql)) + { + diag("server doesn't accept package size"); + return SKIP; + } + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + if (!(res= mysql_store_result(mysql))) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + + row= mysql_fetch_row(res); + diag("Length: %ld", (long)strlen(row[0])); + FAIL_IF(strlen(row[0]) != 1024 * 1024 * 20, "Wrong length"); + + mysql_free_result(res); + mysql_close(mysql); + + rc= mysql_query(my, "SET global max_allowed_packet=@a"); + check_mysql_rc(rc, my); + + return OK; +} + + +static int basic_connect(MYSQL *unused __attribute__((unused))) +{ + MYSQL_ROW row; + MYSQL_RES *res; + MYSQL_FIELD *field; + int rc; + MYSQL *my; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + FAIL_IF(!my_test_connect(my, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + rc= mysql_query(my, "SELECT @@version"); + check_mysql_rc(rc, my); + + res= mysql_store_result(my); + FAIL_IF(!res, mysql_error(my)); + field= mysql_fetch_fields(res); + FAIL_IF(!field, "couldn't fetch field"); + while ((row= mysql_fetch_row(res)) != NULL) + { + FAIL_IF(mysql_num_fields(res) != 1, "Got the wrong number of fields"); + } + FAIL_IF(mysql_errno(my), mysql_error(my)); + + mysql_free_result(res); + mysql_close(my); + + return OK; +} + + +static int use_utf8(MYSQL *my) +{ + MYSQL_ROW row; + MYSQL_RES *res; + int rc; + + /* Make sure that we actually ended up with utf8. */ + rc= mysql_query(my, "SELECT @@character_set_connection"); + check_mysql_rc(rc, my); + + res= mysql_store_result(my); + FAIL_IF(!res, mysql_error(my)); + + while ((row= mysql_fetch_row(res)) != NULL) + { + FAIL_IF(strncmp(row[0], "utf8", 4), "wrong character set"); + } + FAIL_IF(mysql_errno(my), mysql_error(my)); + mysql_free_result(res); + + return OK; +} + +int client_query(MYSQL *mysql) { + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1(" + "id int primary key auto_increment, " + "name varchar(20))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1(id int, name varchar(20))"); + FAIL_IF(!rc, "Error expected"); + rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('mysql')"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('monty')"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('venu')"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('deleted')"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1(name) VALUES('deleted')"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "UPDATE t1 SET name= 'updated' " + "WHERE name= 'deleted'"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "UPDATE t1 SET id= 3 WHERE name= 'updated'"); + FAIL_IF(!rc, "Error expected"); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug12001(MYSQL *mysql) +{ + 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; + + + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + do + { + if (mysql_field_count(mysql) && + (result= mysql_use_result(mysql))) + { + mysql_free_result(result); + } + } + while (!(res= mysql_next_result(mysql))); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_table"); + check_mysql_rc(rc, mysql); + + FAIL_UNLESS(res==1, "res != 1"); + + return OK; +} + + +/* connection options */ +struct my_option_st opt_utf8[] = { + {MYSQL_SET_CHARSET_NAME, (char *)"utf8"}, + {0, NULL} +}; + +static int test_bad_union(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *query= "SELECT 1, 2 union SELECT 1"; + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_UNLESS(rc && mysql_errno(mysql) == 1222, "Error expected"); + + mysql_stmt_close(stmt); + return OK; +} + +/* + Test that mysql_insert_id() behaves as documented in our manual +*/ +static int test_mysql_insert_id(MYSQL *mysql) +{ + unsigned long long res; + int rc; + + if (mysql_get_server_version(mysql) < 50100) { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "drop table if exists t2"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "drop table if exists t3"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "drop table if exists t4"); + check_mysql_rc(rc, mysql); + /* table without auto_increment column */ + rc= mysql_query(mysql, "create table t1 (f1 int, f2 varchar(255), key(f1))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t2 (f1 int not null primary key auto_increment, f2 varchar(255))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t3 (f1 int not null primary key auto_increment, f2 varchar(255)) engine=MyISAM"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t4 (f1 int not null primary key " + "auto_increment, f2 varchar(200), unique (f2)) engine=MyISAM"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "START TRANSACTION"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values (1,'a')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "insert into t1 values (null,'b')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "insert into t1 select 5,'c'"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + + /* + Test for bug #34889: mysql_client_test::test_mysql_insert_id test fails + sporadically + */ + rc= mysql_query(mysql, "insert into t2 values (null,'b')"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 select 5,'c'"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "insert into t1 select null,'d'"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "insert into t1 values (null,last_insert_id(300))"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 300, ""); + rc= mysql_query(mysql, "insert into t1 select null,last_insert_id(400)"); + check_mysql_rc(rc, mysql); + 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. + */ + FAIL_UNLESS(res == 400, ""); + + /* table with auto_increment column */ + rc= mysql_query(mysql, "insert into t3 values (1,'a')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_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')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + + rc= mysql_query(mysql, "insert into t3 values (null,'b')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 2, ""); + rc= mysql_query(mysql, "insert into t3 select 5,'c'"); + check_mysql_rc(rc, mysql); + 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. + */ + FAIL_UNLESS(res == 5, ""); + rc= mysql_query(mysql, "insert into t3 select null,'d'"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 6, ""); + /* with more than one row */ + rc= mysql_query(mysql, "insert into t3 values (10,'a'),(11,'b')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 11, ""); + rc= mysql_query(mysql, "insert into t3 select 12,'a' union select 13,'b'"); + check_mysql_rc(rc, mysql); + 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. + */ + FAIL_UNLESS(res == 13, ""); + rc= mysql_query(mysql, "insert into t3 values (null,'a'),(null,'b')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 14, ""); + rc= mysql_query(mysql, "insert into t3 select null,'a' union select null,'b'"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 16, ""); + rc= mysql_query(mysql, "insert into t3 select 12,'a' union select 13,'b'"); + FAIL_IF(!rc, "Error expected"); + rc= mysql_query(mysql, "insert ignore into t3 select 12,'a' union select 13,'b'"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "insert into t3 values (12,'a'),(13,'b')"); + FAIL_IF(!rc, "Error expected"); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "insert ignore into t3 values (12,'a'),(13,'b')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + /* mixing autogenerated and explicit values */ + rc= mysql_query(mysql, "insert into t3 values (null,'e'),(12,'a'),(13,'b')"); + FAIL_IF(!rc, "Error expected"); + rc= mysql_query(mysql, "insert into t3 values (null,'e'),(12,'a'),(13,'b'),(25,'g')"); + FAIL_IF(!rc, "Error expected"); + rc= mysql_query(mysql, "insert into t3 values (null,last_insert_id(300))"); + check_mysql_rc(rc, mysql); + 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(). + */ + diag("res: %lld", res); + FAIL_UNLESS(res == 20, ""); + /* If first autogenerated number fails and 2nd works: */ + rc= mysql_query(mysql, "insert into t4 values (null,'e')"); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 1, ""); + rc= mysql_query(mysql, "insert ignore into t4 values (null,'e'),(null,'a'),(null,'e')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 2, ""); + /* If autogenerated fails and explicit works: */ + rc= mysql_query(mysql, "insert ignore into t4 values (null,'e'),(12,'c'),(null,'d')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + /* + Behaviour change: old code returned 3 (first autogenerated, even if it + fails); we now return first successful autogenerated. + */ + FAIL_UNLESS(res == 13, ""); + /* UPDATE may update mysql_insert_id() if it uses LAST_INSERT_ID(#) */ + rc= mysql_query(mysql, "update t4 set f1=14 where f1=12"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "update t4 set f1=0 where f1=14"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + rc= mysql_query(mysql, "update t4 set f2=last_insert_id(372) where f1=0"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 372, ""); + /* check that LAST_INSERT_ID() does not update mysql_insert_id(): */ + rc= mysql_query(mysql, "insert into t4 values (null,'g')"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 15, ""); + rc= mysql_query(mysql, "update t4 set f2=(@li:=last_insert_id()) where f1=15"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_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 t4 values (null,@li) on duplicate key " + "update f2=concat('we updated ',f2)"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 15, ""); + + rc= mysql_query(mysql, "drop table t1,t2,t3,t4"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test simple select to debug */ + +static int test_select_direct(MYSQL *mysql) +{ + int rc; + MYSQL_RES *result; + + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_select(id int, id1 tinyint, " + " id2 float, " + " id3 double, " + " name varchar(50))"); + check_mysql_rc(rc, mysql); + + /* insert a row and commit the transaction */ + rc= mysql_query(mysql, "INSERT INTO test_select VALUES(10, 5, 2.3, 4.5, 'venu')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT * FROM test_select"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + Ensure we execute the status code while testing +*/ + +static int test_status(MYSQL *mysql) +{ + mysql_stat(mysql); + check_mysql_rc(mysql_errno(mysql), mysql); + return OK; +} + +static int bug_conc1(MYSQL *mysql) +{ + my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0); + diag("errno: %d", mysql_errno(mysql)); + FAIL_IF(mysql_errno(mysql) != CR_ALREADY_CONNECTED, + "Expected errno=CR_ALREADY_CONNECTED"); + return OK; +} + +static int test_options_initcmd(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + MYSQL_RES *res; + int rc; + + mysql_options(mysql, MYSQL_INIT_COMMAND, "DROP TABLE IF EXISTS t1; CREATE TABLE t1 (a int)"); + mysql_options(mysql, MYSQL_INIT_COMMAND, "INSERT INTO t1 VALUES (1),(2),(3)"); + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, + CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS), mysql_error(mysql)); + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(mysql_num_rows(res) != 3, "Expected 3 rows"); + + mysql_free_result(res); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + mysql_close(mysql); + return OK; +} + +static int test_extended_init_values(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + + mysql_options(mysql, MYSQL_DEFAULT_AUTH, "unknown"); + FAIL_IF(strcmp(mysql->options.extension->default_auth, "unknown"), "option not set"); + + mysql_options(mysql, MYSQL_PLUGIN_DIR, "/tmp/foo"); + FAIL_IF(strcmp(mysql->options.extension->plugin_dir, "/tmp/foo"), "option not set"); + + mysql_close(mysql); + return OK; +} + +static int test_reconnect_maxpackage(MYSQL *unused __attribute__((unused))) +{ + int rc; + ulong max_packet= 0; + MYSQL *mysql; + MYSQL_RES *res; + MYSQL_ROW row; + char *query; + my_bool reconnect= 1; + + SKIP_CONNECTION_HANDLER; + + mysql= mysql_init(NULL); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, + CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS), mysql_error(mysql)); + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + + rc= mysql_query(mysql, "SELECT @@max_allowed_packet"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + max_packet= atol(row[0]); + diag("max_allowed_packet=%lu", max_packet); + mysql_free_result(res); + + query= (char *)malloc(max_packet + 30); + memset(query, 0, max_packet + 30); + + strcpy(query, "SELECT '"); + memset(query + 8, 'A', max_packet); + strcat(query, "' FROM DUAL"); + + rc= mysql_query(mysql, query); + free(query); + if (!rc) + { + diag("expected error"); + mysql_close(mysql); + return FAIL; + } + else + diag("Error (expected): %s", mysql_error(mysql)); + + rc= mysql_ping(mysql); + /* if the server is under load, poll might not report closed + socket since FIN packet came too late */ + if (rc) + rc= mysql_ping(mysql); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "SELECT @@max_allowed_packet"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + max_packet= atol(row[0]); + diag("max_allowed_packet=%lu", max_packet); + mysql_free_result(res); + + + mysql_close(mysql); + return OK; +} + +static int test_compressed(MYSQL *unused __attribute__((unused))) +{ + int rc; + MYSQL *mysql= mysql_init(NULL); + MYSQL_RES *res; + my_bool reconnect= 1; + + mysql_options(mysql, MYSQL_OPT_COMPRESS, (void *)1); + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, + CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS), mysql_error(mysql)); + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + + rc= mysql_query(mysql, "SHOW VARIABLES"); + check_mysql_rc(rc, mysql); + + if ((res= mysql_store_result(mysql))) + mysql_free_result(res); + + mysql_close(mysql); + + return OK; +} + +static int test_conc624(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + char errmsg[MYSQL_ERRMSG_SIZE]; + + stmt_set_error(stmt, 9000, SQLSTATE_UNKNOWN, 0); + snprintf(errmsg, MYSQL_ERRMSG_SIZE, ER_UNKNOWN_ERROR_CODE, 9000); + diag("stmt_error: %s", mysql_stmt_error(stmt)); + FAIL_IF(strcmp(mysql_stmt_error(stmt), errmsg), "expected undefined error 9000"); + + stmt_set_error(stmt, 0, SQLSTATE_UNKNOWN, 0); + snprintf(errmsg, MYSQL_ERRMSG_SIZE, ER_UNKNOWN_ERROR_CODE, 0); + FAIL_IF(strcmp(mysql_stmt_error(stmt), errmsg), "expected undefined error 0"); + + stmt_set_error(stmt, 4999, SQLSTATE_UNKNOWN, 0); + snprintf(errmsg, MYSQL_ERRMSG_SIZE, ER_UNKNOWN_ERROR_CODE, 4999); + FAIL_IF(strcmp(mysql_stmt_error(stmt), errmsg), "expected undefined error 4999"); + + my_set_error(mysql, 4999, SQLSTATE_UNKNOWN, 0); + snprintf(errmsg, MYSQL_ERRMSG_SIZE, ER_UNKNOWN_ERROR_CODE, 4999); + FAIL_IF(strcmp(mysql_error(mysql), errmsg), "expected undefined error 4999"); + + my_set_error(mysql, 0, SQLSTATE_UNKNOWN, 0); + snprintf(errmsg, MYSQL_ERRMSG_SIZE, ER_UNKNOWN_ERROR_CODE, 0); + FAIL_IF(strcmp(mysql_error(mysql), errmsg), "expected undefined error 0"); + + my_set_error(mysql, 9000, SQLSTATE_UNKNOWN, 0); + snprintf(errmsg, MYSQL_ERRMSG_SIZE, ER_UNKNOWN_ERROR_CODE, 9000); + FAIL_IF(strcmp(mysql_error(mysql), errmsg), "expected undefined error 9000"); + + /* test if stmt_set_error works with variadic arguments */ + stmt_set_error(stmt, CR_STMT_CLOSED, SQLSTATE_UNKNOWN, 0, "foobar"); + snprintf(errmsg, MYSQL_ERRMSG_SIZE, ER(CR_STMT_CLOSED), "foobar"); + FAIL_IF(strcmp(mysql_stmt_error(stmt), errmsg), "error when passing variadic arguments to prepared stmt error function"); + + mysql_stmt_close(stmt); + + return OK; +} + +struct my_tests_st my_tests[] = { + {"test_conc624", test_conc624, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc75", test_conc75, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc74", test_conc74, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc71", test_conc71, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc70", test_conc70, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc68", test_conc68, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_compressed", test_compressed, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_reconnect_maxpackage", test_reconnect_maxpackage, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"basic_connect", basic_connect, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"use_utf8", use_utf8, TEST_CONNECTION_NEW, 0, opt_utf8, NULL}, + {"client_query", client_query, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bad_union", test_bad_union, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_select_direct", test_select_direct, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_mysql_insert_id", test_mysql_insert_id, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug12001", test_bug12001, TEST_CONNECTION_NEW, CLIENT_MULTI_STATEMENTS, NULL, NULL}, + {"test_status", test_status, TEST_CONNECTION_NEW, CLIENT_MULTI_STATEMENTS, NULL, NULL}, + {"bug_conc1", bug_conc1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_options_initcmd", test_options_initcmd, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_extended_init_values", test_extended_init_values, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + diag("user: %s", username); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/bulk1.c b/libmariadb/unittest/libmariadb/bulk1.c new file mode 100644 index 00000000..e1c31eaa --- /dev/null +++ b/libmariadb/unittest/libmariadb/bulk1.c @@ -0,0 +1,1099 @@ +/* + Copyright 2011 Kristian Nielsen and Monty Program Ab. + + This file is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "my_test.h" +#include "ma_common.h" + +#define TEST_ARRAY_SIZE 1024 + +static my_bool bulk_enabled= 0; + +char *rand_str(size_t length) { + const char charset[] = "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char *dest= (char *)malloc(length+1); + char *p= dest; + while (length-- > 0) { + *dest++ = charset[rand() % sizeof(charset)]; + } + *dest = '\0'; + return p; +} + +static int check_bulk(MYSQL *mysql) +{ + bulk_enabled= (!(mysql->server_capabilities & CLIENT_MYSQL) && + (mysql->extension->mariadb_server_capabilities & + (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32))); + diag("bulk %ssupported", bulk_enabled ? "" : "not "); + return OK; +} + +static int bulk1(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + const char *stmt_str= "INSERT INTO bulk1 VALUES (?,?)"; + unsigned int array_size= TEST_ARRAY_SIZE; + int rc; + unsigned int i; + char **buffer; + unsigned long *lengths; + unsigned int *vals; + MYSQL_BIND bind[2]; + MYSQL_RES *res; + MYSQL_ROW row; + unsigned int intval; + + if (!bulk_enabled) + return SKIP; + + rc= mysql_select_db(mysql, "testc"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE bulk1 (a int , b VARCHAR(255))"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL(stmt_str)); + check_stmt_rc(rc, stmt); + + /* allocate memory */ + buffer= calloc(TEST_ARRAY_SIZE, sizeof(char *)); + lengths= (unsigned long *)calloc(sizeof(long), TEST_ARRAY_SIZE); + vals= (unsigned int *)calloc(sizeof(int), TEST_ARRAY_SIZE); + + for (i=0; i < TEST_ARRAY_SIZE; i++) + { + buffer[i]= rand_str(254); + lengths[i]= -1; + vals[i]= i; + } + + memset(bind, 0, sizeof(MYSQL_BIND) * 2); + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= vals; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= (void *)buffer; + bind[1].length= (unsigned long *)lengths; + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + for (i=0; i < 100; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(mysql_stmt_affected_rows(stmt) != TEST_ARRAY_SIZE, "affected_rows != TEST_ARRAY_SIZE"); + } + + for (i=0; i < array_size; i++) + free(buffer[i]); + + free(buffer); + free(lengths); + free(vals); + + rc= mysql_stmt_close(stmt); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT COUNT(*) FROM bulk1"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + intval= atoi(row[0]); + mysql_free_result(res); + FAIL_IF(intval != array_size * 100, "Expected 102400 rows"); + + rc= mysql_query(mysql, "SELECT MAX(a) FROM bulk1"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + intval= atoi(row[0]); + mysql_free_result(res); + FAIL_IF(intval != array_size - 1, "Expected max value 1024"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int bulk2(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + MYSQL_BIND bind[2]; + unsigned int i; + unsigned int array_size=1024; + char indicator[1024]; + long lval[1024]; + + if (!bulk_enabled) + return SKIP; + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE bulk2 (a int default 4, b int default 2)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO bulk2 VALUES (?,1)")); + check_stmt_rc(rc, stmt); + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + + for (i=0; i < array_size; i++) + { + indicator[i]= STMT_INDICATOR_DEFAULT; + lval[i]= i; + } + + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].u.indicator= indicator; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= &lval; + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk2"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int bulk3(MYSQL *mysql) +{ + struct st_bulk3 { + char char_value[20]; + unsigned long length; + int int_value; + }; + + struct st_bulk3 val[3]= {{"Row 1", 5, 1}, + {"Row 02", 6, 2}, + {"Row 003", 7, 3}}; + int rc; + MYSQL_BIND bind[2]; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + size_t row_size= sizeof(struct st_bulk3); + int array_size= 3; + + if (!bulk_enabled) + return SKIP; + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk3"); + check_mysql_rc(rc,mysql); + rc= mysql_query(mysql, "CREATE TABLE bulk3 (name varchar(20), row int)"); + check_mysql_rc(rc,mysql); + + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO bulk3 VALUES (?,?)")); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)*2); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ROW_SIZE, &row_size); + check_stmt_rc(rc, stmt); + + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer= &val[0].char_value; + bind[0].length= &val[0].length; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= &val[0].int_value; + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk3"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int bulk4(MYSQL *mysql) +{ + struct st_bulk4 { + char char_value[20]; + char indicator1; + int int_value; + char indicator2; + }; + + struct st_bulk4 val[]= {{"Row 1", STMT_INDICATOR_NTS, 0, STMT_INDICATOR_DEFAULT}, + {"Row 2", STMT_INDICATOR_NTS, 0, STMT_INDICATOR_DEFAULT}, + {"Row 3", STMT_INDICATOR_NTS, 0, STMT_INDICATOR_DEFAULT}}; + int rc; + MYSQL_BIND bind[2]; + MYSQL_RES *res; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + size_t row_size= sizeof(struct st_bulk4); + int array_size= 3; + unsigned long lengths[3]= {-1, -1, -1}; + + if (!bulk_enabled) + return SKIP; + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk4"); + check_mysql_rc(rc,mysql); + rc= mysql_query(mysql, "CREATE TABLE bulk4 (name varchar(20), row int not null default 3)"); + check_mysql_rc(rc,mysql); + + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO bulk4 VALUES (?,?)")); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)*2); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ROW_SIZE, &row_size); + check_stmt_rc(rc, stmt); + + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].u.indicator= &val[0].indicator1; + bind[0].buffer= &val[0].char_value; + bind[0].length= lengths; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].u.indicator= &val[0].indicator2; + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT * FROM bulk4 WHERE row=3"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + rc= (int)mysql_num_rows(res); + mysql_free_result(res); + FAIL_IF(rc != 3, "expected 3 rows"); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk4"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int bulk_null(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + MYSQL_BIND bind[2]; + unsigned int param_count= 2; + unsigned int array_size= 2; + unsigned long lengths[2]= {-1, -1}; + char **buf= calloc(1, 2 * sizeof(char *)); + + if (!bulk_enabled) + { + free(buf); + return SKIP; + } + + buf[0]= strdup("foo"); + buf[1]= strdup("foobar"); + + rc= mariadb_stmt_execute_direct(stmt, "DROP TABLE IF EXISTS bulk_null", -1); + check_stmt_rc(rc, stmt); + + rc= mariadb_stmt_execute_direct(stmt, "CREATE TABLE bulk_null (a int not null auto_increment primary key, b varchar(20))", -1); + check_stmt_rc(rc, stmt); + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_NULL; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= buf; + bind[1].length= lengths; + + mysql_stmt_close(stmt); + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶m_count); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mariadb_stmt_execute_direct(stmt, "INSERT INTO bulk_null VALUES (?, ?)", -1); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + free(buf[0]); + free(buf[1]); + free(buf); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk_null"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int bulk5(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_BIND bind[3]; + MYSQL_RES *res; + unsigned long rows; + unsigned int array_size= 5; + int rc; + int intval[]= {12,13,14,15,16}; + int id[]= {1,2,3,4,5}; + + if (!bulk_enabled) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk5"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE bulk5 (a int, b int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO bulk5 VALUES (1,1), (2,2), (3,3), (4,4), (5,5)"); + check_mysql_rc(rc, mysql); + + + memset(bind, 0, sizeof(MYSQL_BIND) * 3); + + rc= mysql_stmt_prepare(stmt, SL("UPDATE bulk5 SET a=? WHERE a=?")); + check_stmt_rc(rc, stmt); + + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= &intval; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= &id; + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT * FROM bulk5 WHERE a=b+11"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + rows= (unsigned long)mysql_num_rows(res); + diag("rows: %lu", rows); + mysql_free_result(res); + + FAIL_IF(rows != 5, "expected 5 rows"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk5"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int bulk6(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_BIND bind[3]; + MYSQL_RES *res; + unsigned long rows; + unsigned int array_size= 5; + int rc; + int intval[]= {12,13,14,15,16}; + int id[]= {1,2,3,4,5}; + char indicator[5]; + + if (!bulk_enabled) + return SKIP; + memset(indicator, STMT_INDICATOR_IGNORE, 5); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk6"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE bulk6 (a int, b int default 4)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO bulk6 VALUES (1,1), (2,2), (3,3), (4,4), (5,5)"); + check_mysql_rc(rc, mysql); + + + memset(bind, 0, sizeof(MYSQL_BIND) * 3); + + /* 1st case: UPDATE */ + rc= mysql_stmt_prepare(stmt, SL("UPDATE bulk6 SET a=?, b=? WHERE a=?")); + check_stmt_rc(rc, stmt); + + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= &intval; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= &intval; + bind[1].u.indicator= indicator; + bind[2].buffer_type= MYSQL_TYPE_LONG; + bind[2].buffer= &id; + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT * FROM bulk6 WHERE a=b+11"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + rows= (unsigned long)mysql_num_rows(res); + mysql_free_result(res); + + FAIL_IF(rows != 5, "expected 5 rows"); + + /* 2nd case: INSERT - ignore indicator should be same as default */ + rc= mysql_query(mysql, "DELETE FROM bulk6"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO bulk6 VALUES (?,?)")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + /* this should insert 5 default values (=4) */ + memset(indicator, STMT_INDICATOR_DEFAULT, 5); + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + /* this should insert 5 default values (=4) */ + memset(indicator, STMT_INDICATOR_IGNORE, 5); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT * FROM bulk6 WHERE b=4"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + rows= (unsigned long)mysql_num_rows(res); + mysql_free_result(res); + + FAIL_IF(rows != 10, "expected 10 rows"); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk6"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc243(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[3]; + MYSQL_RES *result; + MYSQL_ROW row; + + struct st_data { + unsigned long id; + char id_ind; + char forename[30]; + char forename_ind; + char surname[30]; + char surname_ind; + }; + + struct st_data data[]= { + {0, STMT_INDICATOR_NULL, "Monty", STMT_INDICATOR_NTS, "Widenius", STMT_INDICATOR_NTS}, + {0, STMT_INDICATOR_NULL, "David", STMT_INDICATOR_NTS, "Axmark", STMT_INDICATOR_NTS}, + {0, STMT_INDICATOR_NULL, "default", STMT_INDICATOR_DEFAULT, "N.N.", STMT_INDICATOR_NTS}, + }; + + unsigned int array_size= 1; + size_t row_size= sizeof(struct st_data); + int rc; + + if (!bulk_enabled) + return SKIP; + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk_example2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE bulk_example2 (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"\ + "forename CHAR(30) NOT NULL DEFAULT 'unknown', surname CHAR(30))"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO bulk_example2 VALUES (?,?,?)")); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND) * 3); + + /* We autogenerate id's, so all indicators are STMT_INDICATOR_NULL */ + bind[0].u.indicator= &data[0].id_ind; + bind[0].buffer_type= MYSQL_TYPE_LONG; + + bind[1].buffer= &data[0].forename; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].u.indicator= &data[0].forename_ind; + + bind[2].buffer_type= MYSQL_TYPE_STRING; + bind[2].buffer= &data[0].surname; + bind[2].u.indicator= &data[0].surname_ind; + + /* set array size */ + mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + + /* set row size */ + mysql_stmt_attr_set(stmt, STMT_ATTR_ROW_SIZE, &row_size); + + /* bind parameter */ + mysql_stmt_bind_param(stmt, bind); + + /* execute */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT forename, surname FROM bulk_example2"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result || !mysql_num_rows(result), "Invalid resultset"); + row = mysql_fetch_row(result); + if (strcmp(row[0], "Monty") || strcmp(row[1], "Widenius")) + { + mysql_free_result(result); + diag("Wrong values"); + return FAIL; + } + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE bulk_example2"); + check_mysql_rc(rc, mysql); + return OK; +} +static int bulk7(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + int array_size= 5; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL("UPDATE t1 SET a=a+1")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + + FAIL_IF(!rc, "Error expected: Bulk operation without parameters is not supported"); + diag("%s", mysql_stmt_error(stmt)); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_char_conv1(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + MYSQL_BIND bind_in, bind_out; + char buffer[100]; + char outbuffer[100]; + + if (!bulk_enabled) + return SKIP; + stmt= mysql_stmt_init(mysql); + strcpy (buffer, "\xC3\x82\xC3\x83\xC3\x84\x00"); + + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS char_conv"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE char_conv (a varchar(20)) CHARSET=latin1"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO char_conv VALUES (?)")); + check_stmt_rc(rc, stmt); + + memset(&bind_in, 0, sizeof(MYSQL_BIND)); + bind_in.buffer_type= MYSQL_TYPE_STRING; + bind_in.buffer_length= -1; + bind_in.buffer= &buffer; + + rc= mysql_stmt_bind_param(stmt, &bind_in); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL("SELECT a from char_conv")); + check_stmt_rc(rc, stmt); + + memset(&bind_out, 0, sizeof(MYSQL_BIND)); + bind_out.buffer_type= MYSQL_TYPE_STRING; + bind_out.buffer_length= 100; + bind_out.buffer= outbuffer; + + rc= mysql_stmt_bind_result(stmt, &bind_out); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc == MYSQL_NO_DATA, "Error"); + + mysql_stmt_close(stmt); + + + if (strcmp(buffer, outbuffer)) + { + diag("Error: Expected '%s' instead of '%s'", buffer, outbuffer); + return FAIL; + } + + rc= mysql_query(mysql, "DROP TABLE char_conv"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int test_char_conv2(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + int array_size= 1; + MYSQL_BIND bind_in, bind_out; + char *buffer[1]; + char outbuffer[100]; + + if (!bulk_enabled) + return SKIP; + + stmt= mysql_stmt_init(mysql); + buffer[0]= calloc(1, 7); + strcpy (buffer[0], "\xC3\x82\xC3\x83\xC3\x84\x00"); + + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS char_conv"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE char_conv (a varchar(20)) CHARSET=latin1"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO char_conv VALUES (?)")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + memset(&bind_in, 0, sizeof(MYSQL_BIND)); + bind_in.buffer_type= MYSQL_TYPE_STRING; + bind_in.buffer_length= -1; + bind_in.buffer= &buffer; + + rc= mysql_stmt_bind_param(stmt, &bind_in); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL("SELECT a from char_conv")); + check_stmt_rc(rc, stmt); + + memset(&bind_out, 0, sizeof(MYSQL_BIND)); + bind_out.buffer_type= MYSQL_TYPE_STRING; + bind_out.buffer_length= 100; + bind_out.buffer= outbuffer; + + rc= mysql_stmt_bind_result(stmt, &bind_out); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc == MYSQL_NO_DATA, "Error"); + + mysql_stmt_close(stmt); + + + if (strcmp(buffer[0], outbuffer)) + { + diag("Error: Expected '%s' instead of '%s'", buffer[0], outbuffer); + return FAIL; + } + free(buffer[0]); + + rc= mysql_query(mysql, "DROP TABLE char_conv"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int bulk_skip_row(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[3]; + MYSQL_RES *result; + MYSQL_ROW row; + + struct st_data { + unsigned long id; + char id_ind; + char forename[30]; + char forename_ind; + char surname[30]; + char surname_ind; + }; + + struct st_data data[]={ + { 0, STMT_INDICATOR_NULL, "Monty", STMT_INDICATOR_NTS, "Widenius", STMT_INDICATOR_IGNORE_ROW }, + { 0, STMT_INDICATOR_IGNORE_ROW, "David", STMT_INDICATOR_NTS, "Axmark", STMT_INDICATOR_NTS }, + { 0, STMT_INDICATOR_NULL, "default", STMT_INDICATOR_DEFAULT, "N.N.", STMT_INDICATOR_NTS }, + }; + + unsigned int array_size= 3; + size_t row_size= sizeof(struct st_data); + int rc; + + if (!bulk_enabled) + return SKIP; + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk_example2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE bulk_example2 (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"\ + "forename CHAR(30) NOT NULL DEFAULT 'unknown', surname CHAR(30))"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO bulk_example2 VALUES (?,?,?)")); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND) * 3); + + /* We autogenerate id's, so all indicators are STMT_INDICATOR_NULL */ + bind[0].u.indicator= &data[0].id_ind; + bind[0].buffer_type= MYSQL_TYPE_LONG; + + bind[1].buffer= &data[0].forename; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].u.indicator= &data[0].forename_ind; + + bind[2].buffer_type= MYSQL_TYPE_STRING; + bind[2].buffer= &data[0].surname; + bind[2].u.indicator= &data[0].surname_ind; + + /* set array size */ + mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + + /* set row size */ + mysql_stmt_attr_set(stmt, STMT_ATTR_ROW_SIZE, &row_size); + + /* bind parameter */ + mysql_stmt_bind_param(stmt, bind); + + /* execute */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT forename, surname FROM bulk_example2"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result || mysql_num_rows(result) != 1, "Invalid resultset"); + + row = mysql_fetch_row(result); + if (strcmp(row[0], "unknown") || strcmp(row[1], "N.N.")) + { + mysql_free_result(result); + diag("Wrong values"); + return FAIL; + } + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE bulk_example2"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int bulk_null_null(MYSQL *mysql) +{ + struct st_bulk4 { + char char_value[20]; + char indicator1; + int int_value; + char indicator2; + double double_value; + char indicator3; + char time_value[20]; + char indicator4; + char decimal_value[4]; + char indicator5; + }; + + struct st_bulk4 val[]= {{"3", STMT_INDICATOR_NTS, + 3, STMT_INDICATOR_NONE, + 3.0, STMT_INDICATOR_NONE, + "00:00:00", STMT_INDICATOR_NTS, + "3.0", STMT_INDICATOR_NTS}, + {"3", STMT_INDICATOR_NULL, + 3, STMT_INDICATOR_NULL, + 3.0, STMT_INDICATOR_NULL, + "00:00:00", STMT_INDICATOR_NULL, + "3.0", STMT_INDICATOR_NULL}, + {"3", STMT_INDICATOR_NTS, + 3, STMT_INDICATOR_NONE, + 3.0, STMT_INDICATOR_NONE, + "00:00:00", STMT_INDICATOR_NTS, + "3.0", STMT_INDICATOR_NTS}}; + int rc; + MYSQL_BIND bind[5]; + MYSQL_RES *res; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + size_t row_size= sizeof(struct st_bulk4); + int array_size= 3; + unsigned long server_version= mysql_get_server_version(mysql); + unsigned long lengths[3]= {-1, -1, -1}; + + if (!bulk_enabled) + return SKIP; + + if (server_version > 100300 && + server_version < 100305) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bulk_null"); + check_mysql_rc(rc,mysql); + rc= mysql_query(mysql, "CREATE TABLE bulk_null " + "(s varchar(20), " + " i int, " + " d double, " + " t time, " + " c decimal(3,1))"); + check_mysql_rc(rc,mysql); + + rc= mysql_stmt_prepare(stmt, "INSERT INTO bulk_null VALUES (?,?,?,?,?)", -1); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)*5); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ROW_SIZE, &row_size); + check_stmt_rc(rc, stmt); + + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].u.indicator= &val[0].indicator1; + bind[0].buffer= &val[0].char_value; + bind[0].length= lengths; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= &val[0].int_value; + bind[1].u.indicator= &val[0].indicator2; + bind[2].buffer_type= MYSQL_TYPE_DOUBLE; + bind[2].buffer= &val[0].double_value; + bind[2].u.indicator= &val[0].indicator3; + bind[3].buffer_type= MYSQL_TYPE_STRING; + bind[3].u.indicator= &val[0].indicator4; + bind[3].buffer= &val[0].time_value; + bind[3].length= lengths; + bind[4].buffer_type= MYSQL_TYPE_NEWDECIMAL; + bind[4].u.indicator= &val[0].indicator5; + bind[4].buffer= &val[0].decimal_value; + bind[4].length= lengths; + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT * FROM bulk_null WHERE s='3'"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + rc= (int)mysql_num_rows(res); + mysql_free_result(res); + FAIL_IF(rc != 2, "expected 2 rows"); + + rc= mysql_query(mysql, "SELECT * FROM bulk_null WHERE i=3"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + rc= (int)mysql_num_rows(res); + mysql_free_result(res); + FAIL_IF(rc != 2, "expected 2 rows"); + + rc= mysql_query(mysql, "SELECT * FROM bulk_null WHERE d=3.0"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + rc= (int)mysql_num_rows(res); + mysql_free_result(res); + FAIL_IF(rc != 2, "expected 2 rows"); + + rc= mysql_query(mysql, "SELECT * FROM bulk_null WHERE t='00:00:00'"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + rc= (int)mysql_num_rows(res); + mysql_free_result(res); + FAIL_IF(rc != 2, "expected 2 rows"); + + rc= mysql_query(mysql, "SELECT * FROM bulk_null WHERE c=3.0"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + rc= (int)mysql_num_rows(res); + mysql_free_result(res); + FAIL_IF(rc != 2, "expected 2 rows"); + + rc= mysql_query(mysql, "DROP TABLE bulk_null"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_mdev16593(MYSQL *mysql) +{ + int i; + int rc; + MYSQL_BIND bind[2]; + unsigned int array_size= 2; + int val_a[2]= {1,2}; + char indicators[2]= {STMT_INDICATOR_NULL, STMT_INDICATOR_NULL}; + const char *testcase[]= {"MYSQL_TYPE_LONG", "MYSQL_TYPE_NULL", "STMT_INDICATOR_NULL"}; + + diag("waiting for server fix"); + return SKIP; + + memset(&bind, 0, 2 * sizeof(MYSQL_BIND)); + for (i=0; i < 3; i++) + { + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + rc= mysql_query(mysql, "CREATE OR REPLACE TABLE t1 (a int not null auto_increment primary key, b int)"); + check_mysql_rc(rc, mysql); + switch (i) { + case 0: + bind[0].buffer_type= MYSQL_TYPE_LONG; + break; + case 1: + bind[0].buffer_type= MYSQL_TYPE_NULL; + break; + case 2: + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].u.indicator= indicators; + break; + } + bind[0].buffer= val_a; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= val_a; + + rc= mysql_stmt_prepare(stmt, SL("insert into t1 values(?,?)")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "COMMIT"); + check_mysql_rc(rc, mysql); + + diag("Insert id with buffer_type %s: %lld", + testcase[i], + mysql_stmt_insert_id(stmt)); + + rc= mysql_query(mysql, "SELECT max(a) FROM t1"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + diag("Max value for t1.a=%s", row[0]); + mysql_free_result(res); + + mysql_stmt_close(stmt); + } + return OK; +} + +struct my_tests_st my_tests[] = { + {"check_bulk", check_bulk, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_mdev16593", test_mdev16593, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"bulk_null_null", bulk_null_null, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_char_conv1", test_char_conv1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_char_conv2", test_char_conv2, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc243", test_conc243, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"update_no_param", bulk7, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk5", bulk5, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk6", bulk6, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk1", bulk1, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk2", bulk2, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk3", bulk3, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk4", bulk4, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk_null", bulk_null, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bulk_skip_row", bulk_skip_row, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/charset.c b/libmariadb/unittest/libmariadb/charset.c new file mode 100644 index 00000000..b438f8d9 --- /dev/null +++ b/libmariadb/unittest/libmariadb/charset.c @@ -0,0 +1,881 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ + +#include "my_test.h" + +/* + test gbk charset escaping + + 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" + +/* set connection options */ +struct my_option_st opt_bug8378[] = { + {MYSQL_SET_CHARSET_NAME, (char *) "gbk"}, + {0, NULL} +}; + +int bug_8378(MYSQL *mysql) { + int rc, len; + char out[9], buf[256]; + MYSQL_RES *res; + MYSQL_ROW row; + + len= mysql_real_escape_string(mysql, out, TEST_BUG8378_IN, 4); + FAIL_IF(memcmp(out, TEST_BUG8378_OUT, len), "wrong result"); + + sprintf(buf, "SELECT '%s' FROM DUAL", TEST_BUG8378_OUT); + + rc= mysql_query(mysql, buf); + check_mysql_rc(rc, mysql); + + if ((res= mysql_store_result(mysql))) { + row= mysql_fetch_row(res); + if (memcmp(row[0], TEST_BUG8378_IN, 4)) { + mysql_free_result(res); + return FAIL; + } + mysql_free_result(res); + } else + return FAIL; + + return OK; +} + +int test_client_character_set(MYSQL *mysql) +{ + MY_CHARSET_INFO cs; + char *csname= (char*) "latin2"; + char *csdefault= (char*)mysql_character_set_name(mysql); + + + FAIL_IF(mysql_set_character_set(mysql, csname), mysql_error(mysql)); + + mysql_get_character_set_info(mysql, &cs); + + FAIL_IF(strcmp(cs.csname, "latin2") || strcmp(cs.name, "latin2_general_ci"), + "Character set != latin2"); + FAIL_IF(mysql_set_character_set(mysql, csdefault), mysql_error(mysql)); + + return OK; +} + +/* + * Regression test for bug #10214 + * + * Test escaping with NO_BACKSLASH_ESCAPES + * + */ +int bug_10214(MYSQL *mysql) +{ + int len, rc; + char out[8]; + + /* reset sql_mode */ + rc= mysql_query(mysql, "SET sql_mode=''"); + check_mysql_rc(rc, mysql); + + len= mysql_real_escape_string(mysql, out, "a'b\\c", 5); + FAIL_IF(memcmp(out, "a\\'b\\\\c", len), "wrong result"); + + rc= mysql_query(mysql, "set sql_mode='NO_BACKSLASH_ESCAPES'"); + check_mysql_rc(rc, mysql); + FAIL_IF(!(mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES), + "wrong server status: NO_BACKSLASH_ESCAPES not set"); + + len= mysql_real_escape_string(mysql, out, "a'b\\c", 5); + FAIL_IF(memcmp(out, "a''b\\c", len), "wrong result"); + + return OK; +} + +/* + * simple escaping of special chars + */ +int test_escaping(MYSQL *mysql) +{ + int i= 0, rc, len; + char out[20]; + const char *escape_chars[] = {"'", "\x0", "\n", "\r", "\\", "\0", NULL}; + + /* reset sql_mode, mysql_change_user call doesn't reset it */ + rc= mysql_query(mysql, "SET sql_mode=''"); + check_mysql_rc(rc, mysql); + + while (escape_chars[i]) { + len= mysql_real_escape_string(mysql, out, escape_chars[i], 1); + FAIL_IF(len < 2, "Len < 2"); + i++; + } + + return OK; +} + +/* + * server doesn't reset sql_mode after COM_CHANGE_USER + */ +int bug_41785(MYSQL *mysql) +{ + char out[10]; + int rc, len; + + len= mysql_real_escape_string(mysql, out, "\\", 1); + FAIL_IF(len != 2, "len != 2"); + + rc= mysql_query(mysql, "SET SQL_MODE=NO_BACKSLASH_ESCAPES"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "SET sql_mode=''"); + check_mysql_rc(rc, mysql); + + mysql_change_user(mysql, "root", "", "test"); + + len= mysql_real_escape_string(mysql, out, "\\", 1); + FAIL_IF(len != 2, "len != 2"); + + return OK; +} + +static int test_conversion(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + MYSQL_BIND my_bind[1]; + uchar buff[4]; + ulong length; + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "CREATE TABLE t1 (a TEXT) DEFAULT CHARSET latin1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "SET character_set_connection=utf8, character_set_client=utf8, " + " character_set_results=latin1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + stmt_text= "INSERT INTO t1 (a) VALUES (?)"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + stmt_text= "SELECT a FROM t1"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + my_bind[0].buffer_length= sizeof(buff); + mysql_stmt_bind_result(stmt, my_bind); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(length == 1, "length != 1"); + FAIL_UNLESS(buff[0] == 0xE0, "buff[0] != 0xE0"); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + stmt_text= "DROP TABLE t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "SET NAMES DEFAULT"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug27876(MYSQL *mysql) +{ + 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]; + + rc= mysql_query(mysql, "set names utf8"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "select version()"); + check_mysql_rc(rc, mysql); + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + mysql_free_result(result); + + sprintf(query, "DROP FUNCTION IF EXISTS %s", (char*) utf8_func); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + 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); + check_mysql_rc(rc, mysql); + sprintf(query, "SELECT %s(VERSION())", (char*) utf8_func); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + mysql_free_result(result); + + sprintf(query, "DROP FUNCTION %s", (char*) utf8_func); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "set names default"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_ps_i18n(MYSQL *mysql) +{ + 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; + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + /* + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "SET CHARACTER_SET_CLIENT=koi8r, " + "CHARACTER_SET_CONNECTION=cp1251, " + "CHARACTER_SET_RESULTS=koi8r"; + + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + memset(bind_array, '\0', sizeof(bind_array)); + bind_array[0].buffer_type= MYSQL_TYPE_STRING; + bind_array[0].buffer= (void *) koi8; + bind_array[0].buffer_length= (unsigned long)strlen(koi8); + + bind_array[1].buffer_type= MYSQL_TYPE_STRING; + bind_array[1].buffer= (void *) koi8; + bind_array[1].buffer_length= (unsigned long)strlen(koi8); + + stmt= mysql_stmt_init(mysql); + check_stmt_rc(rc, stmt); + + stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)"; + + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + mysql_stmt_bind_param(stmt, bind_array); + check_stmt_rc(rc, stmt); + +// mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8)); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + 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_stmt_rc(rc, stmt); + FAIL_UNLESS(buf1_len == strlen(cp1251), "buf1_len != strlen(cp1251)"); + FAIL_UNLESS(buf2_len == strlen(cp1251), "buf2_len != strlen(cp1251)"); + FAIL_UNLESS(!memcmp(buf1, cp1251, buf1_len), "buf1 != cp1251"); + FAIL_UNLESS(!memcmp(buf2, cp1251, buf1_len), "buf2 != cp1251"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + /* + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)"; + + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + /* this data must be converted */ + bind_array[0].buffer_type= MYSQL_TYPE_STRING; + bind_array[0].buffer= (void *) koi8; + bind_array[0].buffer_length= (unsigned long)strlen(koi8); + + bind_array[1].buffer_type= MYSQL_TYPE_STRING; + bind_array[1].buffer= (void *) koi8; + bind_array[1].buffer_length= (unsigned long)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_stmt_rc(rc, stmt); + /* 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= (unsigned long)strlen(cp1251); + + bind_array[1].buffer_type= MYSQL_TYPE_BLOB; + bind_array[1].buffer= (void *) cp1251; + bind_array[1].buffer_length= (unsigned long)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_stmt_rc(rc, stmt); + /* 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + 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) + { + FAIL_UNLESS(buf1_len == strlen(koi8), "buf1_len != strlen(koi8)"); + FAIL_UNLESS(buf2_len == strlen(koi8), "buf2_len != strlen(koi8)"); + FAIL_UNLESS(!memcmp(buf1, koi8, buf1_len), "buf1 != koi8"); + FAIL_UNLESS(!memcmp(buf2, koi8, buf1_len), "buf2 != koi8"); + } + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + mysql_stmt_close(stmt); + + stmt_text= "DROP TABLE t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "SET NAMES DEFAULT"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + Bug#30472: libmysql doesn't reset charset, insert_id after succ. + mysql_change_user() call row insertions. +*/ + +static int 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; + int rc; + + /* Get the cached client character set name. */ + + strcpy(character_set_name, mysql_character_set_name(con)); + + /* Retrieve server character set information. */ + + rc= mysql_query(con, "SHOW VARIABLES LIKE 'character_set_client'"); + check_mysql_rc(rc, con); + + rs= mysql_store_result(con); + FAIL_IF(!rs, "Invalid result set"); + row= mysql_fetch_row(rs); + FAIL_IF(!row, "Couldn't fetch row"); + strcpy(character_set_client, row[1]); + mysql_free_result(rs); + + rc= mysql_query(con, "SHOW VARIABLES LIKE 'character_set_results'"); + check_mysql_rc(rc, con); + rs= mysql_store_result(con); + FAIL_IF(!rs, "Invalid result set"); + row= mysql_fetch_row(rs); + FAIL_IF(!row, "Couldn't fetch row"); + strcpy(character_set_results, row[1]); + mysql_free_result(rs); + + rc= mysql_query(con, "SHOW VARIABLES LIKE 'collation_connection'"); + check_mysql_rc(rc, con); + rs= mysql_store_result(con); + FAIL_IF(!rs, "Invalid result set"); + row= mysql_fetch_row(rs); + FAIL_IF(!row, "Couldn't fetch row"); + strcpy(collation_connection, row[1]); + mysql_free_result(rs); + return OK; +} + +#define MY_CS_NAME_SIZE 32 + +static int test_bug30472(MYSQL *mysql) +{ + int rc; + + char character_set_name_1[MY_CS_NAME_SIZE]; + char character_set_client_1[MY_CS_NAME_SIZE]; + char character_set_results_1[MY_CS_NAME_SIZE]; + char collation_connnection_1[MY_CS_NAME_SIZE]; + + char character_set_name_2[MY_CS_NAME_SIZE]; + char character_set_client_2[MY_CS_NAME_SIZE]; + char character_set_results_2[MY_CS_NAME_SIZE]; + char collation_connnection_2[MY_CS_NAME_SIZE]; + + char character_set_name_3[MY_CS_NAME_SIZE]; + char character_set_client_3[MY_CS_NAME_SIZE]; + char character_set_results_3[MY_CS_NAME_SIZE]; + char collation_connnection_3[MY_CS_NAME_SIZE]; + + char character_set_name_4[MY_CS_NAME_SIZE]; + char character_set_client_4[MY_CS_NAME_SIZE]; + char character_set_results_4[MY_CS_NAME_SIZE]; + char collation_connnection_4[MY_CS_NAME_SIZE]; + + SKIP_MAXSCALE; + + if (mysql_get_server_version(mysql) < 50100 || !is_mariadb) + { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + /* Retrieve character set information. */ + + mysql_set_character_set(mysql, "latin1"); + bug30472_retrieve_charset_info(mysql, + character_set_name_1, + character_set_client_1, + character_set_results_1, + collation_connnection_1); + + /* Switch client character set. */ + + FAIL_IF(mysql_set_character_set(mysql, "ascii"), + "Setting cs to ascii failed"); + + /* Retrieve character set information. */ + + bug30472_retrieve_charset_info(mysql, + 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. + */ + + FAIL_UNLESS(strcmp(character_set_name_2, "ascii") == 0, "cs_name != ascii"); + FAIL_UNLESS(strcmp(character_set_client_2, "ascii") == 0, "cs_client != ascii"); + FAIL_UNLESS(strcmp(character_set_results_2, "ascii") == 0, "cs_result != ascii"); + FAIL_UNLESS(strcmp(collation_connnection_2, "ascii_general_ci") == 0, + "collation != ascii_general_ci"); + + diag("%s %s", character_set_name_1, character_set_name_2); + FAIL_UNLESS(strcmp(character_set_name_1, character_set_name_2) != 0, "cs_name1 = cs_name2"); + FAIL_UNLESS(strcmp(character_set_client_1, character_set_client_2) != 0, "cs_client1 = cs_client2"); + FAIL_UNLESS(strcmp(character_set_results_1, character_set_results_2) != 0, "cs_result1 = cs_result2"); + FAIL_UNLESS(strcmp(collation_connnection_1, collation_connnection_2) != 0, "collation1 = collation2"); + + /* Call mysql_change_user() with the same username, password, database. */ + + rc= mysql_change_user(mysql, username, password, (schema) ? schema : "test"); + mysql_set_character_set(mysql, "latin1"); + check_mysql_rc(rc, mysql); + + /* Retrieve character set information. */ + + bug30472_retrieve_charset_info(mysql, + character_set_name_3, + character_set_client_3, + character_set_results_3, + collation_connnection_3); + + /* Check that character set information has been reset. */ + + FAIL_UNLESS(strcmp(character_set_name_1, character_set_name_3) == 0, "cs_name1 != cs_name3"); + FAIL_UNLESS(strcmp(character_set_client_1, character_set_client_3) == 0, "cs_client1 != cs_client3"); + FAIL_UNLESS(strcmp(character_set_results_1, character_set_results_3) == 0, "cs_result1 != cs_result3"); + FAIL_UNLESS(strcmp(collation_connnection_1, collation_connnection_3) == 0, "collation1 != collation3"); + + /* Change connection-default character set in the client. */ + + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "latin2"); + + /* + Call mysql_change_user() in order to check that new connection will + have UTF8 character set on the client and on the server. + */ + + rc= mysql_change_user(mysql, username, password, (schema) ? schema : "test"); + check_mysql_rc(rc, mysql); + + /* Retrieve character set information. */ + + bug30472_retrieve_charset_info(mysql, + 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. */ + + FAIL_UNLESS(strcmp(character_set_name_4, "latin2") == 0, "cs_name != latin2"); + FAIL_UNLESS(strcmp(character_set_client_4, "latin2") == 0, "cs_client != latin2"); + FAIL_UNLESS(strcmp(character_set_results_4, "latin2") == 0, "cs_result != latin2"); + FAIL_UNLESS(strcmp(collation_connnection_4, "latin2_general_ci") == 0, + "collation_connection != latin2_general_ci"); + + /* That's it. Cleanup. */ + + return OK; +} + +static int test_bug_54100(MYSQL *mysql) +{ + MYSQL_RES *result; + MYSQL_ROW row; + int rc; + + rc= mysql_query(mysql, "SHOW CHARACTER SET"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + + while ((row= mysql_fetch_row(result))) + { + /* ignore ucs2 */ + if (strcmp(row[0], "ucs2") + && strcmp(row[0], "utf16le") + && (strcmp(row[0], "utf8mb4") && mariadb_connection(mysql) && mysql_get_server_version(mysql) < 100600) + && (strcmp(row[0], "utf8") && mariadb_connection(mysql) && mysql_get_server_version(mysql) >= 100600) + && strcmp(row[0], "utf16") + && strcmp(row[0], "utf32")) { + rc= mysql_set_character_set(mysql, row[0]); + check_mysql_rc(rc, mysql); + } + } + mysql_free_result(result); + + return OK; +} + + +/* We need this internal function for the test */ + +static int test_utf16_utf32_noboms(MYSQL *mysql __attribute__((unused))) +{ +#ifndef HAVE_ICONV + diag("MariaDB Connector/C was built without iconv support"); + return SKIP; +#else + const char *csname[]= {"utf16", "utf16le", "utf32", "utf8"}; + MARIADB_CHARSET_INFO *csinfo[sizeof(csname)/sizeof(char*)]; + + const int UTF8= sizeof(csname)/sizeof(char*) - 1; + + unsigned char in_string[][8]= {"\xd8\x02\xdc\x60\0", /* utf16(be) */ + "\x02\xd8\x60\xdc\0", /* utf16le */ + "\x00\x01\x08\x60\0\0\0", /* utf32(be) */ + "\xF0\x90\xA1\xA0" }; /* utf8 */ + size_t in_oct_len[]= {6, 6, 8, 5}; + + char buffer[8], as_hex[16]; + int i, error; + size_t rc, in_len, out_len; + + for (i= 0; i < (int)(sizeof(csname)/sizeof(char*)); ++i) + { + csinfo[i]= mariadb_get_charset_by_name(csname[i]); + + if (csinfo[i] == NULL) + { + diag("Could not get cs info for %s", csname[i]); + return FAIL; + } + } + + for (i= 0; i < UTF8; ++i) + { + in_len= in_oct_len[i]; + out_len= sizeof(buffer); + + diag("Converting %s->%s", csname[i], csname[UTF8]); + rc= mariadb_convert_string((char *)in_string[i], &in_len, csinfo[i], buffer, &out_len, csinfo[UTF8], &error); + + FAIL_IF(rc == (size_t)-1, "Conversion failed"); + FAIL_IF(rc != in_oct_len[UTF8], "Incorrect number of written bytes"); + + if (memcmp(buffer, in_string[UTF8], rc) != 0) + { + mysql_hex_string(as_hex, buffer, (unsigned long)rc); + diag("Converted string(%s) does not match the expected one", as_hex); + return FAIL; + } + + in_len= in_oct_len[UTF8]; + out_len= sizeof(buffer); + + diag("Converting %s->%s", csname[UTF8], csname[i]); + rc= mariadb_convert_string((char *)in_string[UTF8], &in_len, csinfo[UTF8], buffer, &out_len, csinfo[i], &error); + + FAIL_IF(rc == (size_t)-1, "Conversion failed"); + diag("rc=%lu oct_len: %lu", (unsigned long)rc, (unsigned long)in_oct_len[i]); + FAIL_IF(rc != in_oct_len[i], "Incorrect number of written bytes"); + + if (memcmp(buffer, in_string[i], rc) != 0) + { + mysql_hex_string(as_hex, buffer, (unsigned long)rc); + diag("Converted string(%s) does not match the expected one", as_hex); + return FAIL; + } + } + + return OK; +#endif +} + +static int charset_auto(MYSQL *my __attribute__((unused))) +{ + const char *csname1, *csname2; + const char *osname; + MYSQL *mysql= mysql_init(NULL); + int rc; + + osname= madb_get_os_character_set(); + + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "auto"); + + FAIL_IF(!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0), + mysql_error(mysql)); + + csname1= mysql_character_set_name(mysql); + diag("Character set: %s os charset: %s", csname1, osname); + + FAIL_IF(!strcasecmp(osname,"utf8") ? strncmp(osname, csname1, 4) : + strcmp(osname, csname1), + "character set is not os character set"); + if (strcmp(osname, "utf8")) + { + rc= mysql_set_character_set(mysql, "utf8"); + check_mysql_rc(rc, mysql); + + csname2= mysql_character_set_name(mysql); + diag("Character set: %s", csname2); + FAIL_IF(!strcmp(csname2, csname1), "Wrong charset: expected utf8"); + + rc= mysql_set_character_set(mysql, "auto"); + check_mysql_rc(rc, mysql); + + csname2= mysql_character_set_name(mysql); + diag("Character set: %s", csname2); + FAIL_IF(strcmp(csname2, osname), "Wrong charset: expected os charset"); + } + mysql_close(mysql); + return OK; +} + +/* check if all server character sets are supported */ +static int test_conc223(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res; + MYSQL_ROW row; + int found= 0; + int mdev27266= 0; + + SKIP_MYSQL(mysql); + + /* + Test if we're running against an MDEV-27266 server. + It can be detected by the presense of the FULL_COLLATION_NAME + column in I_S.COLLATION_CHARACTER_SET_APPLICABILITY. + */ + rc= mysql_query(mysql, + "SELECT COUNT(*) " + "FROM INFORMATION_SCHEMA.COLUMNS " + "WHERE COLUMN_NAME='FULL_COLLATION_NAME' " + " AND TABLE_NAME='COLLATION_CHARACTER_SET_APPLICABILITY'"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + if ((row= mysql_fetch_row(res))) + mdev27266= atoi(row[0]); + mysql_free_result(res); + diag("MDEV-27266 aware server: %d", mdev27266); + + /* + Now get the list of collations either from I_S.COLLATIONS + or I_S.COLLATION_CHARACTER_SET_APPLICABILITY, + depending on the MDEV-27266 server awareness. + */ + if (mdev27266) + rc= mysql_query(mysql, + "SELECT ID, CHARACTER_SET_NAME, FULL_COLLATION_NAME " + "FROM INFORMATION_SCHEMA.COLLATION_CHARACTER_SET_APPLICABILITY"); + else + rc= mysql_query(mysql, + "SELECT ID, CHARACTER_SET_NAME, COLLATION_NAME " + "FROM INFORMATION_SCHEMA.COLLATIONS"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + while ((row = mysql_fetch_row(res))) + { + int id; + + if (row[0]) + { + id= atoi(row[0]); + if (!mariadb_get_charset_by_nr(id)) + { + diag("%04d %s %s", id, row[1], row[2]); + found++; + } + } + } + mysql_free_result(res); + if (found) + { + diag("%d character sets/collations not found", found); + return FAIL; + } + return OK; +} + +struct my_tests_st my_tests[] = { + {"test_conc223", test_conc223, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"charset_auto", charset_auto, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bug_8378: mysql_real_escape with gbk", bug_8378, TEST_CONNECTION_NEW, 0, opt_bug8378, NULL}, + {"test_client_character_set", test_client_character_set, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bug_10214: mysql_real_escape with NO_BACKSLASH_ESCAPES", bug_10214, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_escaping", test_escaping, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conversion", test_conversion, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"bug_41785", bug_41785, TEST_CONNECTION_DEFAULT, 0, NULL, "not fixed yet"}, + {"test_bug27876", test_bug27876, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug30472", test_bug30472, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_ps_i18n", test_ps_i18n, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug_54100", test_bug_54100, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_utf16_utf32_noboms", test_utf16_utf32_noboms, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, 0} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/conc336.c b/libmariadb/unittest/libmariadb/conc336.c new file mode 100644 index 00000000..3d1f47b5 --- /dev/null +++ b/libmariadb/unittest/libmariadb/conc336.c @@ -0,0 +1,59 @@ +#include "my_test.h" + +#define MAX_COUNT 2000 + +int main(int argc, char *argv[]) { + + MYSQL *mysql; + int i; + + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + if (IS_XPAND()) + SKIP_XPAND; + + if (IS_SKYSQL(hostname)) + return 0; + + if (IS_XPAND()) + SKIP_XPAND; + + diag("hostname: %s", hostname); + + for (i = 0; i < MAX_COUNT; ++i) { + + if (mysql_library_init(-1, NULL, NULL) != 0) { + diag("mysql_library_init failed"); + return 1; + } + + mysql = mysql_init(NULL); + if (!mysql) { + diag("mysql_init failed"); + return 1; + } + + if (force_tls) + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &force_tls); + + if (!mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0)) { + diag("mysql_real_connect failed: %s", mysql_error(mysql)); + return 1; + } + + if (mysql_query(mysql, "SELECT NULL LIMIT 0") != 0) { + diag("mysql_query failed: %s", mysql_error(mysql)); + return 1; + } + + mysql_close(mysql); + mysql_library_end(); + + } + + return 0; + +} diff --git a/libmariadb/unittest/libmariadb/connection.c b/libmariadb/unittest/libmariadb/connection.c new file mode 100644 index 00000000..517bc538 --- /dev/null +++ b/libmariadb/unittest/libmariadb/connection.c @@ -0,0 +1,2365 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +/** + Some basic tests of the client API. +*/ + +#include "my_test.h" + +static int test_conc66(MYSQL *my) +{ + MYSQL *mysql= mysql_init(NULL); + int rc; + FILE *fp; + char query[1024]; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (!(fp= fopen("./my-conc66-test.cnf", "w"))) + return FAIL; + + fprintf(fp, "[notmygroup]\n"); + fprintf(fp, "user=foo\n"); + fprintf(fp, "[conc-66]\n"); + fprintf(fp, "user=conc66\n"); + fprintf(fp, "port=3306\n"); + fprintf(fp, "enable-local-infile\n"); + fprintf(fp, "server_plugin=file_key_management:file_key_management_algorithm=AES_CTR;file_key_management_key=secret\n"); + fprintf(fp, "password='test@A1\\\";#test'\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "conc-66"); + check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./my-conc66-test.cnf"); + check_mysql_rc(rc, mysql); + + sprintf(query, "GRANT ALL ON %s.* TO 'conc66'@'%s' IDENTIFIED BY 'test@A1\";#test'", schema, this_host ? this_host : "localhost"); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + rc= mysql_query(my, "FLUSH PRIVILEGES"); + check_mysql_rc(rc, my); + if (!my_test_connect(mysql, hostname, NULL, + NULL, schema, port, socketname, 0)) + { + diag("user: %s", mysql->options.user); + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + diag("user: %s", mysql->options.user); + + sprintf(query, "DROP user 'conc66'@'%s'", this_host ? this_host : "localhost"); + rc= mysql_query(my, query); + + check_mysql_rc(rc, my); + mysql_close(mysql); + return OK; +} + +static int test_bug20023(MYSQL *mysql) +{ + int sql_big_selects_orig; + int max_join_size_orig; + + int sql_big_selects_2; + int sql_big_selects_3; + int sql_big_selects_4; + int sql_big_selects_5; + int rc; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (mysql_get_server_version(mysql) < 50100) { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + + /*********************************************************************** + Remember original SQL_BIG_SELECTS, MAX_JOIN_SIZE values. + ***********************************************************************/ + + query_int_variable(mysql, + "@@session.sql_big_selects", + &sql_big_selects_orig); + + query_int_variable(mysql, + "@@global.max_join_size", + &max_join_size_orig); + + /*********************************************************************** + Test that COM_CHANGE_USER resets the SQL_BIG_SELECTS to the initial value. + ***********************************************************************/ + + /* Issue COM_CHANGE_USER. */ + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + /* Query SQL_BIG_SELECTS. */ + + query_int_variable(mysql, + "@@session.sql_big_selects", + &sql_big_selects_2); + + /* Check that SQL_BIG_SELECTS is reset properly. */ + + FAIL_UNLESS(sql_big_selects_orig == sql_big_selects_2, "Different value for sql_big_select"); + + /*********************************************************************** + 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. */ + + rc= mysql_query(mysql, "SET @@global.max_join_size = 10000"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "SET @@session.max_join_size = default"); + check_mysql_rc(rc, mysql); + + /* Issue COM_CHANGE_USER. */ + + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + /* Query SQL_BIG_SELECTS. */ + + query_int_variable(mysql, + "@@session.sql_big_selects", + &sql_big_selects_3); + + /* Check that SQL_BIG_SELECTS is 0. */ + + FAIL_UNLESS(sql_big_selects_3 == 0, "big_selects != 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 (-1). */ + + rc= mysql_query(mysql, "SET @@global.max_join_size = cast(-1 as unsigned int)"); + rc= mysql_query(mysql, "SET @@session.max_join_size = default"); + + /* Issue COM_CHANGE_USER. */ + + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + /* Query SQL_BIG_SELECTS. */ + + query_int_variable(mysql, + "@@session.sql_big_selects", + &sql_big_selects_4); + + /* Check that SQL_BIG_SELECTS is 1. */ + + FAIL_UNLESS(sql_big_selects_4 == 1, "sql_big_select != 1"); + + /*********************************************************************** + Restore MAX_JOIN_SIZE. + Check that SQL_BIG_SELECTS will be the original one. + ***********************************************************************/ + + rc= mysql_query(mysql, "SET @@global.max_join_size = cast(-1 as unsigned int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET @@session.max_join_size = default"); + check_mysql_rc(rc, mysql); + + /* Issue COM_CHANGE_USER. */ + + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + /* Query SQL_BIG_SELECTS. */ + + query_int_variable(mysql, + "@@session.sql_big_selects", + &sql_big_selects_5); + + /* Check that SQL_BIG_SELECTS is 1. */ + + FAIL_UNLESS(sql_big_selects_5 == sql_big_selects_orig, "big_select != 1"); + + /*********************************************************************** + That's it. Cleanup. + ***********************************************************************/ + + return OK; +} + +static int test_change_user(MYSQL *mysql) +{ + 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; + + diag("Due to mysql_change_user security fix this test will not work anymore."); + return(SKIP); + + /* Prepare environment */ + sprintf(buff, "drop database if exists %s", db); + rc= mysql_query(mysql, buff); + check_mysql_rc(rc, mysql); + + sprintf(buff, "create database %s", db); + rc= mysql_query(mysql, buff); + check_mysql_rc(rc, mysql); + + sprintf(buff, + "grant select on %s.* to %s@'%%' identified by '%s'", + db, + user_pw, + pw); + rc= mysql_query(mysql, buff); + check_mysql_rc(rc, mysql); + + sprintf(buff, + "grant select on %s.* to %s@'%%'", + db, + user_no_pw); + rc= mysql_query(mysql, buff); + check_mysql_rc(rc, mysql); + + + /* Try some combinations */ + rc= mysql_change_user(mysql, NULL, NULL, NULL); + FAIL_UNLESS(rc, "Error expected"); + + + rc= mysql_change_user(mysql, "", NULL, NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, "", "", NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, "", "", ""); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, NULL, "", ""); + FAIL_UNLESS(rc, "Error expected"); + + + rc= mysql_change_user(mysql, NULL, NULL, ""); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, "", NULL, ""); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_pw, NULL, ""); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_pw, "", ""); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_pw, "", NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_pw, NULL, NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_pw, "", db); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_pw, NULL, db); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_pw, pw, db); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, user_pw, pw, NULL); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, user_pw, pw, ""); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, user_no_pw, pw, db); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_no_pw, pw, ""); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_no_pw, pw, NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, user_no_pw, "", NULL); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, user_no_pw, "", ""); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, user_no_pw, "", db); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, user_no_pw, NULL, db); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, "", pw, db); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, "", pw, ""); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, "", pw, NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, NULL, pw, NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, NULL, NULL, db); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, NULL, "", db); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, "", "", db); + FAIL_UNLESS(rc, "Error expected"); + + /* Cleanup the environment */ + + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + sprintf(buff, "drop database %s", db); + rc= mysql_query(mysql, buff); + check_mysql_rc(rc, mysql); + + sprintf(buff, "drop user %s@'%%'", user_pw); + rc= mysql_query(mysql, buff); + check_mysql_rc(rc, mysql); + + sprintf(buff, "drop user %s@'%%'", user_no_pw); + rc= mysql_query(mysql, buff); + check_mysql_rc(rc, mysql); + + return OK; +} + +/** + Bug#31669 Buffer overflow in mysql_change_user() +*/ + +#define LARGE_BUFFER_SIZE 2048 + +static int test_bug31669(MYSQL *mysql) +{ + int rc; + static char buff[LARGE_BUFFER_SIZE+1]; + static char user[USERNAME_CHAR_LENGTH+1]; + static char db[NAME_CHAR_LEN+1]; + static char query[LARGE_BUFFER_SIZE*2]; + + diag("Due to mysql_change_user security fix this test will not work anymore."); + return(SKIP); + + rc= mysql_change_user(mysql, NULL, NULL, NULL); + FAIL_UNLESS(rc, "Error expected"); + + rc= mysql_change_user(mysql, "", "", ""); + FAIL_UNLESS(rc, "Error expected"); + + memset(buff, 'a', sizeof(buff)); + + rc= mysql_change_user(mysql, buff, buff, buff); + FAIL_UNLESS(rc, "Error expected"); + + rc = mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + memset(db, 'a', sizeof(db)); + db[NAME_CHAR_LEN]= 0; + sprintf(query, "CREATE DATABASE IF NOT EXISTS %s", db); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + memset(user, 'b', sizeof(user)); + user[USERNAME_CHAR_LENGTH]= 0; + memset(buff, 'c', sizeof(buff)); + buff[LARGE_BUFFER_SIZE]= 0; + sprintf(query, "GRANT ALL PRIVILEGES ON *.* TO '%s'@'%%' IDENTIFIED BY '%s' WITH GRANT OPTION", user, buff); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH PRIVILEGES"); + check_mysql_rc(rc, mysql); + + rc= mysql_change_user(mysql, user, buff, db); + check_mysql_rc(rc, mysql); + + user[USERNAME_CHAR_LENGTH-1]= 'a'; + rc= mysql_change_user(mysql, user, buff, db); + FAIL_UNLESS(rc, "Error expected"); + + user[USERNAME_CHAR_LENGTH-1]= 'b'; + buff[LARGE_BUFFER_SIZE-1]= 'd'; + rc= mysql_change_user(mysql, user, buff, db); + FAIL_UNLESS(rc, "Error expected"); + + buff[LARGE_BUFFER_SIZE-1]= 'c'; + db[NAME_CHAR_LEN-1]= 'e'; + rc= mysql_change_user(mysql, user, buff, db); + FAIL_UNLESS(rc, "Error expected"); + + db[NAME_CHAR_LEN-1]= 'a'; + rc= mysql_change_user(mysql, user, buff, db); + FAIL_UNLESS(!rc, "Error expected"); + + rc= mysql_change_user(mysql, user + 1, buff + 1, db + 1); + FAIL_UNLESS(rc, "Error expected"); + + rc = mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + sprintf(query, "DROP DATABASE %s", db); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + sprintf(query, "DELETE FROM mysql.user WHERE User='%s'", user); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + FAIL_UNLESS(mysql_affected_rows(mysql) == 1, ""); + + return OK; +} + +/** + Bug# 33831 my_test_connect() should fail if + given an already connected MYSQL handle. +*/ + +static int test_bug33831(MYSQL *mysql) +{ + FAIL_IF(my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0), + "Error expected"); + + return OK; +} + +/* Test MYSQL_OPT_RECONNECT, Bug#15719 */ + +static int test_opt_reconnect(MYSQL *mysql) +{ + my_bool my_true= TRUE; + int rc; + my_bool reconnect; + + printf("true: %d\n", TRUE); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "not enough memory"); + + mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 0, "reconnect != 0"); + + rc= mysql_options(mysql, MYSQL_OPT_RECONNECT, &my_true); + check_mysql_rc(rc, mysql); + + mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 1, "reconnect != 1"); + + if (!(my_test_connect(mysql, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + mysql_close(mysql); + return FAIL; + } + + mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 1, "reconnect != 1"); + + mysql_close(mysql); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "not enough memory"); + + mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 0, "reconnect != 0"); + + if (!(my_test_connect(mysql, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + mysql_close(mysql); + return FAIL; + } + + mysql_get_option(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 0, "reconnect != 0"); + + mysql_close(mysql); + return OK; +} + + +static int test_compress(MYSQL *mysql) +{ + // maxscale doesn't support compression + MYSQL_RES *res; + MYSQL_ROW row; + int rc; + SKIP_MAXSCALE; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "not enough memory"); + + /* use compressed protocol */ + rc= mysql_options(mysql, MYSQL_OPT_COMPRESS, NULL); + + if (!(my_test_connect(mysql, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + return FAIL; + } + + rc= mysql_query(mysql, "SHOW STATUS LIKE 'compression'"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + FAIL_UNLESS(strcmp(row[1], "ON") == 0, "Compression off"); + mysql_free_result(res); + + mysql_close(mysql); + return OK; +} + +static int test_reconnect(MYSQL *mysql) +{ + my_bool my_true= TRUE; + MYSQL *mysql1; + int rc; + my_bool reconnect; + SKIP_MAXSCALE; + + mysql1= mysql_init(NULL); + FAIL_IF(!mysql1, "not enough memory"); + + mysql_get_option(mysql1, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 0, "reconnect != 0"); + + rc= mysql_options(mysql1, MYSQL_OPT_RECONNECT, &my_true); + check_mysql_rc(rc, mysql1); + + mysql_get_option(mysql1, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 1, "reconnect != 1"); + + if (!(my_test_connect(mysql1, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + return FAIL; + } + + mysql_get_option(mysql1, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 1, "reconnect != 1"); + + diag("Thread_id before kill: %lu", mysql_thread_id(mysql1)); + mysql_kill(mysql, mysql_thread_id(mysql1)); + + mysql_ping(mysql1); + + rc= mysql_query(mysql1, "SELECT 1 FROM DUAL LIMIT 0"); + check_mysql_rc(rc, mysql1); + diag("Thread_id after kill: %lu", mysql_thread_id(mysql1)); + + mysql_get_option(mysql1, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_UNLESS(reconnect == 1, "reconnect != 1"); + mysql_close(mysql1); + return OK; +} + +int test_conc21(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res= NULL; + MYSQL_ROW row; + char tmp[256]; + unsigned int check_server_version= 0; + int major=0, minor= 0, patch=0; + SKIP_MAXSCALE; + + if (strlen(mysql_get_server_info(mysql)) > 63) + { + diag("server name is too long - skip until rpl hack was removed"); + return SKIP; + } + + rc= mysql_query(mysql, "SELECT @@version"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(res == NULL, "invalid result set"); + + row= mysql_fetch_row(res); + strcpy(tmp, row[0]); + mysql_free_result(res); + + sscanf(tmp, "%d.%d.%d", &major, &minor, &patch); + + check_server_version= major * 10000 + minor * 100 + patch; + + FAIL_IF(mysql_get_server_version(mysql) != check_server_version, "Numeric server version mismatch"); + FAIL_IF(strcmp(mysql_get_server_info(mysql), tmp) != 0, "String server version mismatch"); + return OK; +} + +int test_conc26(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "ascii"); + + FAIL_IF(my_test_connect(mysql, hostname, "notexistinguser", "password", schema, port, socketname, CLIENT_REMEMBER_OPTIONS), + "Error expected"); + FAIL_IF(!mysql->options.charset_name || strcmp(mysql->options.charset_name, "ascii") != 0, + "expected charsetname=ascii"); + mysql_close(mysql); + + mysql= mysql_init(NULL); + FAIL_IF(my_test_connect(mysql, hostname, "notexistinguser", "password", schema, port, socketname, 0), + "Error expected"); + FAIL_IF(mysql->options.charset_name, "Error: options not freed"); + mysql_close(mysql); + + return OK; +} + +int test_connection_timeout(MYSQL *unused __attribute__((unused))) +{ + unsigned int timeout= 5; + time_t start, elapsed; + MYSQL *mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + start= time(NULL); + if (my_test_connect(mysql, "192.168.1.101", "notexistinguser", "password", schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Error expected - maybe you have to change hostname"); + return FAIL; + } + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + mysql_close(mysql); + FAIL_IF((unsigned int)elapsed > 2 * timeout, "timeout ignored"); + return OK; +} + +int test_connection_timeout2(MYSQL *unused __attribute__((unused))) +{ + unsigned int timeout= 5; + time_t start, elapsed; + MYSQL *mysql; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + SKIP_TLS; + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=SLEEP(7)"); + start= time(NULL); + if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + diag("timeout error expected"); + return FAIL; + } + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + mysql_close(mysql); + FAIL_IF((unsigned int)elapsed > 2 * timeout, "timeout ignored"); + return OK; +} + +int test_connection_timeout3(MYSQL *unused __attribute__((unused))) +{ + unsigned int timeout= 5; + unsigned int read_write_timeout= 10; + int rc; + time_t start, elapsed; + MYSQL *mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (unsigned int *)&read_write_timeout); + mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (unsigned int *)&read_write_timeout); + mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=SLEEP(6)"); + start= time(NULL); + if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("timeout error expected"); + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + return FAIL; + } + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + FAIL_IF((unsigned int)elapsed > timeout + 1, "timeout ignored"); + + mysql_close(mysql); + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (unsigned int *)&read_write_timeout); + mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (unsigned int *)&read_write_timeout); + + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + + start= time(NULL); + rc= mysql_query(mysql, "SET @a:=SLEEP(12)"); + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + FAIL_IF(!rc, "timeout expected"); + mysql_close(mysql); + return OK; +} + + +/* test should run with valgrind */ +static int test_conc118(MYSQL *mysql) +{ + int rc; + my_bool reconnect= 1; + SKIP_SKYSQL; + SKIP_MAXSCALE; + + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + + mysql->options.unused_1= 1; + + rc= mysql_kill(mysql, mysql_thread_id(mysql)); + + mysql_ping(mysql); + + rc= mysql_query(mysql, "SET @a:=1"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql->options.unused_1 != 1, "options got lost"); + + rc= mysql_kill(mysql, mysql_thread_id(mysql)); + + mysql_ping(mysql); + rc= mysql_query(mysql, "SET @a:=1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_wrong_bind_address(MYSQL *unused __attribute__((unused))) +{ + const char *bind_addr= "100.188.111.112"; + MYSQL *mysql; + + if (!hostname || !strcmp(hostname, "localhost")) + { + diag("test doesn't work with unix sockets"); + return SKIP; + } + + mysql= mysql_init(NULL); + + mysql_options(mysql, MYSQL_OPT_BIND, bind_addr); + if (my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error expected"); + mysql_close(mysql); + return FAIL; + } + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return OK; +} + +static int test_bind_address(MYSQL *my) +{ + MYSQL *mysql; + char *bind_addr= getenv("MYSQL_TEST_BINDADDR"); + char query[128]; + int rc; + + SKIP_SKYSQL; + + if (!hostname || !strcmp(hostname, "localhost")) + { + diag("test doesn't work with unix sockets"); + return SKIP; + } + + sprintf(query, "DROP USER '%s'@'%s'", username, bind_addr); + rc= mysql_query(my, query); + + sprintf(query, "CREATE USER '%s'@'%s' IDENTIFIED BY '%s'", username, bind_addr, password); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + sprintf(query, "GRANT ALL ON %s.* TO '%s'@'%s'", schema, username, bind_addr); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + if (!bind_addr) + { + diag("No bind address specified"); + return SKIP; + } + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_BIND, bind_addr); + + if (!my_test_connect(mysql, bind_addr, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s\n", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + diag("%s", mysql_get_host_info(mysql)); + mysql_close(mysql); + return OK; +} + +static int test_get_options(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + int options_int[]= {MYSQL_OPT_CONNECT_TIMEOUT, MYSQL_OPT_LOCAL_INFILE, + MYSQL_OPT_PROTOCOL, MYSQL_OPT_READ_TIMEOUT, MYSQL_OPT_WRITE_TIMEOUT, 0}; + my_bool options_bool[]= {MYSQL_OPT_RECONNECT, MYSQL_REPORT_DATA_TRUNCATION, + MYSQL_OPT_COMPRESS, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_SECURE_AUTH, +#ifdef _WIN32 + MYSQL_OPT_NAMED_PIPE, +#endif + 0}; + int options_char[]= {MYSQL_READ_DEFAULT_FILE, MYSQL_READ_DEFAULT_GROUP, MYSQL_SET_CHARSET_NAME, + MYSQL_OPT_SSL_KEY, MYSQL_OPT_SSL_CA, MYSQL_OPT_SSL_CERT, MYSQL_OPT_SSL_CAPATH, + MYSQL_OPT_SSL_CIPHER, MYSQL_OPT_BIND, MARIADB_OPT_SSL_FP, MARIADB_OPT_SSL_FP_LIST, + MARIADB_OPT_TLS_PASSPHRASE, 0}; + + const char *init_command[3]= {"SET @a:=1", "SET @b:=2", "SET @c:=3"}; + int elements= 0; + char **command; + + + int intval[2]= {1, 0}; + my_bool boolval[2]= {1, 0}; + const char *char1= "test"; + char *char2; + int i; + MYSQL *userdata; + const char *attr_key[] = {"foo1", "foo2", "foo3"}; + const char *attr_val[] = {"bar1", "bar2", "bar3"}; + char **key, **val; + + for (i=0; i < (int)(sizeof(options_int)/sizeof(int)); i++) + { + mysql_options(mysql, options_int[i], &intval[0]); + intval[1]= 0; + mysql_get_optionv(mysql, options_int[i], &intval[1]); + FAIL_IF(intval[0] != intval[1], "mysql_get_optionv (int) failed"); + } + for (i=0; options_bool[i]; i++) + { + mysql_options(mysql, options_bool[i], &boolval[0]); + intval[1]= 0; + mysql_get_optionv(mysql, options_bool[i], &boolval[1]); + FAIL_IF(boolval[0] != boolval[1], "mysql_get_optionv (my_bool) failed"); + } + for (i=0; options_char[i]; i++) + { + mysql_options(mysql, options_char[i], char1); + char2= NULL; + mysql_get_optionv(mysql, options_char[i], (void *)&char2); + if (options_char[i] != MYSQL_SET_CHARSET_NAME) + FAIL_IF(strcmp(char1, char2), "mysql_get_optionv (char) failed"); + } + + for (i=0; i < 3; i++) + mysql_options(mysql, MYSQL_INIT_COMMAND, init_command[i]); + + mysql_get_optionv(mysql, MYSQL_INIT_COMMAND, &command, &elements); + FAIL_IF(elements != 3, "expected 3 elements"); + for (i=0; i < 3; i++) + FAIL_IF(strcmp(init_command[i], command[i]), "wrong init command"); + for (i=0; i < 3; i++) + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, attr_key[i], attr_val[i]); + + mysql_get_optionv(mysql, MYSQL_OPT_CONNECT_ATTRS, NULL, NULL, &elements); + FAIL_IF(elements != 3, "expected 3 connection attributes"); + + key= (char **)malloc(sizeof(char *) * elements); + val= (char **)malloc(sizeof(char *) * elements); + + mysql_get_optionv(mysql, MYSQL_OPT_CONNECT_ATTRS, &key, &val, &elements); + for (i=0; i < elements; i++) + { + diag("%s => %s", key[i], val[i]); + } + + free(key); + free(val); + + mysql_optionsv(mysql, MARIADB_OPT_USERDATA, "my_app", (void *)mysql); + mysql_get_optionv(mysql, MARIADB_OPT_USERDATA, (char *)"my_app", &userdata); + + FAIL_IF(mysql != userdata, "wrong userdata"); + mysql_close(mysql); + return OK; +} + +static int test_sess_track_db(MYSQL *mysql) +{ + int rc; + const char *data; + size_t len; + char tmp_str[512]; + + + if (!(mysql->server_capabilities & CLIENT_SESSION_TRACKING)) + { + diag("Server doesn't support session tracking (cap=%lu)", mysql->server_capabilities); + return SKIP; + } + + rc= mysql_query(mysql, "USE mysql"); + check_mysql_rc(rc, mysql); + FAIL_IF(strcmp(mysql->db, "mysql"), "Expected new schema 'mysql'"); + + FAIL_IF(mysql_session_track_get_first(mysql, SESSION_TRACK_SCHEMA, &data, &len), + "session_track_get_first failed"); + FAIL_IF(strncmp(data, "mysql", len), "Expected new schema 'mysql'"); + + sprintf(tmp_str, "USE %s", schema); + rc= mysql_query(mysql, tmp_str); + check_mysql_rc(rc, mysql); + + sprintf(tmp_str, "Expected new schema '%s'.", schema); + + FAIL_IF(strcmp(mysql->db, schema), tmp_str); + + FAIL_IF(mysql_session_track_get_first(mysql, SESSION_TRACK_SCHEMA, &data, &len), + "session_track_get_first failed"); + FAIL_IF(strncmp(data, schema, len), tmp_str); + + if (mysql_get_server_version(mysql) >= 100300) + { + diag("charset: %s", mysql->charset->csname); + rc= mysql_query(mysql, "SET NAMES ascii"); + check_mysql_rc(rc, mysql); + if (!mysql_session_track_get_first(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len)) + do { + printf("# SESSION_TRACK_VARIABLES: %*.*s\n", (int)len, (int)len, data); + } while (!mysql_session_track_get_next(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len)); + diag("charset: %s", mysql->charset->csname); + FAIL_IF(strcmp(mysql->charset->csname, "ascii"), + "Expected charset 'ascii'"); + + rc= mysql_query(mysql, "SET NAMES latin1"); + check_mysql_rc(rc, mysql); + FAIL_IF(strcmp(mysql->charset->csname, "latin1"), "Expected charset 'latin1'"); + } + rc= mysql_query(mysql, "CREATE PROCEDURE p1() " + "BEGIN " + "SET @@autocommit=0; " + "SET NAMES utf8; " + "SET session auto_increment_increment=2; " + "END "); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CALL p1()"); + check_mysql_rc(rc, mysql); + + if (!mysql_session_track_get_first(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len)) + do { + printf("# SESSION_TRACK_VARIABLES: %*.*s\n", (int)len, (int)len, data); + } while (!mysql_session_track_get_next(mysql, SESSION_TRACK_SYSTEM_VARIABLES, &data, &len)); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc496(MYSQL *mysql) +{ + int rc; + const char *data; + size_t len; + + rc= mysql_query(mysql, "set @@session.session_track_transaction_info=STATE"); + + if (rc && mysql_errno(mysql) == ER_UNKNOWN_SYSTEM_VARIABLE) + { + diag("session_track_transaction_info not supported"); + return SKIP; + } + + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "BEGIN"); + check_mysql_rc(rc, mysql); + if (!mysql_session_track_get_first(mysql, SESSION_TRACK_TRANSACTION_STATE, &data, &len)) + do { + FAIL_IF(len != 8, "expected 8 bytes"); + FAIL_IF(data[0] != 'T', "expected transaction"); + } while (!mysql_session_track_get_next(mysql, SESSION_TRACK_TRANSACTION_STATE, &data, &len)); + + rc= mysql_query(mysql, "CREATE TEMPORARY TABLE t1(a int) ENGINE=InnoDB"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "COMMIT"); + + check_mysql_rc(rc, mysql); + + if (!mysql_session_track_get_first(mysql, SESSION_TRACK_TRANSACTION_STATE, &data, &len)) + do { + FAIL_IF(len != 8, "expected 8 bytes"); + FAIL_IF(data[0] != '_', "expected underscore"); + } while (!mysql_session_track_get_next(mysql, SESSION_TRACK_TRANSACTION_STATE, &data, &len)); + + return OK; +} + + +static int test_unix_socket_close(MYSQL *unused __attribute__((unused))) +{ +#ifdef _WIN32 + diag("test does not run on Windows"); + return SKIP; +#else + MYSQL *mysql= mysql_init(NULL); + FILE *fp; + int i; + + SKIP_SKYSQL; + SKIP_TRAVIS(); + + if (!(fp= fopen("./dummy_sock", "w"))) + { + diag("couldn't create dummy socket"); + return FAIL; + } + fclose(fp); + + for (i=0; i < 10000; i++) + { + my_test_connect(mysql, "localhost", "user", "passwd", NULL, 0, "./dummy_sock", 0); + /* check if we run out of sockets */ + if (mysql_errno(mysql) == 2001) + { + diag("out of sockets after %d attempts", i); + mysql_close(mysql); + return FAIL; + } + } + mysql_close(mysql); + return OK; +#endif +} + + +static int test_reset(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res; + + if (mysql_get_server_version(mysql) < 100200) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1),(2),(3)"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != 3, "Expected 3 rows"); + + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != ~(my_ulonglong)0, "Expected 0 rows"); + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT 1 FROM DUAL"); + FAIL_IF(!rc, "Error expected"); + + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(res, "expected no result"); + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + res= mysql_use_result(mysql); + FAIL_IF(!res, "expected result"); + + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_fetch_row(res), "expected error"); + + mysql_free_result(res); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_auth256(MYSQL *my) +{ + MYSQL *mysql= mysql_init(NULL); + int rc; + MYSQL_RES *res; + my_ulonglong num_rows= 0; + char query[1024]; + SKIP_MAXSCALE; + + if (IS_SKYSQL(hostname)) + return SKIP; + + // xpand doesn't have information_schema.plugins + SKIP_XPAND; + + if (!mysql_client_find_plugin(mysql, "sha256_password", MYSQL_CLIENT_AUTHENTICATION_PLUGIN)) + { + diag("sha256_password plugin not available"); + mysql_close(mysql); + return SKIP; + } + + rc= mysql_query(my, "SELECT * FROM information_schema.plugins where plugin_name='sha256_password'"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(my); + num_rows= mysql_num_rows(res); + mysql_free_result(res); + + if (!num_rows) + { + diag("server doesn't support sha256 authentication"); + mysql_close(mysql); + return SKIP; + } + + rc= mysql_query(my, "DROP USER IF EXISTS sha256user@localhost"); + check_mysql_rc(rc, mysql); + + sprintf(query, "CREATE user 'sha256user'@'%s' identified with sha256_password by 'foo'", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + if (!my_test_connect(mysql, hostname, "sha256user", "foo", NULL, port, socketname, 0)) + { + diag("error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + mysql_close(mysql); + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_SERVER_PUBLIC_KEY, "rsa_public_key.pem"); + if (!my_test_connect(mysql, hostname, "sha256user", "foo", NULL, port, socketname, 0)) + { + diag("error: %s", mysql_error(mysql)); + diag("host: %s", this_host); + mysql_close(mysql); + return FAIL; + } + mysql_close(mysql); + sprintf(query, "DROP USER 'sha256user'@'%s'", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_mdev13100(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + int rc; + FILE *fp; + + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + /* [client] group only */ + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + + /* value from client-mariadb group */ + mysql= mysql_init(NULL); + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + +/* values from client-mariadb group */ + mysql= mysql_init(NULL); + +if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=utf8\n"); + fprintf(fp, "[client-mariadb]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + +/* values from mdev-13100 group */ + mysql= mysql_init(NULL); + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-mariadb]\n"); + fprintf(fp, "default-character-set=utf8\n"); + fprintf(fp, "[mdev13100]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "mdev13100"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + +/* values from [programname] group */ + mysql= mysql_init(NULL); + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=utf8\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=utf8\n"); + fprintf(fp, "[client-mariadb]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + diag("character set: %s", mysql_character_set_name(mysql)); + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + + remove("./mdev13100.cnf"); + + return OK; +} + +static int test_conc276(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + int rc; + my_bool val= 1; + + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(mysql, MYSQL_OPT_RECONNECT, &val); + + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, 0)) + { + diag("Connection failed. Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + + rc= mariadb_reconnect(mysql); + check_mysql_rc(rc, mysql); + + diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + /* this shouldn't crash anymore */ + rc= mysql_query(mysql, "SET @a:=1"); + check_mysql_rc(rc, mysql); + + mysql_close(mysql); + return OK; +} + +static int test_expired_pw(MYSQL *my) +{ + MYSQL *mysql; + int rc; + char query[512]; + unsigned char expire= 1; + + if (mariadb_connection(my) || + !(my->server_capabilities & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)) + { + diag("Server doesn't support password expiration"); + return SKIP; + } + sprintf(query, "DROP USER 'foo'@'%s'", this_host); + rc= mysql_query(my, query); + + sprintf(query, "CREATE USER 'foo'@'%s' IDENTIFIED BY 'foo'", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + sprintf(query, "GRANT ALL ON *.* TO 'foo'@'%s'", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + sprintf(query, "ALTER USER 'foo'@'%s' PASSWORD EXPIRE", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + mysql= mysql_init(NULL); + + my_test_connect(mysql, hostname, "foo", "foo", schema, + port, socketname, 0); + + FAIL_IF(!mysql_errno(mysql), "Error expected"); + mysql_close(mysql); + + mysql= mysql_init(NULL); + mysql_optionsv(mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, &expire); + + my_test_connect(mysql, hostname, "foo", "foo", schema, + port, socketname, 0); + + /* we should be in sandbox mode now, only set commands should be allowed */ + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + FAIL_IF(!rc, "Error expected (we are in sandbox mode"); + + diag("error: %d %s", mysql_errno(mysql), mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql) != ER_MUST_CHANGE_PASSWORD && + mysql_errno(mysql) != ER_MUST_CHANGE_PASSWORD_LOGIN, "Error 1820/1862 expected"); + + mysql_close(mysql); + + sprintf(query, "DROP USER 'foo'@'%s'", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + return OK; +} + +static int test_conc315(MYSQL *mysql) +{ + int rc; + const char *csname; + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + mysql_get_optionv(mysql, MYSQL_SET_CHARSET_NAME, (void *)&csname); + diag("csname=%s", csname); + FAIL_UNLESS(strcmp(csname, MARIADB_DEFAULT_CHARSET) == 0, "Wrong default character set"); + + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + mysql_get_optionv(mysql, MYSQL_SET_CHARSET_NAME, (void *)&csname); + FAIL_UNLESS(strcmp(csname, MARIADB_DEFAULT_CHARSET) == 0, "Wrong default character set"); + return OK; +} +#ifndef WIN32 +static int test_conc317(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + my_bool reconnect = 0; + FILE *fp= NULL; + const char *env= getenv("MYSQL_TMP_DIR"); + char cnf_file1[FN_REFLEN + 1]; + + SKIP_SKYSQL; + + if (travis_test) + return SKIP; + + if (!env) + env= "/tmp"; + + setenv("HOME", env, 1); + + snprintf(cnf_file1, FN_REFLEN, "%s%c.my.cnf", env, FN_LIBCHAR); + + FAIL_IF(!access(cnf_file1, R_OK), "access"); + + mysql= mysql_init(NULL); + fp= fopen(cnf_file1, "w"); + FAIL_IF(!fp, "fopen"); + + fprintf(fp, "[client]\ndefault-character-set = latin2\nreconnect= 1\n"); + fclose(fp); + + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0); + + remove(cnf_file1); + + FAIL_IF(strcmp(mysql_character_set_name(mysql), "latin2"), "expected charset latin2"); + mysql_get_optionv(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_IF(reconnect != 1, "expected reconnect=1"); + mysql_close(mysql); + return OK; +} + +static int test_conc327(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + my_bool reconnect = 0; + FILE *fp1= NULL, *fp2= NULL; + const char *env= getenv("MYSQL_TMP_DIR"); + char cnf_file1[FN_REFLEN + 1]; + char cnf_file2[FN_REFLEN + 1]; + + SKIP_SKYSQL; + + if (travis_test) + return SKIP; + + if (!env) + env= "/tmp"; + + setenv("HOME", env, 1); + + snprintf(cnf_file1, FN_REFLEN, "%s%c.my.cnf", env, FN_LIBCHAR); + snprintf(cnf_file2, FN_REFLEN, "%s%c.my.tmp", env, FN_LIBCHAR); + + FAIL_IF(!access(cnf_file1, R_OK), "access"); + + fp1= fopen(cnf_file1, "w"); + fp2= fopen(cnf_file2, "w"); + FAIL_IF(!fp1 || !fp2, "fopen failed"); + + fprintf(fp1, "!include %s\n", cnf_file2); + + fprintf(fp2, "[client]\ndefault-character-set = latin2\nreconnect= 1\n"); + fclose(fp1); + fclose(fp2); + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0); + + remove(cnf_file1); + remove(cnf_file2); + + diag("new charset: %s", mysql->options.charset_name); + FAIL_IF(strcmp(mysql_character_set_name(mysql), "latin2"), "expected charset latin2"); + mysql_get_optionv(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_IF(reconnect != 1, "expected reconnect=1"); + mysql_close(mysql); + + snprintf(cnf_file1, FN_REFLEN, "%s%cmy.cnf", env, FN_LIBCHAR); + fp1= fopen(cnf_file1, "w"); + fp2= fopen(cnf_file2, "w"); + FAIL_IF(!fp1 || !fp2, "fopen failed"); + + fprintf(fp2, "!includedir %s\n", env); + + fprintf(fp1, "[client]\ndefault-character-set = latin2\nreconnect= 1\n"); + fclose(fp1); + fclose(fp2); + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, cnf_file2); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0); + + remove(cnf_file1); + remove(cnf_file2); + + FAIL_IF(strcmp(mysql_character_set_name(mysql), "latin2"), "expected charset latin2"); + mysql_get_optionv(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_IF(reconnect != 1, "expected reconnect=1"); + mysql_close(mysql); + + return OK; +} +#endif + +static int test_conc332(MYSQL *unused __attribute__((unused))) +{ + int rc; + MYSQL *mysql= mysql_init(NULL); + int server_status1, server_status2; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4"); + + my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0); + + FAIL_IF(mysql_errno(mysql), "Error during connect"); + + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_STATUS, &server_status1); + diag("server_status: %d", server_status1); + + if (server_status1 & SERVER_STATUS_AUTOCOMMIT) + rc= mysql_query(mysql, "SET autocommit= 0"); + else + rc= mysql_query(mysql, "SET autocommit= 1"); + check_mysql_rc(rc, mysql); + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_STATUS, &server_status2); + diag("server_status after changing autocommit: %d", server_status2); + + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_STATUS, &server_status2); + diag("server_status after mysql_change_user: %d", server_status2); + if (server_status1 != server_status2) + { + diag("Expected server_status %d instead of %d", server_status1, server_status2); + mysql_close(mysql); + return FAIL; + } + mysql_close(mysql); + return OK; +} + +static int test_conc351(MYSQL *unused __attribute__((unused))) +{ + int rc; + const char *data; + size_t len; + MYSQL *mysql= mysql_init(NULL); + ulong capabilities= 0; + + my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0); + + FAIL_IF(mysql_errno(mysql), "Error during connect"); + + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_CAPABILITIES, &capabilities); + if (!(capabilities & CLIENT_SESSION_TRACKING)) + { + mysql_close(mysql); + diag("Server doesn't support session tracking (cap=%lu)", mysql->server_capabilities); + return SKIP; + } + rc= mysql_query(mysql, "USE mysql"); + check_mysql_rc(rc, mysql); + FAIL_IF(strcmp(mysql->db, "mysql"), "Expected new schema 'mysql'"); + + FAIL_IF(mysql_session_track_get_first(mysql, SESSION_TRACK_SCHEMA, &data, &len), "expected session track schema"); + + rc= mysql_query(mysql, "SET @a:=1"); + check_mysql_rc(rc, mysql); + + FAIL_IF(!mysql_session_track_get_first(mysql, SESSION_TRACK_SCHEMA, &data, &len), "expected no schema tracking information"); + + mysql_close(mysql); + return OK; +} + +static int test_conc312(MYSQL *my) +{ + int rc; + char query[1024]; + MYSQL *mysql; + + sprintf(query, "DROP USER 'foo'@'%s'", this_host); + rc= mysql_query(my, query); + + sprintf(query, "CREATE USER 'foo'@'%s' IDENTIFIED WITH caching_sha2_password BY 'foo'", this_host); + rc= mysql_query(my, query); + + if (rc) + { + diag("Error: %s", mysql_error(my)); + diag("caching_sha256_password not supported"); + return SKIP; + } + + sprintf(query, "GRANT ALL ON %s.* TO 'foo'@'%s'", schema, this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, my); + + mysql= mysql_init(NULL); + if (!my_test_connect(mysql, hostname, "foo", "foo", schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + + mysql_close(mysql); + + sprintf(query, "DROP USER 'foo'@'%s'", this_host); + rc= mysql_query(my, query); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc366(MYSQL *mysql) +{ + char query[1024]; + int rc; + MYSQL *my; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + { + diag("feature not supported by MySQL server"); + return SKIP; + } + + /* check if ed25519 plugin is available */ + if (!mysql_client_find_plugin(mysql, "client_ed25519", MYSQL_CLIENT_AUTHENTICATION_PLUGIN)) + { + diag("client_ed25519 plugin not available"); + return SKIP; + } + + rc= mysql_query(mysql, "INSTALL SONAME 'auth_ed25519'"); + if (rc) + { + diag("feature not supported, ed25519 plugin not available"); + return SKIP; + } + + if (mysql_get_server_version(mysql) < 100400) { + sprintf(query, "CREATE OR REPLACE USER 'ede'@'%s' IDENTIFIED VIA ed25519 USING '6aW9C7ENlasUfymtfMvMZZtnkCVlcb1ssxOLJ0kj/AA'", this_host); + } else { + sprintf(query, "CREATE OR REPLACE USER 'ede'@'%s' IDENTIFIED VIA ed25519 USING PASSWORD('MySup8%%rPassw@ord')", this_host); + } + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + sprintf(query, "GRANT ALL ON %s.* TO 'ede'@'%s'", schema, this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + my= mysql_init(NULL); + if (plugindir) + mysql_options(my, MYSQL_PLUGIN_DIR, plugindir); + if (!my_test_connect(my, hostname, "ede", "MySup8%rPassw@ord", schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(my)); + return FAIL; + } + mysql_close(my); + + sprintf(query, "DROP USER 'ede'@'%s'", this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + sprintf(query, "UNINSTALL SONAME 'auth_ed25519'"); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc392(MYSQL *mysql) +{ + int rc; + const char *data; + size_t len; + ulong capabilities= 0; + + SKIP_MYSQL(mysql); + + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_CAPABILITIES, &capabilities); + if (!(capabilities & CLIENT_SESSION_TRACKING)) + { + diag("Server doesn't support session tracking (cap=%lu)", mysql->server_capabilities); + return SKIP; + } + + rc= mysql_query(mysql, "set session_track_state_change=1"); + check_mysql_rc(rc, mysql); + + if (mysql_session_track_get_first(mysql, SESSION_TRACK_STATE_CHANGE, &data, &len)) + { + diag("session_track_get_first failed"); + return FAIL; + } + + FAIL_IF(len != 1, "Expected length 1"); + return OK; +} + +static int test_conc443(MYSQL *my __attribute__((unused))) +{ + my_bool x= 1; + unsigned long thread_id= 0; + char query[128]; + MYSQL_RES *result; + MYSQL_ROW row; + int rc; + + MYSQL *mysql= mysql_init(NULL); + + SKIP_MAXSCALE; + + mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=3"); + mysql_options(mysql, MYSQL_OPT_RECONNECT, &x); + + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Connection failed. Error: %s", mysql_error(mysql)); + mysql_close(mysql); + } + + thread_id= mysql_thread_id(mysql); + + sprintf(query, "KILL %lu", thread_id); + rc= mysql_query(mysql, query); + + sleep(3); + + rc= mysql_ping(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT @a"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_thread_id(mysql) == thread_id, "Expected different thread id"); + + result= mysql_store_result(mysql); + if (!result) + return FAIL; + row= mysql_fetch_row(result); + FAIL_IF(strcmp(row[0],"3"), "Wrong result"); + + mysql_free_result(result); + mysql_close(mysql); + + return OK; +} + +static int test_default_auth(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_DEFAULT_AUTH, "mysql_clear_password"); + + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Connection failed. Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + mysql_close(mysql); + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_DEFAULT_AUTH, "caching_sha2_password"); + + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Connection failed. Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + + } + mysql_close(mysql); + return OK; +} + +static int test_gtid(MYSQL *mysql) +{ + int rc; + const char *data; + size_t len; + + if (is_mariadb) + return SKIP; + // https://jira.mariadb.org/browse/XPT-182 + SKIP_XPAND; + + rc= mysql_query(mysql, "SET @@session.session_track_state_change=1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET @@session.session_track_gtids=OWN_GTID"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "BEGIN"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + if (!mysql_session_track_get_first(mysql, SESSION_TRACK_GTIDS, &data, &len)) + do { + printf("# SESSION_TRACK_GTIDS: %*.*s\n", (int)len, (int)len, data); + } while (!mysql_session_track_get_next(mysql, SESSION_TRACK_GTIDS, &data, &len)); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + + if (!mysql_session_track_get_first(mysql, SESSION_TRACK_GTIDS, &data, &len)) + do { + printf("# SESSION_TRACK_GTIDS: %*.*s\n", (int)len, (int)len, data); + } while (!mysql_session_track_get_next(mysql, SESSION_TRACK_GTIDS, &data, &len)); + + rc= mysql_query(mysql, "COMMIT"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc490(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + + if (!my_test_connect(mysql, hostname, username, + password, NULL, port, socketname, CLIENT_CONNECT_WITH_DB)) + { + diag("error: %s\n", mysql_error(mysql)); + return FAIL; + } + mysql_close(mysql); + return OK; +} + +static int test_conc544(MYSQL *mysql) +{ + int rc; + MYSQL *my= mysql_init(NULL); + char query[1024]; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!mysql_client_find_plugin(mysql, "client_ed25519", MYSQL_CLIENT_AUTHENTICATION_PLUGIN)) + { + diag("client_ed25519 plugin not available"); + return SKIP; + } + + rc= mysql_query(mysql, "INSTALL SONAME 'auth_ed25519'"); + if (rc) + { + diag("feature not supported, ed25519 plugin not available"); + return SKIP; + } + + rc= mysql_optionsv(my, MARIADB_OPT_RESTRICTED_AUTH, "client_ed25519"); + check_mysql_rc(rc, mysql); + + if (my_test_connect(my, hostname, username, + password, schema, port, socketname, 0)) + { + diag("error expected (restricted auth)"); + return FAIL; + } + mysql_close(my); + + if (mysql_get_server_version(mysql) < 100400) { + sprintf(query, "CREATE OR REPLACE USER 'ede'@'%s' IDENTIFIED VIA ed25519 USING '6aW9C7ENlasUfymtfMvMZZtnkCVlcb1ssxOLJ0kj/AA'", this_host); + } else { + sprintf(query, "CREATE OR REPLACE USER 'ede'@'%s' IDENTIFIED VIA ed25519 USING PASSWORD('MySup8%%rPassw@ord')", this_host); + } + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + sprintf(query, "GRANT ALL ON %s.* TO 'ede'@'%s'", schema, this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + my= mysql_init(NULL); + if (plugindir) + mysql_optionsv(my, MYSQL_PLUGIN_DIR, plugindir); + mysql_optionsv(my, MARIADB_OPT_RESTRICTED_AUTH, "client_ed25519, mysql_native_password"); + if (!my_test_connect(my, hostname, "ede", "MySup8%rPassw@ord", schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(my)); + return FAIL; + } + mysql_close(my); + + sprintf(query, "DROP USER 'ede'@'%s'", this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + sprintf(query, "UNINSTALL SONAME 'auth_ed25519'"); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conn_str(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + char conn_str[1024]; + int rc=OK; + + snprintf(conn_str, sizeof(conn_str)-1, "host=%s;user=%s;password={%s};port=%d;socket=%s", + hostname ? hostname : "localhost", username ? username : "", + password ? password : "", + port, socketname ? socketname : ""); + + /* SkySQL requires secure connection */ + if (IS_SKYSQL(hostname)) + { + strcat(conn_str, ";ssl_enforce=1"); + } + + if (mariadb_connect(mysql, conn_str)) + { + diag("host: %s", mysql->host); + diag("user: %s", mysql->user); + diag("cipher: %s", mysql_get_ssl_cipher(mysql)); + } else + { + diag("error: %s", mysql_error(mysql)); + rc= FAIL; + } + mysql_close(mysql); + return rc; +} + +static int test_conn_str_1(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql; + FILE *fp; + int rc; + char conn_str[1024]; + mysql= mysql_init(NULL); + + if (!(fp= fopen("./conc274.cnf", "w"))) + return FAIL; + + sprintf(conn_str, "connection=host=%s;user=%s;password=%s;port=%d;ssl_enforce=1;socket=%s", + hostname ? hostname : "localhost", username ? username : "", + password ? password : "", ssl_port, socketname ? socketname : ""); + + fprintf(fp, "[client]\n"); + fprintf(fp, "%s\n", conn_str); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./conc274.cnf"); + check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, NULL, NULL, NULL, NULL, 0, NULL, 0)) + { + diag("Error: %s", mysql_error(mysql)); + remove("./conc274.cnf"); + return FAIL; + } + remove("./conc274.cnf"); + + if (!mysql_get_ssl_cipher(mysql)) + { + diag("Error: No TLS connection"); + return FAIL; + } + diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + mysql_close(mysql); + return OK; +} + +static int test_conc365(MYSQL *my __attribute__((unused))) +{ + int rc= OK; + MYSQL *mysql= mysql_init(NULL); + char tmp[1024]; + + snprintf(tmp, sizeof(tmp) - 1, + "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d;socket=%s", + hostname ? hostname : "localhost", username ? username : "", password ? password : "", + port, socketname ? socketname : ""); + + if (IS_SKYSQL(hostname)) + strcat(tmp, ";ssl_enforce=1"); + + if (!mariadb_connect(mysql, tmp)) + rc= FAIL; + + mysql_close(mysql); + + if (rc) + return rc; + + mysql= mysql_init(NULL); + snprintf(tmp, sizeof(tmp) -1, "127.0.0.1:3300,%s:%d", hostname ? hostname : "localhost", port); + if (!my_test_connect(mysql, tmp, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + rc= FAIL; + } + + mysql_close(mysql); + + if (rc) + return rc; + + mysql= mysql_init(NULL); + mysql_options(mysql, MARIADB_OPT_HOST, tmp); + if (!my_test_connect(mysql, NULL, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + rc= FAIL; + } + + mysql_close(mysql); + return rc; +} + +static int test_conc365_reconnect(MYSQL *my) +{ + int rc= OK; + MYSQL *mysql= mysql_init(NULL); + char tmp[1024]; + my_bool reconnect= 1; + SKIP_MAXSCALE; + + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + + if (IS_SKYSQL(hostname)) + { + snprintf(tmp, sizeof(tmp) - 1, + "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d;socket=%s;ssl_enforce=1", + hostname ? hostname : "localhost", username ? username : "", password ? password : "", + ssl_port, socketname ? socketname : ""); + } else { + snprintf(tmp, sizeof(tmp) - 1, + "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d;socket=%s", + hostname ? hostname : "localhost", username ? username : "", password ? password : "", + port, socketname ? socketname : ""); + } + + if (!my_test_connect(mysql, tmp, username, + password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Error: %s", mysql_error(mysql)); + rc= FAIL; + } + + sprintf(tmp, "KILL %ld", mysql_thread_id(mysql)); + + rc= mysql_query(my, tmp); + check_mysql_rc(rc, my); + + sleep(3); + rc= mysql_ping(mysql); + check_mysql_rc(rc, my); + + mysql_close(mysql); + return rc; +} + +struct st_callback { + char autocommit; + char database[64]; + char charset[64]; +}; + +void my_status_callback(void *ptr, enum enum_mariadb_status_info type, ...) +{ + va_list ap; + struct st_callback *data= (struct st_callback *)ptr; + va_start(ap, type); + + switch(type) { + case STATUS_TYPE: + { + int status= va_arg(ap, int); + data->autocommit= status & SERVER_STATUS_AUTOCOMMIT; + } + break; + case SESSION_TRACK_TYPE: + { + enum enum_session_state_type track_type= va_arg(ap, enum enum_session_state_type); + switch (track_type) { + case SESSION_TRACK_SCHEMA: + { + MARIADB_CONST_STRING *str= va_arg(ap, MARIADB_CONST_STRING *); + strncpy(data->database, str->str, str->length); + } + break; + case SESSION_TRACK_SYSTEM_VARIABLES: + { + MARIADB_CONST_STRING *key= va_arg(ap, MARIADB_CONST_STRING *); + MARIADB_CONST_STRING *val= va_arg(ap, MARIADB_CONST_STRING *); + + if (!strncmp(key->str, "character_set_client", key->length)) + { + strncpy(data->charset, val->str, val->length); + } + } + break; + default: + break; + } + } + default: + break; + } + va_end(ap); +} + +static int test_status_callback(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + char tmp[64]; + int rc; + struct st_callback data= {0,"", ""}; + + rc= mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, my_status_callback, &data); + + if (!my_test_connect(mysql, hostname, username, + password, NULL, port, socketname, 0)) + { + diag("error1: %s", mysql_error(mysql)); + return FAIL; + } + + rc= mysql_autocommit(mysql, 0); + check_mysql_rc(rc, mysql); + rc= mysql_autocommit(mysql, 1); + check_mysql_rc(rc, mysql); + + if (!data.autocommit) + { + diag("autocommit not set"); + return FAIL; + } + diag("-------------------------"); + + sprintf(tmp, "USE %s", schema); + rc= mysql_query(mysql, tmp); + check_mysql_rc(rc, mysql); + + if (strcmp(data.database, schema)) + { + diag("Expected database: %s instead of %s", schema, data.database); + return FAIL; + } + + rc= mysql_query(mysql, "SET NAMES latin1"); + check_mysql_rc(rc, mysql); + + if (strcmp(data.charset, "latin1")) + { + diag("Expected charset latin1 instead of %s", data.charset); + return FAIL; + } + + mysql_close(mysql); + return OK; +} + +static int test_conc632(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + int rc; + + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Connection failed. Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "DROP PROCEDURE conc632"); + + rc= mysql_query(mysql, "CREATE PROCEDURE conc632() " + "BEGIN " + " SELECT 1;" + " SELECT 2;" + "END"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CALL conc632()"); + check_mysql_rc(rc, mysql); + + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_ping(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP PROCEDURE conc632"); + check_mysql_rc(rc, mysql); + + mysql_close(mysql); + return OK; +} + +struct my_tests_st my_tests[] = { + {"test_conc632", test_conc632, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_status_callback", test_status_callback, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc365_reconnect", test_conc365_reconnect, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conn_str", test_conn_str, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conn_str_1", test_conn_str_1, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc544", test_conc544, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc490", test_conc490, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_gtid", test_gtid, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc496", test_conc496, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_default_auth", test_default_auth, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc443", test_conc443, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc366", test_conc366, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc392", test_conc392, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc312", test_conc312, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc351", test_conc351, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc332", test_conc332, TEST_CONNECTION_NONE, 0, NULL, NULL}, +#ifndef WIN32 + {"test_conc327", test_conc327, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc317", test_conc317, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, +#endif + {"test_conc315", test_conc315, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_expired_pw", test_expired_pw, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc276", test_conc276, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_mdev13100", test_mdev13100, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_auth256", test_auth256, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_reset", test_reset, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_unix_socket_close", test_unix_socket_close, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_sess_track_db", test_sess_track_db, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_get_options", test_get_options, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_wrong_bind_address", test_wrong_bind_address, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bind_address", test_bind_address, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc118", test_conc118, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc66", test_conc66, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug20023", test_bug20023, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_bug31669", test_bug31669, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_bug33831", test_bug33831, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_change_user", test_change_user, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_opt_reconnect", test_opt_reconnect, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_compress", test_compress, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_reconnect", test_reconnect, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc21", test_conc21, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc26", test_conc26, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_connection_timeout", test_connection_timeout, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_connection_timeout2", test_connection_timeout2, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_connection_timeout3", test_connection_timeout3, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/cursor.c b/libmariadb/unittest/libmariadb/cursor.c new file mode 100644 index 00000000..9cdda475 --- /dev/null +++ b/libmariadb/unittest/libmariadb/cursor.c @@ -0,0 +1,1852 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" + +/* helper functions */ +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; + +MYSQL_STMT *open_cursor(MYSQL *mysql, 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, SL(query)); + if (rc) { + diag("Error: %s", mysql_stmt_error(stmt)); + return NULL; + } + mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type); + return stmt; +} + +/* + Create statement handle, prepare it with statement, execute and allocate + fetch buffers. +*/ + +int stmt_fetch_init(MYSQL *mysql, Stmt_fetch *fetch, unsigned int stmt_no_arg, + const char *query_arg) +{ + unsigned long type= CURSOR_TYPE_READ_ONLY; + int rc; + unsigned int i; + MYSQL_RES *metadata; + + /* 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, SL(fetch->query)); + FAIL_IF(rc, mysql_stmt_error(fetch->handle)); + + /* + 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); + FAIL_IF(rc, mysql_stmt_error(fetch->handle)); + + /* 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 */ + return OK; +} + + +int fill_tables(MYSQL *mysql, const char **query_list, unsigned query_count) +{ + int rc; + const char **query; + for (query= query_list; query < query_list + query_count; + ++query) + { + rc= mysql_query(mysql, *query); + check_mysql_rc(rc, mysql); + } + return OK; +} + +int stmt_fetch_fetch_row(Stmt_fetch *fetch) +{ + int rc; + unsigned i; + + if ((rc= mysql_stmt_fetch(fetch->handle)) == 0) + { + ++fetch->row_count; + for (i= 0; i < fetch->column_count; ++i) + { + fetch->out_data[i][fetch->out_data_length[i]]= '\0'; + } + } + else + fetch->is_open= FALSE; + + return rc; +} + +void stmt_fetch_close(Stmt_fetch *fetch) +{ + unsigned i; + + 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); +} + + + +enum fetch_type { USE_ROW_BY_ROW_FETCH= 0, USE_STORE_RESULT= 1 }; + +int fetch_n(MYSQL *mysql, 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; + + for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) + { + if (stmt_fetch_init(mysql, fetch, (unsigned int)(fetch - fetch_array), + query_list[fetch - fetch_array])) + return FAIL; + } + + if (fetch_type == USE_STORE_RESULT) + { + for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) + { + rc= mysql_stmt_store_result(fetch->handle); + FAIL_IF(rc, mysql_stmt_error(fetch->handle)); + } + } + + 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) + error_count++; + } + } + } + for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) + stmt_fetch_close(fetch); + free(fetch_array); + + return (error_count) ? FAIL:OK; +} + +static int test_basic_cursors(MYSQL *mysql) +{ + 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" + }; + + + FAIL_IF(fill_tables(mysql, basic_tables, sizeof(basic_tables)/sizeof(*basic_tables)), "fill_tables failed"); + + FAIL_IF(fetch_n(mysql, queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH), "fetch_n failed"); + FAIL_IF(fetch_n(mysql, queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT), "fetch_n failed"); + return OK; +} + + +static int test_cursors_with_union(MYSQL *mysql) +{ + const char *queries[]= + { + "SELECT t1.name FROM t1 UNION SELECT t2.name FROM t2", + "SELECT t1.id FROM t1 WHERE t1.id < 5" + }; + FAIL_IF(fetch_n(mysql, queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH), "fetch_n failed"); + FAIL_IF(fetch_n(mysql, queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT), "fetch_n failed"); + + return OK; +} + + +static int test_cursors_with_procedure(MYSQL *mysql) +{ + const char *queries[]= + { + "SELECT * FROM t1 procedure analyse()" + }; + SKIP_MYSQL(mysql); + FAIL_IF(fetch_n(mysql, queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH), "fetch_n failed"); + FAIL_IF(fetch_n(mysql, queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT), "fetch_n failed"); + + return OK; +} + +/* + Bug#21206: memory corruption when too many cursors are opened at once + + Memory corruption happens when more than 1024 cursors are open + simultaneously. +*/ +static int test_bug21206(MYSQL *mysql) +{ + int retcode= OK; + + 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; + + FAIL_IF(fill_tables(mysql, create_table, sizeof(create_table) / sizeof(*create_table)), "fill_tables failed"); + + for (fetch= fetch_array; fetch < fetch_array + cursor_count; ++fetch) + { + if ((retcode= stmt_fetch_init(mysql, fetch, (unsigned int)(fetch - fetch_array), query))) + break; + } + + for (fetch= fetch_array; fetch < fetch_array + cursor_count; ++fetch) + stmt_fetch_close(fetch); + + free(fetch_array); + + return retcode; +} + +static int test_bug10729(MYSQL *mysql) +{ + 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; + + 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')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + type= (ulong) CURSOR_TYPE_READ_ONLY; + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type); + check_stmt_rc(rc, stmt); + stmt_text= "select name from t1"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + while ((rc= mysql_stmt_fetch(stmt)) == 0) + { + FAIL_UNLESS(strcmp(a, name_array[row_no]) == 0, "a != name_array[row_no]"); + ++row_no; + } + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + } + rc= mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#10736: cursors and subqueries, memroot management */ + +static int test_bug10736(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + char a[21]; + int rc; + const char *stmt_text; + int i= 0; + ulong type; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (id integer not null primary key," + "name VARCHAR(20) NOT NULL)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 (id, name) values " + "(1, 'aaa'), (2, 'bbb'), (3, 'ccc')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + + type= (ulong) CURSOR_TYPE_READ_ONLY; + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type); + check_stmt_rc(rc, stmt); + stmt_text= "select name from t1 where name=(select name from t1 where id=2)"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + while ((rc= mysql_stmt_fetch(stmt)) == 0); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + } + rc= mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#10794: cursors, packets out of order */ + +static int test_bug10794(MYSQL *mysql) +{ + 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; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (id integer not null primary key," + "name varchar(20) not null)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + stmt_text= "insert into t1 (id, name) values (?, ?)"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + for (i= 0; i < 42; i++) + { + id_val= (i+1)*10; + sprintf(a, "a%d", i); + a_len= (unsigned long)strlen(a); /* safety against broken sprintf */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + + stmt_text= "select name from t1"; + rc= mysql_stmt_prepare(stmt, SL(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); + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + /* 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, SL(stmt_text)); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_bind_result(stmt1, my_bind); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + while (1) + { + rc= mysql_stmt_fetch(stmt1); + if (rc == MYSQL_NO_DATA) + { + break; + } + check_stmt_rc(rc, stmt1); + } + mysql_stmt_close(stmt); + mysql_stmt_close(stmt1); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#10760: cursors, crash in a fetch after rollback. */ + +static int test_bug10760(MYSQL *mysql) +{ + 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; + + rc= mysql_query(mysql, "drop table if exists t1, t2"); + check_mysql_rc(rc, mysql); + + /* create tables */ + rc= mysql_query(mysql, "create table t1 (id integer not null primary key)" + " engine=MyISAM"); + check_mysql_rc(rc, mysql);; + for (; i < 42; ++i) + { + char buf[100]; + sprintf(buf, "insert into t1 (id) values (%d)", i+1); + rc= mysql_query(mysql, buf); + check_mysql_rc(rc, mysql);; + } + 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, SL(stmt_text)); + check_stmt_rc(rc, stmt);; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt);; + 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. + */ + /* FAIL_IF(!rc, "Error expected"); */ + /* + 2: check that MyISAM tables used in cursors survive + COMMIT/ROLLBACK. + */ + rc= mysql_rollback(mysql); /* should not close the cursor */ + check_mysql_rc(rc, mysql);; + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt);; + + /* + 3: check that cursors to InnoDB tables are closed (for now) by + COMMIT/ROLLBACK. + */ + if (check_variable(mysql, "@@have_innodb", "YES")) + { + stmt_text= "select id from t1 order by 1"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt);; + + rc= mysql_query(mysql, "alter table t1 engine=InnoDB"); + check_mysql_rc(rc, mysql);; + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt);; + mysql_stmt_bind_result(stmt, my_bind); + + rc= mysql_stmt_execute(stmt); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == 0, "rc != 0"); + rc= mysql_rollback(mysql); /* should close the cursor */ + } + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + rc= mysql_autocommit(mysql, TRUE); /* restore default */ + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#11172: cursors, crash on a fetch from a datetime column */ + +static int test_bug11172(MYSQL *mysql) +{ + 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; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (id integer not null primary key," + "hired date not null)"); + check_mysql_rc(rc, mysql); + 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')"); + check_mysql_rc(rc, mysql); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + stmt_text= "SELECT id, hired FROM t1 WHERE hired=?"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + type= (ulong) CURSOR_TYPE_READ_ONLY; + mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); + + memset(bind_in, '\0', sizeof(bind_in)); + memset(bind_out, '\0', sizeof(bind_out)); + memset(&hired, '\0', 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_stmt_rc(rc, stmt); + rc= mysql_stmt_bind_result(stmt, bind_out); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + while ((rc= mysql_stmt_fetch(stmt)) == 0); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "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"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#11656: cursors, crash on a fetch from a query with distinct. */ + +static int test_bug11656(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + int rc; + const char *stmt_text; + char buf[2][20]; + int i= 0; + ulong type; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + 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))"); + check_mysql_rc(rc, mysql); + + stmt_text= "select distinct test_kind, test_id from t1 " + "where server in (?, ?)"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + type= (ulong) CURSOR_TYPE_READ_ONLY; + mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); + + memset(my_bind, '\0', sizeof(my_bind)); + strcpy(buf[0], "pcint502_MY2"); + strcpy(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= (unsigned long)strlen(buf[i]); + } + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Cursors: opening a cursor to a complicated query with ORDER BY */ + +static int test_bug11901(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + int rc; + char workdept[20]; + ulong workdept_len; + uint32 empno; + const ulong type= (ulong)CURSOR_TYPE_READ_ONLY; + const char *stmt_text; + + + stmt_text= "drop table if exists t1, t2"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "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"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type); + check_stmt_rc(rc, stmt); + + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + + empno= 10; + + /* ERROR: next statement causes a server crash */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1, t2"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#11904: mysql_stmt_attr_set CURSOR_TYPE_READ_ONLY grouping wrong result */ + +static int test_bug11904(MYSQL *mysql) +{ + 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}; + + /* create tables */ + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bug11904b"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE bug11904b (id int, name char(10), primary key(id, name))"); + check_mysql_rc(rc, mysql); + + 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')"); + + check_mysql_rc(rc, mysql); + 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 ORDER BY id"; + + rc= mysql_stmt_prepare(stmt1, SL(stmt_text)); + check_stmt_rc(rc, stmt1); + + 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_stmt_rc(rc, stmt1); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + FAIL_UNLESS(country_id == 1, "country_id != 1"); + FAIL_UNLESS(memcmp(row_data, "plovdiv", 7) == 0, "row_data != 'plovdiv'"); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + FAIL_UNLESS(country_id == 2, "country_id != 2"); + FAIL_UNLESS(memcmp(row_data, "LA", 2) == 0, "row_data != 'LA'"); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + FAIL_UNLESS(country_id == 3, "country_id != 3"); + FAIL_UNLESS(memcmp(row_data, "berlin", 6) == 0, "row_data != 'Berlin'"); + + rc= mysql_stmt_close(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_query(mysql, "drop table bug11904b"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* Bug#12243: multiple cursors, crash in a fetch after commit. */ + +static int test_bug12243(MYSQL *mysql) +{ + MYSQL_STMT *stmt1, *stmt2; + int rc; + const char *stmt_text; + ulong type; + + if (!check_variable(mysql, "@@have_innodb", "YES")) + { + diag("Skip -> Test required InnoDB"); + return SKIP; + } + + /* create tables */ + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (a int) engine=InnoDB"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 (a) values (1), (2)"); + check_mysql_rc(rc, mysql); + mysql_autocommit(mysql, FALSE); + /* create statement */ + stmt1= mysql_stmt_init(mysql); + stmt2= mysql_stmt_init(mysql); + type= (ulong) CURSOR_TYPE_READ_ONLY; + rc= mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (const void*) &type); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_attr_set(stmt2, STMT_ATTR_CURSOR_TYPE, (const void*) &type); + check_stmt_rc(rc, stmt1); + + stmt_text= "select a from t1"; + + rc= mysql_stmt_prepare(stmt1, SL(stmt_text)); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_prepare(stmt2, SL(stmt_text)); + check_stmt_rc(rc, stmt2); + rc= mysql_stmt_execute(stmt2); + check_stmt_rc(rc, stmt2); + rc= mysql_stmt_fetch(stmt2); + check_stmt_rc(rc, stmt2); + + rc= mysql_stmt_close(stmt1); + check_stmt_rc(rc, stmt1); + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + rc= mysql_stmt_fetch(stmt2); + check_stmt_rc(rc, stmt2); + + mysql_stmt_close(stmt2); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + mysql_autocommit(mysql, TRUE); /* restore default */ + + return OK; +} + +/* Bug#11909: wrong metadata if fetching from two cursors */ + +static int test_bug11909(MYSQL *mysql) +{ + 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; + const ulong type= (ulong)CURSOR_TYPE_READ_ONLY; + + + stmt_text= "drop table if exists t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + /* ****** Begin of trace ****** */ + + stmt_text= "SELECT empno, firstname, midinit, lastname," + "workdept, salary, bonus FROM t1 ORDER BY empno"; + stmt1= mysql_stmt_init(mysql); + FAIL_IF(!stmt1, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt1, SL(stmt_text)); + check_stmt_rc(rc, stmt1); + mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, + (const void*) &type); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt1); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_fetch(stmt1); + FAIL_UNLESS(rc == 0, "rc != 0"); + FAIL_UNLESS(empno == 10, "empno != 10"); + FAIL_UNLESS(strcmp(firstname, "CHRISTINE""") == 0, "firstname != 'Christine'"); + FAIL_UNLESS(strcmp(midinit, "I""") == 0, ""); + FAIL_UNLESS(strcmp(lastname, "HAAS""") == 0, "lastname != 'HAAS'"); + FAIL_UNLESS(strcmp(workdept, "A00""") == 0, "workdept != 'A00'"); + FAIL_UNLESS(salary == (double) 52750.0, "salary != 52750"); + FAIL_UNLESS(bonus == (float) 1000.0, "bonus =! 1000"); + + stmt_text = "SELECT empno, firstname FROM t1"; + stmt2= mysql_stmt_init(mysql); + FAIL_IF(!stmt2, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt2, SL(stmt_text)); + check_stmt_rc(rc, stmt2); + mysql_stmt_attr_set(stmt2, STMT_ATTR_CURSOR_TYPE, + (const void*) &type); + rc= mysql_stmt_bind_result(stmt2, my_bind); + check_stmt_rc(rc, stmt2); + + rc= mysql_stmt_execute(stmt2); + check_stmt_rc(rc, stmt2); + + rc= mysql_stmt_fetch(stmt2); + FAIL_UNLESS(rc == 0, "rc != 0"); + + FAIL_UNLESS(empno == 10, "empno != 10"); + FAIL_UNLESS(strcmp(firstname, "CHRISTINE""") == 0, "firstname != 'Christine'"); + + rc= mysql_stmt_reset(stmt2); + check_stmt_rc(rc, stmt2); + + /* ERROR: next statement should return 0 */ + + rc= mysql_stmt_fetch(stmt1); + FAIL_UNLESS(rc == 0, "rc != 0"); + + mysql_stmt_close(stmt1); + mysql_stmt_close(stmt2); + rc= mysql_rollback(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#13488: wrong column metadata when fetching from cursor */ + +static int test_bug13488(MYSQL *mysql) +{ + 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 f1, f2, f3 from t1 left join t2 on f1=f2 where f1=1"; + + + rc= mysql_query(mysql, "drop table if exists t1, t2"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (f1 int not null primary key)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t2 (f2 int not null primary key, " + "f3 int not null)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values (1), (2)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t2 values (1,2), (2,4)"); + check_mysql_rc(rc, mysql); + + 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_stmt_rc(rc, stmt1); + + rc= mysql_stmt_prepare(stmt1, SL(query)); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_bind_result(stmt1, my_bind); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_free_result(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_reset(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_close(stmt1); + check_stmt_rc(rc, stmt1); + + FAIL_UNLESS(f1 == 1, "f1 != 1"); + FAIL_UNLESS(f2 == 1, "f2 != 1"); + FAIL_UNLESS(f3 == 2, "f3 != 2"); + rc= mysql_query(mysql, "drop table t1, t2"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + Bug#13524: warnings of a previous command are not reset when fetching + from a cursor. +*/ + +static int test_bug13524(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + unsigned int warning_count; + const ulong type= CURSOR_TYPE_READ_ONLY; + const char *query= "select * from t1"; + + + rc= mysql_query(mysql, "drop table if exists t1, t2"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (a int not null primary key)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values (1), (2), (3), (4)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + warning_count= mysql_warning_count(mysql); + FAIL_UNLESS(warning_count == 0, "warning_count != 0"); + + /* Check that DROP TABLE produced a warning (no such table) */ + rc= mysql_query(mysql, "drop table if exists t2"); + check_mysql_rc(rc, mysql); + warning_count= mysql_warning_count(mysql); + FAIL_UNLESS(warning_count == 1, "warning_count != 1"); + + /* + Check that fetch from a cursor cleared the warning from the previous + command. + */ + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + warning_count= mysql_warning_count(mysql); + FAIL_UNLESS(warning_count == 0, "warning_count != 0"); + + /* Cleanup */ + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + Bug#14845 "mysql_stmt_fetch returns MYSQL_NO_DATA when COUNT(*) is 0" +*/ + +static int test_bug14845(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const ulong type= CURSOR_TYPE_READ_ONLY; + const char *query= "select count(*) from t1 where 1 = 0"; + + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (id int(11) default null, " + "name varchar(20) default null)" + "engine=MyISAM DEFAULT CHARSET=utf8"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values (1,'abc'),(2,'def')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == 0, ""); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, ""); + + /* Cleanup */ + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + Bug#14210 "Simple query with > operator on large table gives server + crash" +*/ + +static int test_bug14210(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + const char *stmt_text; + ulong type; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + /* + 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. + */ + rc= mysql_query(mysql, "create table t1 (a varchar(255)) engine=InnoDB"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 (a) values (repeat('a', 256))"); + check_mysql_rc(rc, mysql); + 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"); + check_mysql_rc(rc, mysql); + } + /* 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + while ((rc= mysql_stmt_fetch(stmt)) == 0); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + rc= mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "set @@session.max_heap_table_size=default"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + Bug#24179 "select b into $var" fails with --cursor_protocol" + The failure is correct, check that the returned message is meaningful. +*/ + +static int test_bug24179(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + + // works with xpand + SKIP_XPAND; + + stmt= open_cursor(mysql, "select 1 into @a"); + rc= mysql_stmt_execute(stmt); + FAIL_UNLESS(rc, "Error expected"); + FAIL_UNLESS(mysql_stmt_errno(stmt) == 1323, "stmt_errno != 1323"); + mysql_stmt_close(stmt); + + return OK; +} + +/** + Bug#32265 Server returns different metadata if prepared statement is used +*/ + +static int test_bug32265(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + MYSQL_FIELD *field; + MYSQL_RES *metadata; + + if (mysql_get_server_version(mysql) < 50100) { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS v1"); + rc= mysql_query(mysql, "CREATE TABLE t1 (a INTEGER)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE VIEW v1 AS SELECT * FROM t1"); + check_mysql_rc(rc, mysql); + + stmt= open_cursor(mysql, "SELECT * FROM t1"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); + FAIL_UNLESS(field, "couldn't fetch field"); + FAIL_UNLESS(strcmp(field->table, "t1") == 0, "table != t1"); + FAIL_UNLESS(strcmp(field->org_table, "t1") == 0, "org_table != t1"); + FAIL_UNLESS(strcmp(field->db, schema) == 0, "db != schema"); + mysql_free_result(metadata); + mysql_stmt_close(stmt); + + stmt= open_cursor(mysql, "SELECT a '' FROM t1 ``"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); + FAIL_UNLESS(strcmp(field->table, "") == 0, "field != ''"); + FAIL_UNLESS(strcmp(field->org_table, "t1") == 0, "org_table != t1"); + FAIL_UNLESS(strcmp(field->db, schema) == 0, "db != schema"); + mysql_free_result(metadata); + mysql_stmt_close(stmt); + + stmt= open_cursor(mysql, "SELECT a '' FROM t1 ``"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); + FAIL_UNLESS(strcmp(field->table, "") == 0, "table != ''"); + FAIL_UNLESS(strcmp(field->org_table, "t1") == 0, "org_table != t1"); + FAIL_UNLESS(strcmp(field->db, schema) == 0, "db != schema"); + mysql_free_result(metadata); + mysql_stmt_close(stmt); + + stmt= open_cursor(mysql, "SELECT * FROM v1"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); + FAIL_UNLESS(strcmp(field->table, "v1") == 0, "table != v1"); + FAIL_UNLESS(strcmp(field->org_table, "v1") == 0, "org_table != v1"); + FAIL_UNLESS(strcmp(field->db, schema) == 0, "db != schema"); + mysql_free_result(metadata); + mysql_stmt_close(stmt); + + stmt= open_cursor(mysql, "SELECT * FROM v1 /* SIC */ GROUP BY 1"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_field(metadata); + FAIL_UNLESS(strcmp(field->table, "v1") == 0, "table != v1"); + FAIL_UNLESS(strcmp(field->org_table, "v1") == 0, "org_table != v1"); + FAIL_UNLESS(strcmp(field->db, schema) == 0, "schema != db"); + mysql_free_result(metadata); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/** + Bug#38486 Crash when using cursor protocol +*/ + +static int test_bug38486(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + unsigned long type= CURSOR_TYPE_READ_ONLY; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t10"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t10 (a INT)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*)&type); + check_stmt_rc(rc, stmt); + stmt_text= "INSERT INTO t10 VALUES (1)"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t10"); + check_mysql_rc(rc, mysql); + mysql_stmt_close(stmt); + + return OK; +} + +static int test_bug8880(MYSQL *mysql) +{ + MYSQL_STMT *stmt_list[2], **stmt; + MYSQL_STMT **stmt_list_end= (MYSQL_STMT**) stmt_list + 2; + int rc; + + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (a int not null primary key, b int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values (1,1)"); + check_mysql_rc(rc, mysql); + /* + 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(mysql, "select a from t1"); + for (stmt= stmt_list; stmt < stmt_list_end; stmt++) + { + rc= mysql_stmt_execute(*stmt); + check_stmt_rc(rc, *stmt); + } + for (stmt= stmt_list; stmt < stmt_list_end; stmt++) + mysql_stmt_close(*stmt); + return OK; +} + +static int test_bug9159(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *stmt_text= "select a, b from t1"; + const unsigned long type= CURSOR_TYPE_READ_ONLY; + + + 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)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + mysql_stmt_prepare(stmt, SL(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"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + 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 int test_bug9478(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + char a[6]; + ulong a_len; + int rc, i; + + 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')"); + check_mysql_rc(rc, mysql); + + stmt= open_cursor(mysql, "select name from t1 where id=2"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + /* + 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + { + char buff[8]; + /* Fill in the fetch packet */ + int4store(buff, stmt->stmt_id); + buff[4]= 1; /* prefetch rows */ +/* rc= ((*mysql->methods->advanced_command)(mysql, COM_STMT_FETCH, + (uchar*) buff, + sizeof(buff), 0,0,1,NULL) || + (*mysql->methods->read_query_result)(mysql)); */ + FAIL_UNLESS(rc, "error expected"); + } + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + + /* mariadb client supports GEOMETRY, so no error will + be returned + FAIL_UNLESS(rc && mysql_stmt_errno(stmt), "Error expected"); + */ + } + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + /* Test the case with a server side cursor */ + stmt= open_cursor(mysql, "select name from t1"); + + mysql_stmt_bind_result(stmt, my_bind); + + for (i= 0; i < 5; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + while (! (rc= mysql_stmt_fetch(stmt))); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc && mysql_stmt_errno(stmt), "Error expected"); + } + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Crash when opening a cursor to a query with DISTINCT and no key */ + +static int test_bug9520(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + char a[6]; + ulong a_len; + int rc, row_count= 0; + + + 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')"); + check_mysql_rc(rc, mysql); + + stmt= open_cursor(mysql, "select distinct b from t1"); + + /* + Not crashes with: + stmt= open_cursor(mysql, "select distinct a from t1"); + */ + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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++; + + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + FAIL_UNLESS(row_count == 3, "row_count != 3"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + Error message is returned for unsupported features. + Test also cursors with non-default PREFETCH_ROWS +*/ + +static int test_bug9643(MYSQL *mysql) +{ + 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; + + + 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)"); + check_mysql_rc(rc, mysql); + + 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); + FAIL_UNLESS(rc, "Error expected"); + + type= (ulong) CURSOR_TYPE_READ_ONLY; + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS, + (void*) &prefetch_rows); + check_stmt_rc(rc, stmt); + stmt_text= "select * from t1"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + while ((rc= mysql_stmt_fetch(stmt)) == 0) + ++num_rows; + FAIL_UNLESS(num_rows == 9, "num_rows != 9"); + + rc= mysql_stmt_close(stmt); + FAIL_UNLESS(rc == 0, ""); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_basic_cursors", test_basic_cursors, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_cursors_with_union", test_cursors_with_union, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_cursors_with_procedure", test_cursors_with_procedure, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug21206", test_bug21206, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug10729", test_bug10729, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug10736", test_bug10736, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug10794", test_bug10794, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug10760", test_bug10760, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11172", test_bug11172, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11656", test_bug11656, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11901", test_bug11901, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11904", test_bug11904, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug12243", test_bug12243, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11909", test_bug11909, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug13488", test_bug13488, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug13524", test_bug13524, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug14845", test_bug14845, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug14210", test_bug14210, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug24179", test_bug24179, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug32265", test_bug32265, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug38486", test_bug38486, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug8880", test_bug8880, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug9159", test_bug9159, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug9478", test_bug9478, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug9520", test_bug9520, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug9643", test_bug9643, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/data.csv b/libmariadb/unittest/libmariadb/data.csv new file mode 100644 index 00000000..07471374 --- /dev/null +++ b/libmariadb/unittest/libmariadb/data.csv @@ -0,0 +1,100 @@ +00000100,1,60000.000000 +00000101,2,60000.000000 +00000102,3,60000.000000 +00000103,1,60000.000000 +00000104,2,60000.000000 +00000105,3,60000.000000 +00000106,1,60000.000000 +00000107,2,60000.000000 +00000108,3,60000.000000 +00000109,1,60000.000000 +00000110,2,60000.000000 +00000111,3,60000.000000 +00000112,1,60000.000000 +00000113,2,60000.000000 +00000114,3,60000.000000 +00000115,1,60000.000000 +00000116,2,60000.000000 +00000117,3,60000.000000 +00000118,1,60000.000000 +00000119,2,60000.000000 +00000120,3,60000.000000 +00000121,1,60000.000000 +00000122,2,60000.000000 +00000123,3,60000.000000 +00000124,1,60000.000000 +00000125,2,60000.000000 +00000126,3,60000.000000 +00000127,1,60000.000000 +00000128,2,60000.000000 +00000129,3,60000.000000 +00000130,1,60000.000000 +00000131,2,60000.000000 +00000132,3,60000.000000 +00000133,1,60000.000000 +00000134,2,60000.000000 +00000135,3,60000.000000 +00000136,1,60000.000000 +00000137,2,60000.000000 +00000138,3,60000.000000 +00000139,1,60000.000000 +00000140,2,60000.000000 +00000141,3,60000.000000 +00000142,1,60000.000000 +00000143,2,60000.000000 +00000144,3,60000.000000 +00000145,1,60000.000000 +00000146,2,60000.000000 +00000147,3,60000.000000 +00000148,1,60000.000000 +00000149,2,60000.000000 +00000150,3,60000.000000 +00000151,1,60000.000000 +00000152,2,60000.000000 +00000153,3,60000.000000 +00000154,1,60000.000000 +00000155,2,60000.000000 +00000156,3,60000.000000 +00000157,1,60000.000000 +00000158,2,60000.000000 +00000159,3,60000.000000 +00000160,1,60000.000000 +00000161,2,60000.000000 +00000162,3,60000.000000 +00000163,1,60000.000000 +00000164,2,60000.000000 +00000165,3,60000.000000 +00000166,1,60000.000000 +00000167,2,60000.000000 +00000168,3,60000.000000 +00000169,1,60000.000000 +00000170,2,60000.000000 +00000171,3,60000.000000 +00000172,1,60000.000000 +00000173,2,60000.000000 +00000174,3,60000.000000 +00000175,1,60000.000000 +00000176,2,60000.000000 +00000177,3,60000.000000 +00000178,1,60000.000000 +00000179,2,60000.000000 +00000180,3,60000.000000 +00000181,1,60000.000000 +00000182,2,60000.000000 +00000183,3,60000.000000 +00000184,1,60000.000000 +00000185,2,60000.000000 +00000186,3,60000.000000 +00000187,1,60000.000000 +00000188,2,60000.000000 +00000189,3,60000.000000 +00000190,1,60000.000000 +00000191,2,60000.000000 +00000192,3,60000.000000 +00000193,1,60000.000000 +00000194,2,60000.000000 +00000195,3,60000.000000 +00000196,1,60000.000000 +00000197,2,60000.000000 +00000198,3,60000.000000 +00000199,1,60000.000000 diff --git a/libmariadb/unittest/libmariadb/dyncol.c b/libmariadb/unittest/libmariadb/dyncol.c new file mode 100644 index 00000000..8edb20a8 --- /dev/null +++ b/libmariadb/unittest/libmariadb/dyncol.c @@ -0,0 +1,323 @@ +/* +Copyright (c) 2013 Monty Program AB. 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-1301 USA +*/ + +#include "my_test.h" +#include "mariadb_dyncol.h" + +static int create_dyncol_named(MYSQL *mysql) +{ + DYNAMIC_COLUMN dyncol; + DYNAMIC_COLUMN_VALUE *vals; + uint i, column_count= 6; + int rc; + const char *strval[]= {"Val1", "Val2", "Val3", "Val4", "Val5", "Val6"}; + MYSQL_LEX_STRING keys1[]= {{(char *)"key1", 4}, {(char *)"key2", 4}, + {(char *)"key3", 4}, {(char *)"key4", 4}, + {(char *)"key5", 4}, {(char *)"key6", 4}}, + + keys2[]= {{(char *)"key1", 4}, {(char *)"key1", 4}, + {(char *)"key3", 4}, {(char *)"key4", 4}, + {(char *)"key5", 4}, {(char *)"key6", 4}}, + + keys3[]= {{(char *)"\x70\x61\x72\x61\x00\x30", 6}, + {(char *)"\x70\x61\x72\x61\x00\x31", 6}, + {(char *)"\x70\x61\x72\x61\x00\x32", 6}, + {(char *)"\x70\x61\x72\x61\x00\x33", 6}, + {(char *)"\x70\x61\x72\x61\x00\x34", 6}, + {(char *)"\x70\x61\x72\x61\x00\x35", 6}}; + MYSQL_LEX_STRING *my_keys; + uint my_count; + + vals= (DYNAMIC_COLUMN_VALUE *)malloc(column_count * sizeof(DYNAMIC_COLUMN_VALUE)); + + for (i=0; i < column_count; i++) + { + vals[i].type= DYN_COL_STRING; + vals[i].x.string.value.str= (char *)strval[i]; + vals[i].x.string.value.length= strlen(strval[i]); + vals[i].x.string.charset= (MARIADB_CHARSET_INFO *)mysql->charset; + diag("%s", keys3[i].str); + } + + mariadb_dyncol_init(&dyncol); + rc= mariadb_dyncol_create_many_named(&dyncol, column_count, keys1, vals, 0); + mariadb_dyncol_free(&dyncol); + FAIL_IF(mariadb_dyncol_create_many_named(&dyncol, column_count, keys1, vals, 1) < 0, "Error"); + column_count= 0; + FAIL_IF(mariadb_dyncol_column_count(&dyncol, &column_count) < 0, "Error"); + + FAIL_IF(column_count != 6, "6 columns expected"); + mariadb_dyncol_free(&dyncol); + + rc= mariadb_dyncol_create_many_named(&dyncol, column_count, keys3, vals, 1); + if (rc < 0) { + diag("Error!!: %d", rc); + return FAIL; + } + column_count= 0; + FAIL_IF(mariadb_dyncol_column_count(&dyncol, &column_count) < 0, "Error"); + + FAIL_IF(column_count != 6, "6 columns expected"); + + mariadb_dyncol_free(&dyncol); + + /* Now try to add a duplicate key */ + + FAIL_IF(mariadb_dyncol_create_many_named(&dyncol, column_count, keys2, vals, 1) >=0, "Error expected"); + mariadb_dyncol_free(&dyncol); + + /* binary keys */ + rc= mariadb_dyncol_create_many_named(&dyncol, column_count, keys3, vals, 1); + FAIL_IF(rc < 0, "binary keys failed"); + + /* get keys*/ + rc= mariadb_dyncol_list_named(&dyncol, &my_count, &my_keys); + FAIL_IF(rc < 0, "list named failed"); + + for (i=0; i < my_count; i++) + { + if (memcmp(my_keys[i].str, keys3[i].str, keys3[i].length) != 0) + diag("error key %d", i); + vals[i].type=DYN_COL_NULL; + } + rc= mariadb_dyncol_update_many_named(&dyncol, column_count, keys3, vals); + FAIL_IF(rc < 0, "update failed"); + mariadb_dyncol_free(&dyncol); + + keys3[0].str= (char *)"test"; + for (i=0; i < column_count; i++) + diag("%s", my_keys[i].str); + + free(vals); + free(my_keys); + return OK; +} + +static int mdev_4994(MYSQL *unused __attribute__((unused))) +{ + DYNAMIC_COLUMN dyncol; + uint key= 1; + DYNAMIC_COLUMN_VALUE val; + int rc; + + + val.type= DYN_COL_NULL; + + mariadb_dyncol_init(&dyncol); + rc= mariadb_dyncol_create_many_num(&dyncol, 1, &key, &val, 0); + FAIL_IF(rc < 0, "Unexpected error"); + mariadb_dyncol_free(&dyncol); + return OK; +} + +static int create_dyncol_num(MYSQL *mysql) +{ + DYNAMIC_COLUMN dyncol; + DYNAMIC_COLUMN_VALUE vals[5]; + uint i, column_count= 5; + uint my_count; + MYSQL_LEX_STRING *my_keys; + DYNAMIC_COLUMN_VALUE *my_vals; + int rc; + const char *strval[]= {"Val1", "Val2", "Val3", "Val4", "Val5"}; + + uint keys1[5]= {1,2,3,4,5}, + keys2[5]= {1,2,2,4,5}; + MYSQL_LEX_STRING key1= {(char *)"1",1}; + + for (i=0; i < column_count; i++) + { + vals[i].type= DYN_COL_STRING; + vals[i].x.string.value.str= (char *)strval[i]; + vals[i].x.string.value.length= strlen(strval[i]); + vals[i].x.string.charset= (MARIADB_CHARSET_INFO *)mysql->charset; + } + FAIL_IF(mariadb_dyncol_create_many_num(&dyncol, column_count, keys1, vals, 1) <0, "Error (keys1)"); + + vals[0].x.string.value.str= (char *)strval[1]; + rc= mariadb_dyncol_update_many_named(&dyncol,1, &key1, vals); + diag("update: %d", rc); + + rc= mariadb_dyncol_unpack(&dyncol, &my_count, &my_keys, &my_vals); + diag("unpack: %d %d", rc, my_count); + + free(my_keys); + free(my_vals); + + FAIL_IF(mariadb_dyncol_column_count(&dyncol, &column_count) < 0, "Error"); + FAIL_IF(column_count != 5, "5 columns expected"); + mariadb_dyncol_free(&dyncol); + FAIL_IF(mariadb_dyncol_create_many_num(&dyncol, column_count, keys2, vals, 1) >=0, "Error expected (keys2)"); + mariadb_dyncol_free(&dyncol); + return OK; +} + +static int mdev_x1(MYSQL *mysql) +{ + int rc; + uint i; + uint num_keys[5]= {1,2,3,4,5}; + const char *strval[]= {"Val1", "Val2", "Val3", "Val4", "Val5"}; + DYNAMIC_COLUMN_VALUE vals[5]; + DYNAMIC_COLUMN dynstr; + MYSQL_LEX_STRING my_key= {(char *)"1", 2}; + uint unpack_columns; + MYSQL_LEX_STRING *unpack_keys; + DYNAMIC_COLUMN_VALUE *unpack_vals; + + for (i=0; i < 5; i++) + { + vals[i].type= DYN_COL_STRING; + vals[i].x.string.value.str= (char *)strval[i]; + vals[i].x.string.value.length= strlen(strval[i]); + vals[i].x.string.charset= (MARIADB_CHARSET_INFO *)mysql->charset; + } + + mariadb_dyncol_init(&dynstr); + + /* create numeric */ + rc= mariadb_dyncol_create_many_num(&dynstr, 5, num_keys, vals, 1); + if (rc < 0) + { + diag("Error: %d", rc); + return FAIL; + } + + /* unpack and print values */ + rc= mariadb_dyncol_unpack(&dynstr, &unpack_columns, &unpack_keys, &unpack_vals); + if (rc < 0) + { + diag("Error: %d", rc); + return FAIL; + } + + for (i=0; i < unpack_columns; i++) + if (memcmp(unpack_vals[i].x.string.value.str, vals[i].x.string.value.str, vals[i].x.string.value.length)) + diag("Error1: key: %1s val: %s %s", unpack_keys[i].str, unpack_vals[i].x.string.value.str, vals[i].x.string.value.str); + + free(unpack_keys); + free(unpack_vals); + + /* change one value and update with named key */ +/* vals[0].x.string.value.str= strval[1]; */ + rc= mariadb_dyncol_update_many_named(&dynstr, 1, &my_key, vals); + if (rc < 0) + { + diag("Error: %d", rc); + return FAIL; + } + + /* unpack and print values */ + rc= mariadb_dyncol_unpack(&dynstr, &unpack_columns, &unpack_keys, &unpack_vals); + if (rc < 0) + { + diag("Error: %d", rc); + return FAIL; + } + diag("Columns: %d", unpack_columns); + + for (i=0; i < unpack_columns; i++) + diag("Key: %s Len: %lu", unpack_keys[i].str, (unsigned long)unpack_keys[i].length); + + + free(unpack_keys); + free(unpack_vals); + + mariadb_dyncol_free(&dynstr); + return OK; +} + +static int dyncol_column_count(MYSQL *unused __attribute__((unused))) +{ + DYNAMIC_COLUMN dyncol; + uint column_count= 5; + int rc; + + mariadb_dyncol_init(&dyncol); /* memset(&dyncol, 0, sizeof(DYNAMIC_COLUMN)) */ + rc= mariadb_dyncol_column_count(&dyncol, &column_count); + diag("rc=%d", rc); + FAIL_IF(rc < 0, "unexpected error"); + FAIL_IF(column_count > 0, "Expected column_count=0"); + return OK; +} + +static int dyncol_nested(MYSQL *mysql __attribute__((unused))) +{ + DYNAMIC_COLUMN col1, col2; + DYNAMIC_COLUMN_VALUE value[2]; + MYSQL_LEX_STRING cols[2]= {{(char *)"0",1},{(char *)"1",1}}; + DYNAMIC_STRING s; + + mariadb_dyncol_init(&col1); + mariadb_dyncol_init(&col2); + + memset(&value, 0, sizeof(DYNAMIC_COLUMN_VALUE)); + + value[0].type= DYN_COL_UINT; + value[0].x.ulong_value = 17; + + mariadb_dyncol_create_many_named(&col1, 1, cols, value, 0); + if (mariadb_dyncol_check(&col1) != ER_DYNCOL_OK) + { + diag("Error while creating col1"); + return FAIL; + } + + value[1].type= DYN_COL_DYNCOL; + value[1].x.string.value.str= col1.str; + value[1].x.string.value.length= col1.length; + + mariadb_dyncol_create_many_named(&col2, 2, cols, value, 0); + if (mariadb_dyncol_check(&col2) != ER_DYNCOL_OK) + { + diag("Error while creating col1"); + return FAIL; + } + mariadb_dyncol_json(&col2, &s); + if (strcmp(s.str, "{\"0\":17,\"1\":{\"0\":17}}") != 0) + { + diag("%s != %s", s.str, "{\"0\":17,\"1\":{\"0\":17}}"); + return FAIL; + } + ma_dynstr_free(&s); + mariadb_dyncol_free(&col1); + mariadb_dyncol_free(&col2); + return OK; +} + +struct my_tests_st my_tests[] = { + {"mdev_x1", mdev_x1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"mdev_4994", mdev_4994, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"create_dyncol_named", create_dyncol_named, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"create_dyncol_num", create_dyncol_num, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"dyncol_column_count", dyncol_column_count, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"dyncol_nested", dyncol_nested, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, 0} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/errors.c b/libmariadb/unittest/libmariadb/errors.c new file mode 100644 index 00000000..b0914782 --- /dev/null +++ b/libmariadb/unittest/libmariadb/errors.c @@ -0,0 +1,299 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" + +/* Test warnings */ + +static int test_client_warnings(MYSQL *mysql) +{ + int rc; + + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + + FAIL_IF(!mysql_warning_count(mysql), "Warning expected"); + + return OK; +} + + +static int test_ps_client_warnings(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + const char *query= "DROP TABLE IF EXISTS test_non_exists"; + + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(rc, mysql_stmt_error(stmt)); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(rc, mysql_stmt_error(stmt)); + + FAIL_IF(!mysql_warning_count(mysql), "Warning expected"); + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_server_warnings(MYSQL *mysql) +{ + int rc; + MYSQL_RES *result; + + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SHOW WARNINGS"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, mysql_error(mysql)); + FAIL_IF(!mysql_num_rows(result), "Empty resultset"); + + mysql_free_result(result); + + return OK; +} + + +/* Test errors */ + +static int test_client_errors(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE test_non_exists"); + FAIL_IF(!rc, "Error expected"); + + FAIL_IF(!mysql_errno(mysql), "Error expected"); + FAIL_IF(!strlen(mysql_error(mysql)), "Empty errormsg"); + FAIL_IF(strcmp(mysql_sqlstate(mysql), "00000") == 0, "Invalid SQLstate"); + + return OK; +} + +static int test_ps_client_errors(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + const char *query= "DROP TABLE test_non_exists"; + + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(rc, mysql_stmt_error(stmt)); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(!rc, mysql_stmt_error(stmt)); + + FAIL_IF(!mysql_stmt_errno(stmt), "Error expected"); + FAIL_IF(!strlen(mysql_stmt_error(stmt)), "Empty errormsg"); + FAIL_IF(strcmp(mysql_stmt_sqlstate(stmt), "00000") == 0, "Invalid SQLstate"); + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_server_errors(MYSQL *mysql) +{ + int rc; + MYSQL_RES *result; + + rc= mysql_query(mysql, "DROP TABLE if exists test_non_exists"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE test_non_exists"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SHOW ERRORS"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, mysql_error(mysql)); + FAIL_IF(!mysql_num_rows(result), "Empty resultset"); + mysql_free_result(result); + + return OK; +} + +/* Bug #16143: mysql_stmt_sqlstate returns an empty string instead of '00000' */ + +static int test_bug16143(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + /* Check mysql_stmt_sqlstate return "no error" */ + FAIL_UNLESS(strcmp(mysql_stmt_sqlstate(stmt), "00000") == 0, "Expected SQLstate 000000"); + mysql_stmt_close(stmt); + return OK; +} + +/* Test warnings for cuted rows */ + +static int test_cuted_rows(MYSQL *mysql) +{ + int rc, count; + MYSQL_RES *result; + + if (!is_mariadb) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE if exists t1"); + check_mysql_rc(rc, mysql); + mysql_query(mysql, "DROP TABLE if exists t2"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1(c1 tinyint)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t2(c1 int not null)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "START TRANSACTION"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 values(10), (NULL), (NULL)"); + check_mysql_rc(rc, mysql); + + count= mysql_warning_count(mysql); + FAIL_UNLESS(count == 0, "warnings != 0"); + + rc= mysql_query(mysql, "INSERT INTO t2 SELECT * FROM t1"); + check_mysql_rc(rc, mysql); + + count= mysql_warning_count(mysql); + FAIL_UNLESS(count == 2, "warnings != 2"); + + rc= mysql_query(mysql, "SHOW WARNINGS"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 2, "rowcount != 2"); + mysql_free_result(result); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES('junk'), (876789)"); + check_mysql_rc(rc, mysql); + + count= mysql_warning_count(mysql); + FAIL_UNLESS(count == 2, "warnings != 2"); + + rc= mysql_query(mysql, "SHOW WARNINGS"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 2, "rowcount != 2"); + mysql_free_result(result); + + rc= mysql_query(mysql, "DROP TABLE t1, t2"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_parse_error_and_bad_length(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char stmt_str[128]; + + /* check that we get 4 syntax errors over the 4 calls */ + + rc= mysql_query(mysql, "SHOW DATABAAAA"); + FAIL_UNLESS(rc, "Error expected"); + rc= mysql_real_query(mysql, SL_BIN("SHOW DATABASES\0AAA")); + FAIL_UNLESS(rc, "Error expected"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SHOW DATABAAAA")); + FAIL_IF(!rc, "Error expected"); + mysql_stmt_close(stmt); + stmt= mysql_stmt_init(mysql); + FAIL_UNLESS(stmt, ""); + memset(stmt_str, 0, 100); + + SKIP_XPAND; + + strcpy(stmt_str, "SHOW DATABASES"); + rc= mysql_stmt_prepare(stmt, stmt_str, 99); + FAIL_IF(!rc, "Error expected"); + mysql_stmt_close(stmt); + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_client_warnings", test_client_warnings, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_ps_client_warnings", test_ps_client_warnings, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_server_warnings", test_server_warnings, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_client_errors", test_client_errors, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_ps_client_errors", test_ps_client_errors, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_server_errors", test_server_errors, TEST_CONNECTION_DEFAULT, 0, NULL , "Open bug: #42364"}, + {"test_bug16143", test_bug16143, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_cuted_rows", test_cuted_rows, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_parse_error_and_bad_length", test_parse_error_and_bad_length, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/features-10_2.c b/libmariadb/unittest/libmariadb/features-10_2.c new file mode 100644 index 00000000..6e5064dc --- /dev/null +++ b/libmariadb/unittest/libmariadb/features-10_2.c @@ -0,0 +1,255 @@ +/* +*/ + +#include "my_test.h" + +static int execute_direct(MYSQL *mysql) +{ + int rc= 0; + long i= 0; + MYSQL_STMT *stmt; + MYSQL_BIND bind; + unsigned int param_count= 1; + MYSQL_RES *res= NULL; + + stmt= mysql_stmt_init(mysql); + + rc= mariadb_stmt_execute_direct(stmt, "DROP TABLE IF EXISTS t1", -1); + check_stmt_rc(rc, stmt); + + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1", -1); + check_stmt_rc(rc, stmt); + + while (!mysql_stmt_fetch(stmt)); + + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1", -1); + check_stmt_rc(rc, stmt); + + rc= mariadb_stmt_execute_direct(stmt, "CREATE TABLE t1 (a int)", -1); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + + bind.buffer= &i; + bind.buffer_type= MYSQL_TYPE_LONG; + bind.buffer_length= sizeof(long); + + mysql_stmt_close(stmt); + stmt= mysql_stmt_init(mysql); + mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶m_count); + + rc= mysql_stmt_bind_param(stmt, &bind); + check_stmt_rc(rc, stmt); + rc= mariadb_stmt_execute_direct(stmt, "INSERT INTO t1 VALUES (?)", -1); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "START TRANSACTION"); + check_mysql_rc(rc, mysql); + + for (i=1; i < 1000; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + rc= mysql_stmt_close(stmt); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT * FROM t1"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(mysql_num_rows(res) != 1000, "Expected 1000 rows"); + + mysql_free_result(res); + rc= mysql_query(mysql, "COMMIT"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int execute_direct_example(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_BIND bind[2]; + int intval= 1; + int param_count= 2; + int rc; + const char *strval= "execute_direct_example1"; + + /* Direct execution without parameters */ + rc= mariadb_stmt_execute_direct(stmt, "DROP TABLE IF EXISTS execute_direct", -1); + check_stmt_rc(rc, stmt); + rc= mariadb_stmt_execute_direct(stmt, "CREATE TABLE execute_direct (a int, b varchar(20))", -1); + rc= mysql_stmt_close(stmt); + stmt= mysql_stmt_init(mysql); + check_stmt_rc(rc, stmt); + memset(bind, 0, sizeof(MYSQL_BIND) * 2); + bind[0].buffer_type= MYSQL_TYPE_SHORT; + bind[0].buffer= &intval; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= (char *)strval; + bind[1].buffer_length= (unsigned long)strlen(strval); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + /* set number of parameters */ + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶m_count); + check_stmt_rc(rc, stmt); + + /* bind parameters */ + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mariadb_stmt_execute_direct(stmt, "INSERT INTO execute_direct VALUES (?,?)", -1); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE execute_direct"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int conc_213(MYSQL *mysql) +{ + MYSQL_BIND bind; + unsigned int param_count= 1; + long id= 1234; + MYSQL_STMT *stmt; + + stmt = mysql_stmt_init(mysql); + + memset(&bind, '\0', sizeof(bind)); + + bind.buffer_type = MYSQL_TYPE_LONG; + bind.buffer = (void *)&id; + bind.buffer_length = sizeof(long); +/* bind.is_null = &is_null; + bind.length = &length; + bind.error = &error; */ + + mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶m_count); + check_stmt_rc(mysql_stmt_bind_param(stmt, &bind), stmt); + check_stmt_rc(mariadb_stmt_execute_direct(stmt, "SELECT ?", -1), stmt); + check_stmt_rc(mysql_stmt_store_result(stmt), stmt); + check_stmt_rc(mysql_stmt_free_result(stmt), stmt); + + mysql_stmt_close(stmt); + + return OK; +} + +static int conc_212(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1, 2", -1); + check_stmt_rc(rc, stmt); + mysql_stmt_store_result(stmt); + mysql_stmt_free_result(stmt); + + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1, 2", -1); + check_stmt_rc(rc, stmt); + mysql_stmt_store_result(stmt); + mysql_stmt_free_result(stmt); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc,mysql); + + + rc= mysql_stmt_close(stmt); + + return OK; +} + +static int conc_218(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_BIND bind[2]; + int id=1; + my_bool is_null= 0, error= 0; + unsigned int param_count= 1; + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = (void *)&id; + bind[0].buffer_length = 4; + bind[0].is_null = &is_null; + bind[0].error = &error; + + mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶m_count); + check_stmt_rc(mysql_stmt_bind_param(stmt, bind), stmt); + check_stmt_rc(mariadb_stmt_execute_direct(stmt, "SELECT ?", -1), stmt); + check_stmt_rc(mysql_stmt_store_result(stmt), stmt); + + check_stmt_rc(mysql_stmt_free_result(stmt), stmt); + + param_count= 1; + mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶m_count); + check_stmt_rc(mysql_stmt_bind_param(stmt, bind), stmt); + check_stmt_rc(mariadb_stmt_execute_direct(stmt, "SELECT ?", -1), stmt); + mysql_stmt_close(stmt); + + return OK; +} + +static int test_cursor(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + unsigned long prefetch_rows= 1; + unsigned long cursor_type= CURSOR_TYPE_READ_ONLY; + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor_type); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS, &prefetch_rows); + check_stmt_rc(rc, stmt); + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1 FROM DUAL UNION SELECT 2 FROM DUAL", -1); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1 FROM DUAL UNION SELECT 2 FROM DUAL", -1); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_cursor", test_cursor, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"conc_218", conc_218, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"conc_212", conc_212, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"conc_213", conc_213, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"execute_direct", execute_direct, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"execute_direct_example", execute_direct_example, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + + mysql_library_init(0,0,NULL); + + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + mysql_server_end(); + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/fetch.c b/libmariadb/unittest/libmariadb/fetch.c new file mode 100644 index 00000000..55b1314a --- /dev/null +++ b/libmariadb/unittest/libmariadb/fetch.c @@ -0,0 +1,1003 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" + +/* Generalized fetch conversion routine for all basic types */ + +static int bind_fetch(MYSQL *mysql, int row_count) +{ + MYSQL_STMT *stmt; + int rc, i, count= row_count; + int32 data[10]; + int8 i8_data; + int16 i16_data; + int 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]; + char query[MAX_TEST_QUERY_LENGTH]; + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + strcpy(query, "INSERT INTO test_bind_fetch VALUES (?, ?, ?, ?, ?, ?, ?)"); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc,stmt); + + FAIL_UNLESS(mysql_stmt_param_count(stmt) == 7, "ParamCount != 7"); + + memset(my_bind, '\0', 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_stmt_rc(rc,stmt); + + 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_stmt_rc(rc,stmt); + } + + rc= mysql_commit(mysql); + check_stmt_rc(rc,stmt); + + mysql_stmt_close(stmt); + + rc= my_stmt_result(mysql, "SELECT * FROM test_bind_fetch"); + FAIL_UNLESS(row_count == rc, "Wrong number of rows"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + strcpy(query, "SELECT * FROM test_bind_fetch"); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc,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_stmt_rc(rc,stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc,stmt); + + while (row_count--) + { + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + rc= 10+row_count; + + /* TINY */ + FAIL_UNLESS((int) i8_data == rc, "Invalid value for i8_data"); + FAIL_UNLESS(length[0] == 1, "Invalid length"); + rc+= 13; + + /* SHORT */ + FAIL_UNLESS((int) i16_data == rc, "Invalid value for i16_data"); + FAIL_UNLESS(length[1] == 2, "Invalid length"); + rc+= 13; + + /* LONG */ + FAIL_UNLESS((int) i32_data == rc, "Invalid value for i32_data"); + FAIL_UNLESS(length[2] == 4, "Invalid length"); + rc+= 13; + + /* LONGLONG */ + FAIL_UNLESS((int) i64_data == rc, "Invalid value for i64_data"); + FAIL_UNLESS(length[3] == 8, "Invalid length"); + rc+= 13; + + /* FLOAT */ + FAIL_UNLESS((int)f_data == rc, "Invalid value for f_data"); + FAIL_UNLESS(length[4] == 4, "Invalid length"); + rc+= 13; + + /* DOUBLE */ + FAIL_UNLESS((int)d_data == rc, "Invalid value for d_data"); + FAIL_UNLESS(length[5] == 8, "Invalid length"); + rc+= 13; + + /* CHAR */ + { + char buff[20]; + long len= sprintf(buff, "%d", rc); + FAIL_UNLESS(strcmp(s_data, buff) == 0, "Invalid value for s_data"); + FAIL_UNLESS(length[6] == (ulong) len, "Invalid length"); + } + } + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "Expected MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + return OK; +} + + +static int test_fetch_seek(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[3]; + MYSQL_ROW_OFFSET row; + int rc; + int32 c1; + char c2[11], c3[20]; + const char *query = "SELECT * FROM t1"; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1(c1 int primary key auto_increment, c2 char(10), c3 timestamp)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1(c2) values('venu'), ('mysql'), ('open'), ('source')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc,stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc,stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc,stmt); + + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + row= mysql_stmt_row_tell(stmt); + + row= mysql_stmt_row_seek(stmt, row); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + row= mysql_stmt_row_seek(stmt, row); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + mysql_stmt_data_seek(stmt, 0); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc != MYSQL_NO_DATA, "Expected MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Test mysql_stmt_fetch_column() with offset */ + +static int test_fetch_offset(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + char data[11], chunk[5]; + ulong length[2]; + int rc; + my_bool is_null[2]; + const char *query = "SELECT * FROM t1"; + + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1(a char(10), b mediumblob)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values('abcdefghij', 'klmnopqrstzy'), (null, null)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc,stmt); + + memset(my_bind, '\0', 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[0]; + my_bind[0].length= &length[0]; + + my_bind[1].buffer_type= MYSQL_TYPE_MEDIUM_BLOB; + my_bind[1].buffer= NULL; + my_bind[1].buffer_length= 0; + my_bind[1].is_null= &is_null[1]; + my_bind[1].length= &length[1]; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0); + FAIL_IF(!rc, "Error expected"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc,stmt); +diag("truncation: %d", mysql->options.report_data_truncation); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_DATA_TRUNCATED, "rc != MYSQL_DATA_TRUNCATED"); + + data[0]= '\0'; + rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 0); + check_stmt_rc(rc,stmt); + + + FAIL_IF(!(strncmp(data, "abcdefghij", 11) == 0 && length[0] == 10), "Wrong value"); + FAIL_IF(my_bind[0].error_value, "No truncation, but error is set"); + + rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 5); + check_stmt_rc(rc,stmt); + FAIL_IF(!(strncmp(data, "fghij", 6) == 0 && length[0] == 10), "Wrong value"); + FAIL_IF(my_bind[0].error_value, "No truncation, but error is set"); + + rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 9); + check_stmt_rc(rc,stmt); + FAIL_IF(!(strncmp(data, "j", 2) == 0 && length[0] == 10), "Wrong value"); + FAIL_IF(my_bind[0].error_value, "No truncation, but error is set"); + + /* Now blob field */ + my_bind[1].buffer= chunk; + my_bind[1].buffer_length= sizeof(chunk); + + rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 0); + check_stmt_rc(rc,stmt); + + FAIL_IF(!(strncmp(chunk, "klmno", 5) == 0 && length[1] == 12), "Wrong value"); + FAIL_IF(my_bind[1].error_value == '\0', "Truncation, but error is not set"); + + rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 5); + check_stmt_rc(rc,stmt); + FAIL_IF(!(strncmp(chunk, "pqrst", 5) == 0 && length[1] == 12), "Wrong value"); + FAIL_IF(my_bind[1].error_value == '\0', "Truncation, but error is not set"); + + rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 10); + check_stmt_rc(rc,stmt); + FAIL_IF(!(strncmp(chunk, "zy", 2) == 0 && length[1] == 12), "Wrong value"); + FAIL_IF(my_bind[1].error_value, "No truncation, but error is set"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + memset(is_null, 0, sizeof(is_null)); + + rc= mysql_stmt_fetch_column(stmt, &my_bind[0], 0, 0); + check_stmt_rc(rc,stmt); + + FAIL_IF(is_null[0] != 1, "Null flag not set"); + + rc= mysql_stmt_fetch_column(stmt, &my_bind[1], 1, 0); + check_stmt_rc(rc,stmt); + + FAIL_IF(is_null[1] != 1, "Null flag not set"); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc != MYSQL_NO_DATA, "Expected MYSQL_NO_DATA"); + + rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Test mysql_stmt_fetch_column() */ + +static int test_fetch_column(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + char c2[20], bc2[20]; + ulong l1, l2, bl1, bl2; + int rc, c1, bc1; + const char *query= "SELECT * FROM t1 ORDER BY c2 DESC"; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1(c1 int primary key auto_increment, c2 char(10))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1(c2) values('venu'), ('mysql')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc,stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc,stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc,stmt); + + rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0); /* No-op at this point */ + FAIL_IF(!rc, "Error expected"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + 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_stmt_rc(rc,stmt); + FAIL_IF(!(strncmp(c2, "venu", 4) == 0 && l2 == 4), "Expected c2='venu'"); + + c2[0]= '\0'; l2= 0; + rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0); + check_stmt_rc(rc,stmt); + FAIL_IF(!(strcmp(c2, "venu") == 0 && l2 == 4), "Expected c2='venu'"); + + 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_stmt_rc(rc,stmt); + FAIL_IF(!(c1 == 1 && l1 == 4), "Expected c1=1"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + + 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_stmt_rc(rc,stmt); + FAIL_IF(!(strncmp(c2, "mysq", 4) == 0 && l2 == 5), "Expected c2='mysql'"); + + c2[0]= '\0'; l2= 0; + rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0); + check_stmt_rc(rc,stmt); + FAIL_IF(!(strcmp(c2, "mysql") == 0 && l2 == 5), "Expected c2='mysql'"); + + 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_stmt_rc(rc,stmt); + FAIL_IF(!(c1 == 2 && l1 == 4), "Expected c2=2"); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc!=MYSQL_NO_DATA, "Expected MYSQL_NO_DATA"); + + rc= mysql_stmt_fetch_column(stmt, my_bind, 1, 0); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Test fetch without prior bound buffers */ + +static int test_fetch_nobuffs(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[4]; + char str[4][50]; + int rc; + const char *query = "SELECT DATABASE(), CURRENT_USER(), \ + CURRENT_DATE(), CURRENT_TIME()"; + + stmt = mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + + FAIL_IF(rc != 1, "Expected 1 row"); + + memset(my_bind, '\0', sizeof(my_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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + { + rc++; + } + FAIL_IF(rc != 1, "Expected 1 row"); + + mysql_stmt_close(stmt); + + return OK; +} + +/* Test fetch null */ + +static int test_fetch_null(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + int i; + int nData= 0; + MYSQL_BIND my_bind[11]; + ulong length[11]; + my_bool is_null[11]; + char query[MAX_TEST_QUERY_LENGTH]; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_fetch_null"); + check_mysql_rc(rc, mysql); + + 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))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_fetch_null (col11) " + "VALUES (1000), (88), (389789)"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + FAIL_IF(rc, mysql_error(mysql)); + + /* fetch */ + memset(my_bind, '\0', 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 */ + + strcpy((char *)query , "SELECT * FROM test_fetch_null"); + + rc= my_stmt_result(mysql, query); + FAIL_UNLESS(rc == 3, "Expected 3 rows"); + + stmt = mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + { + rc++; + for (i= 0; i < 10; i++) + { + FAIL_IF(!is_null[i], "Expected is_null"); + } + FAIL_UNLESS(nData == 1000 || nData == 88 || nData == 389789, "Wrong value for nData"); + FAIL_UNLESS(is_null[i] == 0, "Expected !is_null"); + FAIL_UNLESS(length[i] == 4, "Expected length=4"); + } + FAIL_UNLESS(rc == 3, "Expected 3 rows"); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE test_fetch_null"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Test fetching of date, time and ts */ + +static int test_fetch_date(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + uint i; + int rc; + long 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]; + const char *query= "SELECT * FROM test_bind_result"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_bind_result(c1 date, c2 time, \ + c3 timestamp, \ + c4 year, \ + c5 datetime, \ + c6 timestamp, \ + c7 timestamp)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET SQL_MODE=''"); + check_mysql_rc(rc, mysql); + 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')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + FAIL_IF(rc, mysql_error(mysql)); + + memset(my_bind, '\0', 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(mysql, "SELECT * FROM test_bind_result"); + FAIL_UNLESS(rc == 1, "Expected 1 row"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + ts_4[0]= '\0'; + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(strcmp(date, "2002-01-02") == 0, "date != '2002-01-02'"); + FAIL_UNLESS(d_length == 10, "d_length != 10"); + + FAIL_UNLESS(strcmp(my_time, "12:49:00") == 0, "mytime != '12:49:00'"); + FAIL_UNLESS(t_length == 8, "t_length != 8"); + + FAIL_UNLESS(strcmp(ts, "2002-01-02 17:46:59") == 0, "ts != '2002-01-02 17:46:59'"); + FAIL_UNLESS(ts_length == 19, "ts_length != 19"); + + FAIL_UNLESS(strcmp(dt, "2010-07-10 00:00:00") == 0, "dt != 2010-07-10 00:00:00"); + FAIL_UNLESS(dt_length == 19, "dt_length != 19"); + + FAIL_UNLESS(strcmp(ts_4, "0000-00-00 00:00:00") == 0, "ts4 != '0000-00-00 00:00:00'"); + FAIL_UNLESS(ts4_length == strlen("0000-00-00 00:00:00"), "ts4_length != 19"); + + FAIL_UNLESS(strcmp(ts_6, "1999-12-29 00:00:00") == 0, "ts_6 != '1999-12-29 00:00:00'"); + FAIL_UNLESS(ts6_length == 19, "ts6_length != 19"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Test fetching of str to all types */ + +static int test_fetch_str(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + check_mysql_rc(rc, mysql); + + 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))"); + check_mysql_rc(rc, mysql); + + rc= bind_fetch(mysql, 3); + mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + return rc; +} + +/* Test fetching of long to all types */ + +static int test_fetch_long(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + check_mysql_rc(rc, mysql); + 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)"); + check_mysql_rc(rc, mysql); + rc= bind_fetch(mysql, 4); + mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + return rc; +} + + +/* Test fetching of short to all types */ + +static int test_fetch_short(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + check_mysql_rc(rc, mysql); + 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)"); + check_mysql_rc(rc, mysql); + rc= bind_fetch(mysql, 5); + mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + return rc; +} + + +/* Test fetching of tiny to all types */ + +static int test_fetch_tiny(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + check_mysql_rc(rc, mysql); + + 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)"); + check_mysql_rc(rc, mysql); + rc= bind_fetch(mysql, 3); + mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + return rc; +} + + +/* Test fetching of longlong to all types */ + +static int test_fetch_bigint(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + check_mysql_rc(rc, mysql); + + 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)"); + check_mysql_rc(rc, mysql); + rc= bind_fetch(mysql, 2); + mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + return rc; +} + + +/* Test fetching of float to all types */ + +static int test_fetch_float(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + check_mysql_rc(rc, mysql); + + 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)"); + check_mysql_rc(rc, mysql); + + rc= bind_fetch(mysql, 2); + mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + return rc; +} + + +/* Test fetching of double to all types */ + +static int test_fetch_double(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + check_mysql_rc(rc, mysql); + 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)"); + check_mysql_rc(rc, mysql); + rc= bind_fetch(mysql, 3); + mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_fetch"); + return rc; +} + +static int test_conc281(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_BIND bind[2]; + unsigned long length= 0; + char buffer[2048]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc282"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE conc282 (a blob, b varchar(1000), c int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO conc282 VALUES (REPEAT('A',2000), REPEAT('B', 999),3)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, "SELECT a, b FROM conc282", -1); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND) * 2); + + bind[0].buffer_type= MYSQL_TYPE_BLOB; + bind[0].buffer= buffer; + bind[0].buffer_length= 2048; + bind[0].length= &length; + + rc= mysql_stmt_fetch_column(stmt, &bind[0], 0, 0); + check_stmt_rc(rc, stmt); + + FAIL_IF(length != 2000, "Expected length= 2000"); + FAIL_IF(buffer[0] != 'A' || buffer[1999] != 'A', "Wrong result"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE conc282"); + check_mysql_rc(rc, mysql); + + return OK; + +} + +struct my_tests_st my_tests[] = { + {"test_conc281", test_conc281, 1, 0, NULL, NULL}, + {"test_fetch_seek", test_fetch_seek, 1, 0, NULL , NULL}, + {"test_fetch_offset", test_fetch_offset, 1, 0, NULL , NULL}, + {"test_fetch_column", test_fetch_column, 1, 0, NULL , NULL}, + {"test_fetch_nobuffs", test_fetch_nobuffs, 1, 0, NULL , NULL}, + {"test_fetch_null", test_fetch_null, 1, 0, NULL , NULL}, + {"test_fetch_date", test_fetch_date, 1, 0, NULL , NULL}, + {"test_fetch_str", test_fetch_str, 1, 0, NULL , NULL}, + {"test_fetch_long", test_fetch_long, 1, 0, NULL , NULL}, + {"test_fetch_short", test_fetch_short, 1, 0, NULL , NULL}, + {"test_fetch_tiny", test_fetch_tiny, 1, 0, NULL , NULL}, + {"test_fetch_bigint", test_fetch_bigint, 1, 0, NULL , NULL}, + {"test_fetch_float", test_fetch_float, 1, 0, NULL , NULL}, + {"test_fetch_double", test_fetch_double, 1, 0, NULL , NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/fingerprint.list.in b/libmariadb/unittest/libmariadb/fingerprint.list.in new file mode 100644 index 00000000..06f2be28 --- /dev/null +++ b/libmariadb/unittest/libmariadb/fingerprint.list.in @@ -0,0 +1,4 @@ +86DD6D764C0CA47C5014E1E7802674BFDB674ED3 +@SSL_CERT_FINGER_PRINT@ +73F1FEC1FE041473563BFF2D624B88F6CFCFE626 + diff --git a/libmariadb/unittest/libmariadb/getopt.c b/libmariadb/unittest/libmariadb/getopt.c new file mode 100644 index 00000000..50debeff --- /dev/null +++ b/libmariadb/unittest/libmariadb/getopt.c @@ -0,0 +1,742 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 + Free Software Foundation, Inc. + +Changes by monty: +- Added include of string.h when necessary. +- Removed two warnings from gcc. + +This file is part of the GNU C Library. Its master source is NOT part of +the C library, however. The master source lives in /gd/gnu/lib. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. + Ditto for AIX 3.2 and <stdlib.h>. */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if (!defined (__STDC__) || !__STDC__) && !defined(MSDOS) && !defined(OS2) +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include <ma_global.h> /* Changes for mysys */ +#include <ma_string.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include <stdlib.h> +#endif /* GNU C library. */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "ma_getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include <string.h> +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +static char * +my_index (const char *str, int chr) +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv) +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (const char *optstring) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) +{ + optarg = NULL; + + if (optind == 0) + optstring = _getopt_initialize (optstring); + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0')) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound=0; /* Keep gcc happy */ + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((size_t) (nameend - nextchar) == (size_t) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/libmariadb/unittest/libmariadb/logs.c b/libmariadb/unittest/libmariadb/logs.c new file mode 100644 index 00000000..60554a06 --- /dev/null +++ b/libmariadb/unittest/libmariadb/logs.c @@ -0,0 +1,223 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" + +#ifdef ENABLE_IF_IN_USE +static int enable_general_log(MYSQL *mysql, int truncate) +{ + int rc; + + rc= mysql_query(mysql, "set @save_global_general_log=@@global.general_log"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "set @@global.general_log=on"); + check_mysql_rc(rc, mysql); + + if (truncate) + { + rc= mysql_query(mysql, "truncate mysql.general_log"); + check_mysql_rc(rc, mysql); + } + + return OK; +} + + +static int restore_general_log(MYSQL *mysql) +{ + int rc; + rc= mysql_query(mysql, "set @@global.general_log=@save_global_general_log"); + check_mysql_rc(rc, mysql); + + return OK; +} +#endif + +/* Test update/binary logs */ + +static int test_logs(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + char data[255]; + size_t length; + int rc; + short id; + + rc= mysql_query(mysql, "SET session sql_mode=''"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_logs"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_logs(id smallint, name varchar(20))"); + check_mysql_rc(rc, mysql); + + strcpy((char *)data, "INSERT INTO test_logs VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(data)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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= (unsigned long *)&length; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + id= 9876; + strcpy((char *)data, "MySQL - Open Source Database"); + length= strlen(data); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + strcpy((char *)data, "'"); + length= 1; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + strcpy((char *)data, "\""); + length= 1; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + strcpy((char *)data, "my\'sql\'"); + length= strlen(data); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + strcpy((char *)data, "my\"sql\""); + length= strlen(data); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + strcpy((char *)data, "INSERT INTO test_logs VALUES(20, 'mysql')"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(data)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + strcpy((char *)data, "SELECT * FROM test_logs WHERE id=?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(data)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + my_bind[1].buffer_length= 255; + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(id == 9876, "id != 9876"); + FAIL_UNLESS(length == 19 || length == 20, "Invalid Length"); /* Due to VARCHAR(20) */ + FAIL_UNLESS(strncmp(data, "MySQL - Open Source", 19) == 0, "data != 'MySQL - Open Source'"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(length == 1, "length != 1"); + FAIL_UNLESS(strcmp(data, "'") == 0, "data != '''"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(length == 1, "length != 1"); + FAIL_UNLESS(strcmp(data, "\"") == 0, "data != '\"'"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(length == 7, "length != 7"); + FAIL_UNLESS(strcmp(data, "my\'sql\'") == 0, "data != my'sql'"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(length == 7, "length != 7"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE test_logs"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_logs", test_logs, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/ma_getopt.c b/libmariadb/unittest/libmariadb/ma_getopt.c new file mode 100644 index 00000000..186cdcd5 --- /dev/null +++ b/libmariadb/unittest/libmariadb/ma_getopt.c @@ -0,0 +1,741 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 + Free Software Foundation, Inc. + +Changes by monty: +- Added include of string.h when necessary. +- Removed two warnings from gcc. + +This file is part of the GNU C Library. Its master source is NOT part of +the C library, however. The master source lives in /gd/gnu/lib. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. + Ditto for AIX 3.2 and <stdlib.h>. */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if (!defined (__STDC__) || !__STDC__) && !defined(MSDOS) && !defined(OS2) +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include <stdlib.h> +#endif /* GNU C library. */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "ma_getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include <string.h> +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +static char * +my_index (const char *str, int chr) +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv) +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (const char *optstring) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) +{ + optarg = NULL; + + if (optind == 0) + optstring = _getopt_initialize (optstring); + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0')) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound=0; /* Keep gcc happy */ + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((size_t) (nameend - nextchar) == (size_t) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/libmariadb/unittest/libmariadb/ma_getopt.h b/libmariadb/unittest/libmariadb/ma_getopt.h new file mode 100644 index 00000000..e169e829 --- /dev/null +++ b/libmariadb/unittest/libmariadb/ma_getopt.h @@ -0,0 +1,131 @@ +/* Declarations for getopt. + Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc. + +This file is part of the GNU C Library. Its master source is NOT part of +the C library, however. The master source lives in /gd/gnu/lib. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ || defined(__cplusplus) + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if ( defined (__STDC__) && __STDC__ ) || defined(__cplusplus) || defined(MSDOS) +#ifdef __EMX__ +int getopt (int, char **, __const__ char *); +#elif defined( __GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (int argc, char *const *argv, const char *optstring); +#endif /* __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (int argc, char *const *argv, const char *optstring); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/libmariadb/unittest/libmariadb/misc.c b/libmariadb/unittest/libmariadb/misc.c new file mode 100644 index 00000000..f67d1366 --- /dev/null +++ b/libmariadb/unittest/libmariadb/misc.c @@ -0,0 +1,1689 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" +#include "ma_common.h" + +#include <mysql/client_plugin.h> + + +/* + Bug#28075 "COM_DEBUG crashes mysqld" +*/ +#ifdef _WIN32 +#define R_OK 4 +#endif + +static int test_bug28075(MYSQL *mysql) +{ + int rc; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + SKIP_XPAND; + + rc= mysql_dump_debug_info(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_ping(mysql); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + Bug#28505: mysql_affected_rows() returns wrong value if CLIENT_FOUND_ROWS + flag is set. +*/ + +static int test_bug28505(MYSQL *mysql) +{ + unsigned long long res; + int rc; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1(f1 int primary key)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values(1)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values(1) on duplicate key update f1=1"); + check_mysql_rc(rc, mysql); + res= mysql_affected_rows(mysql); + FAIL_UNLESS(!res, "res != 0"); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + Bug #29692 Single row inserts can incorrectly report a huge number of + row insertions +*/ + +static int test_bug29692(MYSQL *mysql) +{ + int rc; + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1(f1 int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values(1)"); + check_mysql_rc(rc, mysql); + FAIL_UNLESS(1 == mysql_affected_rows(mysql), "affected_rows != 1"); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int bug31418_impl() +{ + my_bool is_null; + MYSQL *mysql; + int rc; + + + /* Create a new connection. */ + + mysql= test_connect(NULL); + if (!mysql) + return FAIL; + + /*********************************************************************** + Check that lock is free: + - IS_FREE_LOCK() should return 1; + - IS_USED_LOCK() should return NULL; + ***********************************************************************/ + + is_null= query_int_variable(mysql, + "IS_FREE_LOCK('bug31418')", + &rc); + FAIL_UNLESS(!is_null && rc, "rc = 0"); + + is_null= query_int_variable(mysql, + "IS_USED_LOCK('bug31418')", + &rc); + FAIL_UNLESS(is_null, "rc = 0"); + + /*********************************************************************** + 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(mysql, "GET_LOCK('bug31418', 1)", &rc); + FAIL_UNLESS(rc, "rc = 0"); + + is_null= query_int_variable(mysql, + "IS_FREE_LOCK('bug31418')", + &rc); + FAIL_UNLESS(!is_null && !rc, "rc = 0"); + + is_null= query_int_variable(mysql, + "IS_USED_LOCK('bug31418')", + &rc); + FAIL_UNLESS(!is_null && rc, "rc = 0"); + + /*********************************************************************** + 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; + **********************************************************************/ + + rc= mysql_change_user(mysql, username, password, schema ? schema : "test"); + check_mysql_rc(rc, mysql); + + is_null= query_int_variable(mysql, + "IS_FREE_LOCK('bug31418')", + &rc); + FAIL_UNLESS(!is_null && rc, "rc = 0"); + + is_null= query_int_variable(mysql, + "IS_USED_LOCK('bug31418')", + &rc); + FAIL_UNLESS(is_null, "rc = 0"); + + /*********************************************************************** + That's it. Cleanup. + ***********************************************************************/ + + mysql_close(mysql); + return OK; +} + +static int test_bug31418(MYSQL *unused __attribute__((unused))) +{ + int i; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + /* Run test case for BUG#31418 for three different connections. */ + + for (i=0; i < 3; i++) + if (bug31418_impl()) + return FAIL; + + return OK; +} + +/* Query processing */ + +static int test_debug_example(MYSQL *mysql) +{ + int rc; + MYSQL_RES *result; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_debug_example"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_debug_example(" + "id INT PRIMARY KEY AUTO_INCREMENT, " + "name VARCHAR(20), xxx INT)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_debug_example (name) " + "VALUES ('mysql')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "UPDATE test_debug_example SET name='updated' " + "WHERE name='deleted'"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT * FROM test_debug_example where name='mysql'"); + check_mysql_rc(rc, mysql); + + result= mysql_use_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + while (mysql_fetch_row(result)); + mysql_free_result(result); + + rc= mysql_query(mysql, "DROP TABLE test_debug_example"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + Test a crash when invalid/corrupted .frm is used in the + SHOW TABLE STATUS + bug #93 (reported by serg@mysql.com). +*/ + +static int test_frm_bug(MYSQL *mysql) +{ + 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[1024]; + int rc; + + SKIP_MYSQL(mysql); + SKIP_XPAND; + + mysql_autocommit(mysql, TRUE); + + rc= mysql_query(mysql, "drop table if exists test_frm_bug"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "flush tables"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("show variables like 'datadir'")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + snprintf(test_frm, sizeof(test_frm)-1, "%s/%s/test_frm_bug.frm", data_dir, schema); + + if (!(test_file= fopen(test_frm, "w"))) + { + mysql_stmt_close(stmt); + diag("Can't write to file %s -> SKIP", test_frm); + return SKIP; + } + + rc= mysql_query(mysql, "SHOW TABLE STATUS like 'test_frm_bug'"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set");/* It can't be NULL */ + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + mysql_data_seek(result, 0); + + row= mysql_fetch_row(result); + FAIL_IF(!row, "couldn't fetch row"); + + FAIL_UNLESS(row[17] != 0, "row[17] != 0"); + + mysql_free_result(result); + mysql_stmt_close(stmt); + + fclose(test_file); + mysql_query(mysql, "drop table if exists test_frm_bug"); + unlink(test_frm); + return OK; +} + +static int test_wl4166_1(MYSQL *mysql) +{ + 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]; + const char *query; + int rc; + int i; + + if (mysql_get_server_version(mysql) < 50100) { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + rc= mysql_query(mysql, "DROP TABLE IF EXISTS table_4166"); + check_mysql_rc(rc, mysql); + + 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)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + query= "INSERT INTO table_4166(col1, col2, col3, col4, col5, col6, col7) " + "VALUES(?, ?, ?, ?, ?, ?, ?)"; + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 7, "param_count != 7"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + 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"); + check_mysql_rc(rc, mysql); + + /* + 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_stmt_rc(rc, stmt); + 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"); + check_mysql_rc(rc, mysql); + return OK; +} + + +static int test_wl4166_2(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int c_int; + MYSQL_TIME d_date; + MYSQL_BIND bind_out[2]; + int rc; + + if (mysql_get_server_version(mysql) < 50100) { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (c_int int, d_date date)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, + "insert into t1 (c_int, d_date) values (42, '1948-05-15')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("select * from t1")); + check_stmt_rc(rc, stmt); + + memset(bind_out, '\0', 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_stmt_rc(rc, stmt); + + /* int -> varchar transition */ + + rc= mysql_query(mysql, + "alter table t1 change column c_int c_int varchar(11)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(c_int == 42, "c_int != 42"); + FAIL_UNLESS(d_date.year == 1948, "y!=1948"); + FAIL_UNLESS(d_date.month == 5, "m != 5"); + FAIL_UNLESS(d_date.day == 15, "d != 15"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + /* varchar to int retrieval with truncation */ + + rc= mysql_query(mysql, "update t1 set c_int='abcde'"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(!rc, "Error expected"); + + FAIL_UNLESS(c_int == 0, "c != 0"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + /* alter table and increase the number of columns */ + rc= mysql_query(mysql, "alter table t1 add column d_int int"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(!rc, "Error expected"); + + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + + /* decrease the number of columns */ + rc= mysql_query(mysql, "alter table t1 drop d_date, drop d_int"); + check_mysql_rc(rc, mysql); + rc= mysql_stmt_execute(stmt); + diag("rc=%d error: %d\n", rc, mysql_stmt_errno(stmt)); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/** + Test how warnings generated during assignment of parameters + are (currently not) preserve in case of reprepare. +*/ + +static int test_wl4166_3(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + MYSQL_TIME tm[1]; + + if (mysql_get_server_version(mysql) < 50100) { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (year datetime)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("insert into t1 (year) values (?)")); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "param_count != 1"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + tm[0].year= 2014; + 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"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + diag("rc=%d %s", rc, mysql_stmt_error(stmt)); + check_stmt_rc(rc, stmt); + + if (verify_col_data(mysql, "t1", "year", "2014-01-01 01:01:01")) { + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + return FAIL; + } + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + + +/** + Test that long data parameters, as well as parameters + that were originally in a different character set, are + preserved in case of reprepare. +*/ + +static int test_wl4166_4(MYSQL *mysql) +{ + 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; + + SKIP_XPAND; + + if (mysql_get_server_version(mysql) < 50100) { + diag("Test requires MySQL Server version 5.1 or above"); + return SKIP; + } + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + /* + 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))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "set character_set_client=koi8r, " + "character_set_connection=cp1251, " + "character_set_results=koi8r"); + check_mysql_rc(rc, mysql); + + memset(bind_array, '\0', 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= (unsigned long)strlen(koi8); + + stmt= mysql_stmt_init(mysql); + check_stmt_rc(rc, stmt); + + stmt_text= "insert into t1 (c1, c2) values (?, ?)"; + + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + mysql_stmt_bind_param(stmt, bind_array); + + mysql_stmt_send_long_data(stmt, 0, koi8, (unsigned long)strlen(koi8)); + + /* Cause a reprepare at statement execute */ + rc= mysql_query(mysql, "alter table t1 add column d int"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + + FAIL_UNLESS(buf1_len == strlen(cp1251), ""); + FAIL_UNLESS(buf2_len == strlen(cp1251), ""); + FAIL_UNLESS(!memcmp(buf1, cp1251, buf1_len), ""); + FAIL_UNLESS(!memcmp(buf2, cp1251, buf1_len), ""); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, ""); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "set names default"); + check_mysql_rc(rc, mysql); + return OK; +} + +/** + Test that COM_REFRESH issues a implicit commit. +*/ + +static int test_wl4284_1(MYSQL *mysql) +{ + int rc; + MYSQL_ROW row; + MYSQL_RES *result; + + diag("Test temporarily disabled"); + return SKIP; + + if (mysql_get_server_version(mysql) < 60000) { + diag("Test requires MySQL Server version 6.0 or above"); + return SKIP; + } + + /* set AUTOCOMMIT to OFF */ + rc= mysql_autocommit(mysql, FALSE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS trans"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE trans (a INT) ENGINE=InnoDB"); + + if (mysql_errno(mysql) == ER_UNKNOWN_STORAGE_ENGINE) + { + diag("InnoDB not configured or available"); + return SKIP; + } + + check_mysql_rc(rc, mysql); + + + rc= mysql_query(mysql, "INSERT INTO trans VALUES(1)"); + check_mysql_rc(rc, mysql); + + rc= mysql_refresh(mysql, REFRESH_GRANT | REFRESH_TABLES); + check_mysql_rc(rc, mysql); + + rc= mysql_rollback(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT * FROM trans"); + check_mysql_rc(rc, mysql); + + result= mysql_use_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + row= mysql_fetch_row(result); + FAIL_IF(!row, "Can't fetch row"); + + mysql_free_result(result); + + /* set AUTOCOMMIT to OFF */ + rc= mysql_autocommit(mysql, FALSE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE trans"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug49694(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res; + MYSQL_ROW row; + int i; + FILE *fp; + + diag("Load local infile server : %ld", (mysql->server_capabilities & CLIENT_LOCAL_FILES)); + diag("Load local infile client : %ld", (mysql->client_flag & CLIENT_LOCAL_FILES)); + + SKIP_LOAD_INFILE_DISABLE; + SKIP_SKYSQL; + + /* XPT-600: local_infile variable not supported */ + SKIP_XPAND; + + rc= mysql_query(mysql, "select @@LOCAL_INFILE"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + if (atol(row[0]) == 0) { + diag("Load local infile disable"); + return SKIP; + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS enclist"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE `enclist` (" + " `pat_id` int(11) NOT NULL," + " `episode_id` int(11) NOT NULL," + " `enc_id` double NOT NULL," + " PRIMARY KEY (`pat_id`,`episode_id`,`enc_id`)" + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"); + check_mysql_rc(rc, mysql); + + fp= fopen("data.csv", "w"); + FAIL_IF(!fp, "Can't open data.csv"); + + for (i=0; i < 100; i++) + fprintf (fp, "%.08d,%d,%f\r\n", 100 + i, i % 3 + 1, 60000.0 + i/100); + fclose(fp); + + rc= mysql_query(mysql, "LOAD DATA LOCAL INFILE 'data.csv' INTO TABLE enclist " + "FIELDS TERMINATED BY '.' LINES TERMINATED BY '\r\n'"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DELETE FROM enclist"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != 100, "Import failure. Expected 2 imported rows"); + + rc= mysql_query(mysql, "DROP TABLE enclist"); + check_mysql_rc(rc, mysql); + mysql_free_result(res); + return OK; +} + +static int test_conc49(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res; + MYSQL_ROW row; + + int i; + FILE *fp; + + SKIP_LOAD_INFILE_DISABLE; + SKIP_SKYSQL; + SKIP_XPAND; + + rc= mysql_query(mysql, "select @@LOCAL_INFILE"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + + i= !atol(row[0]); + if (i) { + diag("Load local infile disable"); + mysql_free_result(res); + return SKIP; + } + mysql_free_result(res); + + fp= fopen("./sample.csv", "w"); + for (i=1; i < 4; i++) + fprintf(fp, "\"%d\", \"%d\", \"%d\"\r\n", i, i, i); + fclose(fp); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc49"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE conc49 (a int, b int, c int) Engine=InnoDB DEFAULT CHARSET=latin1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "LOAD DATA LOCAL INFILE './sample.csv' INTO TABLE conc49 FIELDS ESCAPED BY ' ' TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\r\n'"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT a FROM conc49"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + rc= (int)mysql_num_rows(res); + mysql_free_result(res); + FAIL_IF(rc != 3, "3 rows expected"); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc49"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_ldi_path(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + +#ifdef _WIN32 + rc= mysql_query(mysql, "LOAD DATA LOCAL INFILE 'X:/non_existing_path/data.csv' INTO TABLE t1 " + "FIELDS TERMINATED BY '.' LINES TERMINATED BY '\r\n'"); +#else + rc= mysql_query(mysql, "LOAD DATA LOCAL INFILE '/non_existing_path/data.csv' INTO TABLE t1 " + "FIELDS TERMINATED BY '.' LINES TERMINATED BY '\r\n'"); +#endif + FAIL_IF(rc== 0, "Error expected"); + diag("Error: %d", mysql_errno(mysql)); + FAIL_IF(mysql_errno(mysql) == 0, "Error expected"); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +#if _WIN32 +static int test_conc44(MYSQL *mysql) +{ + char query[1024]; + char *a_filename= "æøå.csv"; + int rc; + int i; + FILE *fp; + + rc= mysql_set_character_set(mysql, "latin1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS enclist"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE `enclist` (" + " `pat_id` int(11) NOT NULL," + " `episode_id` int(11) NOT NULL," + " `enc_id` double NOT NULL," + " PRIMARY KEY (`pat_id`,`episode_id`,`enc_id`)" + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"); + check_mysql_rc(rc, mysql); + + fp= fopen(a_filename, "w"); + FAIL_IF(!fp, "Can't open file"); + + for (i=0; i < 100; i++) + fprintf (fp, "%.08d,%d,%f\r\n", 100 + i, i % 3 + 1, 60000.0 + i/100); + fclose(fp); + + sprintf(query, "LOAD DATA LOCAL INFILE '%s' INTO TABLE enclist " + "FIELDS TERMINATED BY '.' LINES TERMINATED BY '\r\n'", a_filename); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DELETE FROM enclist"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != 100, "Import failure. Expected 2 imported rows"); + + rc= mysql_query(mysql, "DROP TABLE enclist"); + check_mysql_rc(rc, mysql); + return OK; +} +#endif + +static int test_connect_attrs(MYSQL *my) +{ + MYSQL *mysql; + MYSQL_RES *result; + int rc, len; + + rc= mysql_query(my, "SELECT * FROM performance_schema.session_connect_attrs LIMIT 1"); + if (rc != 0) + { + diag("Server doesn't support connection attributes"); + return SKIP; + } + + result= mysql_store_result(my); + /* MariaDB Connector/C already sent connection attrs after handshake. So if the table is + empty, it indicates that the performance schema is disabled */ + if (!mysql_num_rows(result)) + { + diag("skip: performance_schema not enabled"); + mysql_free_result(result); + return SKIP; + } + mysql_free_result(result); + + mysql= mysql_init(NULL); + + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo0", "bar0"); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo1", "bar1"); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo2", "bar2"); + + FAIL_IF(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + if (!(mysql->server_capabilities & CLIENT_CONNECT_ATTRS)) + { + diag("Server doesn't support connection attributes"); + return SKIP; + } + + rc= mysql_query(mysql, "SELECT * FROM performance_schema.session_connect_attrs where attr_name like 'foo%'"); + check_mysql_rc(rc, mysql); + result= mysql_store_result(mysql); + rc= (int)mysql_num_rows(result); + mysql_free_result(result); + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, NULL); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo0", "bar0"); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo1", "bar1"); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo2", "bar2"); + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "foo0"); + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "foo1"); + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "foo2"); + + len= (int)mysql->options.extension->connect_attrs_len; + + mysql_close(mysql); + + FAIL_IF(rc < 3, "Expected 3 or more rows"); + FAIL_IF(len != 0, "Expected connection_attr_len=0"); + + return OK; +} + +static int test_conc_114(MYSQL *mysql) +{ + if (mysql_client_find_plugin(mysql, "foo", 0)) + { + diag("Null pointer expected"); + return FAIL; + } + diag("Error: %s", mysql_error(mysql)); + return OK; +} + +/* run with valgrind */ +static int test_conc117(MYSQL *unused __attribute__((unused))) +{ + my_bool reconnect= 1; + MYSQL *my= mysql_init(NULL); + SKIP_MAXSCALE; + FAIL_IF(!my_test_connect(my, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + mysql_kill(my, mysql_thread_id(my)); + + mysql_options(my, MYSQL_OPT_RECONNECT, &reconnect); + + mysql_query(my, "SET @a:=1"); + mysql_close(my); + + return OK; +} + +static int test_read_timeout(MYSQL *unused __attribute__((unused))) +{ + int timeout= 5, rc; + MYSQL *my= mysql_init(NULL); + SKIP_MAXSCALE; + mysql_options(my, MYSQL_OPT_READ_TIMEOUT, &timeout); + FAIL_IF(!my_test_connect(my, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + rc= mysql_query(my, "SELECT SLEEP(50)"); + + FAIL_IF(rc == 0, "error expected"); + diag("error: %s", mysql_error(my)); + + mysql_close(my); + + return OK; +} + +#ifndef __has_feature +# define __has_feature(x) 0 +#endif +#if !__has_feature(memory_sanitizer) +#ifdef HAVE_REMOTEIO +void *remote_plugin; +static int test_remote1(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res; + MYSQL_ROW row; + SKIP_SKYSQL; + SKIP_XPAND; + + remote_plugin= (void *)mysql_client_find_plugin(mysql, "remote_io", MARIADB_CLIENT_REMOTEIO_PLUGIN); + if (!remote_plugin) + { + diag("skip - no remote io plugin available"); + diag("error: %s", mysql_error(mysql)); + return SKIP; + } + + SKIP_LOAD_INFILE_DISABLE; + + rc= mysql_query(mysql, "select @@LOCAL_INFILE"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + if (atol(row[0]) == 0) { + diag("Load local infile disable"); + return SKIP; + } + mysql_free_result(res); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a text)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "LOAD DATA LOCAL INFILE 'http://www.example.com' INTO TABLE t1"); + if (rc && mysql_errno(mysql) == 2058) + { + diag("remote_io plugin not available"); + return SKIP; + } + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_remote2(MYSQL *my) +{ + MYSQL *mysql; + + if (!remote_plugin) + { + diag("skip - no remote io plugin available"); + return SKIP; + } + mysql= mysql_init(NULL); + + mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "http://localhost/test.cnf"); + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "test"); + my_test_connect(mysql, hostname, username, password, schema, + 0, socketname, 0), mysql_error(my); + diag("port: %d", mysql->port); + mysql_close(mysql); + return OK; +} +#endif +#endif + +#ifndef _WIN32 +static int test_mdev12965(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + my_bool reconnect = 0; + FILE *fp= NULL; + const char *env= getenv("MYSQL_TMP_DIR"); + char cnf_file1[FN_REFLEN + 1]; + + SKIP_SKYSQL; + if (travis_test) + return SKIP; + + if (!env) + env= "/tmp"; + + setenv("HOME", env, 1); + + snprintf(cnf_file1, FN_REFLEN, "%s%c.my.cnf", env, FN_LIBCHAR); + + diag("Config file: %s", cnf_file1); + + FAIL_IF(!access(cnf_file1, R_OK), "access"); + + mysql= mysql_init(NULL); + fp= fopen(cnf_file1, "w"); + FAIL_IF(!fp, "fopen"); + + fprintf(fp, "[client]\ndefault-character-set=latin2\nreconnect=1\n"); + fclose(fp); + + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0); + + remove(cnf_file1); + + FAIL_IF(strcmp(mysql_character_set_name(mysql), "latin2"), "expected charset latin2"); + mysql_get_optionv(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_IF(reconnect != 1, "expected reconnect=1"); + mysql_close(mysql); + return OK; +} +#endif + +static int test_get_info(MYSQL *mysql) +{ + size_t sval; + unsigned int ival; + char *cval; + int rc; + MY_CHARSET_INFO cs; + MARIADB_CHARSET_INFO *ci; + char **errors; + + rc= mariadb_get_infov(mysql, MARIADB_MAX_ALLOWED_PACKET, &sval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("max_allowed_packet: %lu", (unsigned long)sval); + rc= mariadb_get_infov(mysql, MARIADB_NET_BUFFER_LENGTH, &sval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("net_buffer_length: %lu", (unsigned long)sval); + rc= mariadb_get_infov(mysql, MARIADB_CLIENT_VERSION_ID, &sval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("client_version_id: %lu", (unsigned long)sval); + rc= mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_VERSION_ID, &sval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("server_version_id: %lu", (unsigned long)sval); + rc= mariadb_get_infov(mysql, MARIADB_CONNECTION_MARIADB_CHARSET_INFO, &cs); + FAIL_IF(rc, "mysql_get_info failed"); + diag("charset name: %s", cs.csname); + rc= mariadb_get_infov(mysql, MARIADB_CONNECTION_PVIO_TYPE, &ival); + FAIL_IF(rc, "mysql_get_info failed"); + diag("connection type: %d", ival); + rc= mariadb_get_infov(mysql, MARIADB_CONNECTION_PROTOCOL_VERSION_ID, &ival); + FAIL_IF(rc, "mysql_get_info failed"); + diag("protocol_version: %d", ival); + rc= mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_TYPE, &cval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("server_type: %s", cval); + rc= mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_VERSION, &cval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("server_version: %s", cval); + rc= mariadb_get_infov(mysql, MARIADB_CLIENT_VERSION, &cval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("client_version: %s", cval); + rc= mariadb_get_infov(mysql, MARIADB_CHARSET_NAME, &ci, "utf8"); + FAIL_IF(rc, "mysql_get_info failed"); + diag("charset_name: %s", ci->csname); + diag("charset_nr: %d", ci->nr); + rc= mariadb_get_infov(mysql, MARIADB_CHARSET_ID, &ci, 63); + FAIL_IF(rc, "mysql_get_info failed"); + diag("charset_name: %s", ci->csname); + rc= mariadb_get_infov(mysql, MARIADB_CLIENT_ERRORS, &errors); + FAIL_IF(rc, "mysql_get_info failed"); + diag("error[0]: %s", errors[0]); + rc= mysql_query(mysql, "DROP TABLE IF exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1),(2)"); + check_mysql_rc(rc, mysql); + rc= mariadb_get_infov(mysql, MARIADB_CONNECTION_INFO, &cval); + FAIL_IF(rc, "mysql_get_info failed"); + diag("mariadb_info: %s", cval); + return OK; +} + +static int test_zerofill(MYSQL *mysql) +{ + int rc; + MYSQL_ROW row; + MYSQL_RES *res; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int(10) zerofill)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + if ((res= mysql_store_result(mysql))) + { + row= mysql_fetch_row(res); + diag("zerofill: %s", row[0]); + mysql_free_result(res); + } + return OK; +} + +static int test_server_status(MYSQL *mysql) +{ + int rc; + unsigned int server_status; +// MYSQL_STMT *stmt; + + if (mysql_get_server_version(mysql) < 100200) + return SKIP; + +// stmt= mysql_stmt_init(mysql); + + rc= mysql_autocommit(mysql, 1); + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_STATUS, &server_status); + FAIL_IF(!(server_status & SERVER_STATUS_AUTOCOMMIT), + "autocommit flag not set"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int, b int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 (a) VALUES (1),(2),(3),(4),(5)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "UPDATE t1 SET a=9 WHERE a=8"); + check_mysql_rc(rc, mysql); + + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_STATUS, &server_status); + FAIL_IF(!(server_status & SERVER_QUERY_NO_INDEX_USED), "autocommit flag not set"); + + rc= mysql_query(mysql, "CREATE SCHEMA test_tmp"); + check_mysql_rc(rc, mysql); + + rc= mysql_select_db(mysql, "test_tmp"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP SCHEMA test_tmp"); + check_mysql_rc(rc, mysql); + + mariadb_get_infov(mysql, MARIADB_CONNECTION_SERVER_STATUS, &server_status); + FAIL_IF(!(server_status & SERVER_STATUS_DB_DROPPED), + "DB_DROP flag not set"); + + FAIL_IF(!(server_status & SERVER_SESSION_STATE_CHANGED), + "SESSION_STATE_CHANGED flag not set"); + + rc= mysql_select_db(mysql, schema); + check_mysql_rc(rc, mysql); + +// mysql_stmt_close(stmt); + + return OK; +} + +static int test_wl6797(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *stmt_text; + my_ulonglong res; + + if (mysql_get_server_version(mysql) < 50703 || + (mariadb_connection(mysql) && mysql_get_server_version(mysql) < 100203)) + { + diag("Skipping test_wl6797: " + "tested feature does not exist in versions before MySQL 5.7.3 and MariaDB 10.2\n"); + return OK; + } + /* clean up the session */ + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + + /* do prepare of a query */ + mysql_query(mysql, "use test"); + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "CREATE TABLE t1 (a int)"); + + stmt= mysql_stmt_init(mysql); + stmt_text= "INSERT INTO t1 VALUES (1), (2)"; + + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + /* Execute the insert statement */ + rc= mysql_stmt_execute(stmt); + check_mysql_rc(rc, mysql); + + /* + clean the session this should remove the prepare statement + from the cache. + */ + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + + /* this below stmt should report error */ + rc= mysql_stmt_execute(stmt); + FAIL_IF(rc == 0, ""); + + /* + bug#17653288: MYSQL_RESET_CONNECTION DOES NOT RESET LAST_INSERT_ID + */ + + mysql_query(mysql, "DROP TABLE IF EXISTS t2"); + rc= mysql_query(mysql, "CREATE TABLE t2 (a int NOT NULL PRIMARY KEY"\ + " auto_increment)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t2 VALUES (null)"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 1, ""); + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + + rc= mysql_query(mysql, "INSERT INTO t2 VALUES (last_insert_id(100))"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 100, ""); + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "DROP TABLE IF EXISTS t2"); + mysql_stmt_close(stmt); + return OK; +} + +static int test_conc384(MYSQL *my __attribute__((unused))) +{ + char value[1000]; + int len; + MYSQL *mysql= mysql_init(NULL); + + memset(&value, 'A', 999); + value[999]= 0; + + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo", value); + len= (int)mysql->options.extension->connect_attrs_len; + /* Length: 1 (=len) + 3 (="foo") + 3 (=len) + 999 (="AAA...") = 1006 */ + FAIL_IF(len != 1006, "Wrong length"); + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "foo"); + len= (int)mysql->options.extension->connect_attrs_len; + /* Length should be zero after deleting the connection attribute */ + FAIL_IF(len != 0, "Wrong length"); + mysql_close(mysql); + return OK; +} + +#ifndef _WIN32 +static int test_conc395(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + FILE *fp= NULL; + const char *env= getenv("MYSQL_TMP_DIR"); + char cnf_file1[FN_REFLEN + 1]; + + SKIP_SKYSQL; + if (travis_test) + return SKIP; + + if (!env) + env= "/tmp"; + + setenv("HOME", env, 1); + + snprintf(cnf_file1, FN_REFLEN, "%s%c.my.cnf", env, FN_LIBCHAR); + + FAIL_IF(!access(cnf_file1, R_OK), "access"); + + mysql= mysql_init(NULL); + fp= fopen(cnf_file1, "w"); + FAIL_IF(!fp, "fopen"); + + /* Mix dash and underscore */ + fprintf(fp, "[client]\ndefault_character-set=latin2\n"); + fclose(fp); + + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0); + + remove(cnf_file1); + + FAIL_IF(strcmp(mysql_character_set_name(mysql), "latin2"), "expected charset latin2"); + mysql_close(mysql); + return OK; +} + +static int test_sslenforce(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + FILE *fp= NULL; + const char *env= getenv("MYSQL_TMP_DIR"); + char cnf_file1[FN_REFLEN + 1]; + + SKIP_NOTLS; + SKIP_SKYSQL; + + if (travis_test) + return SKIP; + + if (!env) + env= "/tmp"; + setenv("HOME", env, 1); + + snprintf(cnf_file1, FN_REFLEN, "%s%c.my.cnf", env, FN_LIBCHAR); + + FAIL_IF(!access(cnf_file1, R_OK), "access"); + + mysql= mysql_init(NULL); + fp= fopen(cnf_file1, "w"); + FAIL_IF(!fp, "fopen"); + + /* Mix dash and underscore */ + fprintf(fp, "[client]\nssl_enforce=1\n"); + fclose(fp); + + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0); + + remove(cnf_file1); + + FAIL_IF(!mysql_get_ssl_cipher(mysql), "no secure connection"); + mysql_close(mysql); + return OK; +} +#endif + +#if !__has_feature(memory_sanitizer) +static int test_conc457(MYSQL *mysql) +{ + MYSQL_RES *result; + + SKIP_MYSQL(mysql); + + result= mysql_list_processes(mysql); + + FAIL_IF(mysql_field_count(mysql) != 9, "expected 9 columns"); + mysql_free_result(result); + return OK; +} +#endif + +static int test_conc458(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + FAIL_IF(mysql_get_timeout_value(mysql) != 0, "expected timeout 0"); + mysql_close(mysql); + return OK; +} + + +static int test_conc533(MYSQL *mysql) +{ + my_bool skip= 1; + int rc; + MYSQL_RES *result; + MYSQL_ROW row; + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + char buffer[10]; + + rc= mysql_options(mysql, MARIADB_OPT_SKIP_READ_RESPONSE, &skip); + + rc= mysql_real_query(mysql, SL("SELECT 1")); + check_mysql_rc(rc, mysql); + + rc= mysql->methods->db_read_query_result(mysql); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + + FAIL_IF(strcmp(row[0], "1"), "Expected value \"1\""); + mysql_free_result(result); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL("SELECT 1")); + check_stmt_rc(rc, stmt); + + rc= mysql->methods->db_read_prepare_response(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_field_count(stmt) != 1, "Expected field_count= 1"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql->methods->db_read_execute_response(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer= buffer; + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer_length= 10; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(strcmp(buffer, "1"), "Expected value \"1\""); + + mysql_stmt_close(stmt); + + return OK; +} + +int display_extended_field_attribute(MYSQL *mysql) +{ + MYSQL_RES *result; + MYSQL_FIELD *fields; + + if (mysql_query(mysql, "CREATE TEMPORARY TABLE t1 (a POINT)")) + return 1; + + if (mysql_query(mysql, "SELECT a FROM t1")) + return 1; + + if (!(result= mysql_store_result(mysql))) + return 1; + + if ((fields= mysql_fetch_fields(result))) + { + MARIADB_CONST_STRING field_attr; + + if (!mariadb_field_attr(&field_attr, &fields[0], + MARIADB_FIELD_ATTR_DATA_TYPE_NAME)) + { + printf("Extended field attribute: %s\n", field_attr.str); + } + } + mysql_free_result(result); + return 0; +} + + +static int test_ext_field_attr(MYSQL *mysql) +{ + display_extended_field_attribute(mysql); + + return OK; +} + +struct my_tests_st my_tests[] = { + {"test_ext_field_attr", test_ext_field_attr, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc533", test_conc533, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc458", test_conc458, TEST_CONNECTION_NONE, 0, NULL, NULL}, +#if !__has_feature(memory_sanitizer) + {"test_conc457", test_conc457, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, +#endif + {"test_conc384", test_conc384, TEST_CONNECTION_NONE, 0, NULL, NULL}, +#ifndef _WIN32 + {"test_mdev12965", test_mdev12965, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc395", test_conc395, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_sslenforce", test_sslenforce, TEST_CONNECTION_NONE, 0, NULL, NULL}, +#endif + {"test_wl6797", test_wl6797, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_server_status", test_server_status, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_read_timeout", test_read_timeout, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_zerofill", test_zerofill, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, +#if !__has_feature(memory_sanitizer) +#ifdef HAVE_REMOTEIO + {"test_remote1", test_remote1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_remote2", test_remote2, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#endif +#endif + {"test_get_info", test_get_info, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc117", test_conc117, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc_114", test_conc_114, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc49", test_conc49, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_bug28075", test_bug28075, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug28505", test_bug28505, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_debug_example", test_debug_example, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug29692", test_bug29692, TEST_CONNECTION_NEW, CLIENT_FOUND_ROWS, NULL, NULL}, + {"test_bug31418", test_bug31418, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_frm_bug", test_frm_bug, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_wl4166_1", test_wl4166_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_wl4166_2", test_wl4166_2, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_wl4166_3", test_wl4166_3, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_wl4166_4", test_wl4166_4, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_wl4284_1", test_wl4284_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_bug49694", test_bug49694, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_ldi_path", test_ldi_path, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#ifdef _WIN32 + {"test_conc44", test_conc44, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#endif + {NULL, NULL, 0, 0, NULL, 0} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/my_test.h b/libmariadb/unittest/libmariadb/my_test.h new file mode 100644 index 00000000..9f75d67a --- /dev/null +++ b/libmariadb/unittest/libmariadb/my_test.h @@ -0,0 +1,770 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include <ma_global.h> +#include <ma_sys.h> +#include <mysql.h> +#include <tap.h> +#include "ma_getopt.h" +#include <memory.h> +#include <string.h> +#include <errmsg.h> +#include <stdlib.h> +#include <ma_server_error.h> +#include <mysql/client_plugin.h> +#include <errmsg.h> + +#ifndef WIN32 +#include <pthread.h> +#else +#include <io.h> +#define unlink _unlink +#endif + +#ifndef OK +# define OK 0 +#endif +#ifndef FAIL +# define FAIL 1 +#endif +#ifndef SKIP +# define SKIP -1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef TRUE +# define TRUE 1 +#endif + +#define IS_SKYSQL(a) ((a) && strstr((a), "skysql.mariadb.com")) +#define SKIP_SKYSQL \ +if (IS_SKYSQL(hostname)) \ +{ \ + diag("Not supported by SkySQL"); \ + return SKIP; \ +} + +#ifndef HAVE_SSL +#define SKIP_NOTLS \ +{ \ + diag("TLS not supported"); \ + return SKIP;\ +} +#else +#define SKIP_NOTLS +#endif + +#define SKIP_TLS \ +if (force_tls)\ +{\ + diag("Test doesn't work with TLS");\ + return SKIP;\ +} + +MYSQL *mysql_default = NULL; /* default connection */ + +#define IS_MAXSCALE()\ + ((mysql_default && strstr(mysql_get_server_info(mysql_default), "maxScale")) ||\ + (getenv("srv")!=NULL && (strcmp(getenv("srv"), "maxscale") == 0 ||\ + strcmp(getenv("srv"), "skysql-ha") == 0))) + +#define SKIP_MAXSCALE \ +if (IS_MAXSCALE()) \ +{ \ + diag("test disabled with maxscale"); \ + return SKIP; \ +} + +#define IS_XPAND()\ + ((mysql_default && strstr(mysql_get_server_info(mysql_default), "Xpand")) ||\ + (getenv("srv")!=NULL && strcmp(getenv("srv"), "xpand") == 0)) + +#define SKIP_XPAND \ +if (IS_XPAND()) \ +{ \ + diag("test disabled with Xpand"); \ + return SKIP; \ +} + +#define SKIP_LOAD_INFILE_DISABLE \ +if (!((mysql->server_capabilities & CLIENT_LOCAL_FILES) && \ + (mysql->options.client_flag & CLIENT_LOCAL_FILES)) || \ + IS_XPAND()) { \ + diag("Load local infile not supported"); \ + return SKIP; \ +} + +#define MAX_KEY MAX_INDEXES +#define MAX_KEY_LENGTH_DECIMAL_WIDTH 4 /* strlen("4096") */ + +#define SL(s) (s), (unsigned long)strlen((s)) +#define SL_BIN(s) (s), (unsigned long)sizeof((s)) + +#define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */ + +/* prevent warnings on Win64 by using STMT_LEN instead of strlen */ +#define STMT_LEN(A) ((unsigned long)strlen((A))) + +#define SKIP_TRAVIS()\ +do {\ + if (getenv("TRAVIS"))\ + {\ + diag("Skip test on Travis CI");\ + return SKIP;\ + }\ +}while(0) + +#define SKIP_MYSQL(mysql)\ +do {\ + if (!mariadb_connection(mysql))\ + {\ + diag("Skip test for non MariaDB server");\ + return OK;\ + }\ +} while(0) + +#define check_mysql_rc(rc, mysql) \ +do {\ + if (rc)\ + {\ + diag("Error (%d): %s (%d) in %s line %d", rc, mysql_error(mysql), \ + mysql_errno(mysql), __FILE__, __LINE__);\ + return(FAIL);\ + }\ +} while(0) + +#define check_stmt_rc(rc, stmt) \ +do {\ + if (rc)\ + {\ + diag("Error: %s (%s: %d)", mysql_stmt_error(stmt), __FILE__, __LINE__);\ + return(FAIL);\ + }\ +} while(0) + +#define FAIL_IF(expr, reason)\ +do {\ + if (expr)\ + {\ + diag("Error: %s (%s: %d)", reason, __FILE__, __LINE__);\ + return FAIL;\ + }\ +} while(0) + +#define FAIL_UNLESS(expr, reason)\ +do {\ + if (!(expr))\ + {\ + diag("Error: %s (%s: %d)", reason, __FILE__, __LINE__);\ + return FAIL;\ + }\ +} while(0) + +#define SKIP_CONNECTION_HANDLER \ +do {\ + if (hostname && strstr(hostname, "://"))\ + {\ + diag("Test skipped (connection handler)");\ + return SKIP;\ + }\ +} while(0) + +/* connection options */ +#define TEST_CONNECTION_DEFAULT 1 /* default connection */ +#define TEST_CONNECTION_NONE 2 /* tests creates own connection */ +#define TEST_CONNECTION_NEW 4 /* create a separate connection */ +#define TEST_CONNECTION_DONT_CLOSE 8 /* don't close connection */ + +struct my_option_st +{ + enum mysql_option option; + char *value; +}; + +struct my_tests_st +{ + const char *name; + int (*function)(MYSQL *); + int connection; + ulong connect_flags; + struct my_option_st *options; + const char *skipmsg; +}; + +MYSQL *my_test_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); + +static const char *schema = 0; +static char *hostname = 0; +static char *password = 0; +static unsigned int port = 0; +static unsigned int ssl_port = 0; +static char *socketname = 0; +static char *username = 0; +static int force_tls= 0; +static uchar is_mariadb= 0; +static char *this_host= 0; +static char *plugindir= 0; +static unsigned char travis_test= 0; +/* +static struct my_option test_options[] = +{ + {"schema", 'd', "database to use", (uchar **) &schema, (uchar **) &schema, + 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", (uchar **) &hostname, (uchar **) &hostname, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"password", 'p', + "Password to use when connecting to server.", (uchar **) &password, (uchar **) &password, + 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) ").", + (uchar **) &port, + (uchar **) &port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"socket", 'S', "Socket file to use for connection", + (uchar **) &socketname, (uchar **) &socketname, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"user", 'u', "User for login if not current user", (uchar **) &username, + (uchar **) &username, 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} +}; +*/ +#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__) + +int do_verify_prepare_field(MYSQL_RES *result, + unsigned int no, const char *name, + const char *org_name, + enum enum_field_types type __attribute__((unused)), + const char *table, + const char *org_table, const char *db, + unsigned long length __attribute__((unused)), + const char *def __attribute__((unused)), + const char *file __attribute__((unused)), + int line __attribute__((unused))) +{ + MYSQL_FIELD *field; +/* MARIADB_CHARSET_INFO *cs; */ + + FAIL_IF(!(field= mysql_fetch_field_direct(result, no)), "FAILED to get result"); +/* cs= mysql_find_charset_nr(field->charsetnr); + FAIL_UNLESS(cs, "Couldn't get character set"); */ + FAIL_UNLESS(strcmp(field->name, name) == 0, "field->name differs"); + FAIL_UNLESS(strcmp(field->org_name, org_name) == 0, "field->org_name differs"); +/* + if ((expected_field_length= length * cs->mbmaxlen) > UINT_MAX32) + expected_field_length= UINT_MAX32; +*/ + /* + 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->char_maxlen == 1) +// FAIL_UNLESS(field->type == type, "field->type differs"); + if (table) + FAIL_UNLESS(strcmp(field->table, table) == 0, "field->table differs"); + if (org_table) + FAIL_UNLESS(strcmp(field->org_table, org_table) == 0, "field->org_table differs"); + if (strcmp(field->db,db)) + diag("%s / %s", field->db, db); + FAIL_UNLESS(strcmp(field->db, db) == 0, "field->db differs"); + /* + 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. + */ + + return OK; +} + +void get_this_host(MYSQL *mysql) +{ + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mysql, "select substr(current_user(), locate('@', current_user())+1)")) + return; + + if ((res= mysql_store_result(mysql))) + { + if ((row= mysql_fetch_row(res))) + this_host= strdup(row[0]); + mysql_free_result(res); + } +} + +/* Prepare statement, execute, and process result set for given query */ + +int my_stmt_result(MYSQL *mysql, const char *buff) +{ + MYSQL_STMT *stmt; + int row_count= 0; + int rc; + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, buff, (unsigned long)strlen(buff)); + FAIL_IF(rc, mysql_stmt_error(stmt)); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(rc, mysql_stmt_error(stmt)); + + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + row_count++; + + mysql_stmt_close(stmt); + + return row_count; +} +/* +static my_bool +get_one_option(int optid, const struct my_option *opt __attribute__((unused)), + char *argument) +{ + switch (optid) { + case '?': + case 'I': + my_print_help(test_options); + exit(0); + break; + } + return 0; +} +*/ +/* Utility function to verify a particular column data */ + +int verify_col_data(MYSQL *mysql, 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; + + if (table && col) + { + sprintf(query, "SELECT %s FROM %s LIMIT 1", col, table); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + } + result= mysql_use_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + if (!(row= mysql_fetch_row(result)) || !row[0]) { + diag("Failed to get the result"); + goto error; + } + if(strcmp(row[0], exp_data)) { + diag("Expected %s, got %s", exp_data, row[0]); + goto error; + } + mysql_free_result(result); + + return OK; + +error: + mysql_free_result(result); + return FAIL; +} + +my_bool query_int_variable(MYSQL *con, const char *var_name, int *var_value) +{ + MYSQL_RES *rs; + MYSQL_ROW row; + + char query_buffer[MAX_TEST_QUERY_LENGTH]; + + my_bool is_null; + + sprintf(query_buffer, + "SELECT %s", + (const char *) var_name); + + FAIL_IF(mysql_query(con, query_buffer), "Query failed"); + FAIL_UNLESS(rs= mysql_store_result(con), "Invaliid result set"); + FAIL_UNLESS(row= mysql_fetch_row(rs), "Nothing to fetch"); + + is_null= row[0] == NULL; + + if (!is_null) + *var_value= atoi(row[0]); + + mysql_free_result(rs); + + return is_null; +} + +static void usage() +{ + printf("Execute test with the following options:\n"); + printf("-h hostname\n"); + printf("-u username\n"); + printf("-p password\n"); + printf("-d database\n"); + printf("-S socketname\n"); + printf("-t force use of TLS\n"); + printf("-P port number\n"); + printf("? displays this help and exits\n"); +} + +void get_options(int argc, char **argv) +{ + int c= 0; + + while ((c=getopt(argc,argv, "h:u:p:d:w:P:S:t:?")) >= 0) + { + switch(c) { + case 'h': + hostname= optarg; + break; + case 'u': + username= optarg; + break; + case 'p': + password= optarg; + break; + case 'd': + schema= optarg; + break; + case 'P': + port= atoi(optarg); + ssl_port=port; + break; + case 'S': + socketname= optarg; + break; + case 't': + force_tls= 1; + break; + case '?': + usage(); + exit(0); + break; + default: + usage(); + BAIL_OUT("Unknown option %c\n", c); + } + } +} + + +int check_variable(MYSQL *mysql, const char *variable, const char *value) +{ + char query[MAX_TEST_QUERY_LENGTH]; + MYSQL_RES *result; + MYSQL_ROW row; + + sprintf(query, "SELECT %s", variable); + result= mysql_store_result(mysql); + if (!result) + return FAIL; + + if ((row = mysql_fetch_row(result))) + if (strcmp(row[0], value) == 0) { + mysql_free_result(result); + return OK; + } + mysql_free_result(result); + return FAIL; +} + +/* + * function *test_connect + * + * returns a new connection. This function will be called, if the test doesn't + * use default_connection. + */ +MYSQL *test_connect(struct my_tests_st *test) +{ + MYSQL *mysql; + int i= 0, rc; + int timeout= 10; + my_bool truncation_report= 1; + if (!(mysql = mysql_init(NULL))) { + BAIL_OUT("Not enough memory available - mysql_init failed"); + } + mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, &truncation_report); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + if (plugindir) + mysql_options(mysql, MYSQL_PLUGIN_DIR, plugindir); + + /* option handling */ + if (test && test->options) { + + while (test->options[i].option) + { + if (mysql_options(mysql, test->options[i].option, test->options[i].value)) { + diag("Couldn't set option %d. Error (%d) %s", test->options[i].option, + mysql_errno(mysql), mysql_error(mysql)); + mysql_close(mysql); + return(NULL); + } + i++; + } + } + if (!(my_test_connect(mysql, hostname, username, password, + schema, port, socketname, (test) ? test->connect_flags:0))) + { + diag("Couldn't establish connection to server %s. Error (%d): %s", + hostname, mysql_errno(mysql), mysql_error(mysql)); + mysql_close(mysql); + return(NULL); + } + + /* Clear sql_mode when establishing a new connection. */ + rc= mysql_query(mysql, "SET sql_mode=''"); + if (rc) + { + diag("Error (%d): %s (%d) in %s line %d", rc, mysql_error(mysql), + mysql_errno(mysql), __FILE__, __LINE__); + return(NULL); + } + + return(mysql); +} + +static int reset_connection(MYSQL *mysql) { + int rc; + + if (is_mariadb && !IS_MAXSCALE()) + rc= mysql_change_user(mysql, username, password, schema); + else + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "SET sql_mode=''"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + * function get_envvars(( + * + * checks for connection related environment variables + */ +void get_envvars() { + char *envvar; + + if (!getenv("MYSQLTEST_VARDIR") && + !getenv("MARIADB_CC_TEST")) + { + skip_all("Tests skipped.\nFor running unittest suite outside of MariaDB server tests,\nplease specify MARIADB_CC_TEST environment variable."); + exit(0); + } + + if (getenv("TRAVIS_JOB_ID")) + travis_test= 1; + + if (!hostname && (envvar= getenv("MYSQL_TEST_HOST"))) + hostname= envvar; + + + if (!username) + { + if ((envvar= getenv("MYSQL_TEST_USER"))) + username= envvar; + else + username= (char *)"root"; + } + if (!password && (envvar= getenv("MYSQL_TEST_PASSWD"))) + password= envvar; + if (!schema && (envvar= getenv("MYSQL_TEST_DB"))) + schema= envvar; + if (!schema) + schema= "test"; + if (!port) + { + if ((envvar= getenv("MYSQL_TEST_PORT"))) + port= atoi(envvar); + else if ((envvar= getenv("MASTER_MYPORT"))) + port= atoi(envvar); + diag("port: %d", port); + } + if (!ssl_port) + { + if ((envvar= getenv("MYSQL_TEST_SSL_PORT"))) + ssl_port= atoi(envvar); + else + ssl_port = port; + diag("ssl_port: %d", ssl_port); + } + + if (!force_tls && (envvar= getenv("MYSQL_TEST_TLS"))) + force_tls= atoi(envvar); + if (!socketname) + { + if ((envvar= getenv("MYSQL_TEST_SOCKET"))) + socketname= envvar; + else if ((envvar= getenv("MASTER_MYSOCK"))) + socketname= envvar; + diag("socketname: %s", socketname); + } + if ((envvar= getenv("MYSQL_TEST_PLUGINDIR"))) + plugindir= envvar; + + if (IS_XPAND()) + { + + } +} + +MYSQL *my_test_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) +{ + if (force_tls) + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &force_tls); + if (!mysql_real_connect(mysql, host, user, passwd, db, port, unix_socket, clientflag)) + { + diag("error: %s", mysql_error(mysql)); + return NULL; + } + + if (mysql && force_tls && !mysql_get_ssl_cipher(mysql)) + { + diag("Error: TLS connection not established"); + return NULL; + } + if (!this_host) + get_this_host(mysql); + return mysql; +} + + +void run_tests(struct my_tests_st *test) { + int i, rc, total=0; + MYSQL *mysql; + + while (test[total].function) + total++; + plan(total); + +/* display TLS stats */ + mysql= mysql_init(NULL); + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + + if (!mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0)) + { + BAIL_OUT("Can't establish TLS connection to server."); + } + + if (!mysql_query(mysql, "SHOW VARIABLES LIKE '%ssl%'")) + { + MYSQL_RES *res; + MYSQL_ROW row; + + diag("TLS server variables"); + diag("--------------------"); + + res= mysql_store_result(mysql); + while ((row= mysql_fetch_row(res))) + diag("%s: %s", row[0], row[1]); + mysql_free_result(res); + diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + diag("--------------------"); + } + mysql_close(mysql); + + + if ((mysql_default= test_connect(NULL))) + { + diag("Testing against MySQL Server %s", mysql_get_server_info(mysql_default)); + diag("Host: %s", mysql_get_host_info(mysql_default)); + diag("Client library: %s", mysql_get_client_info()); + is_mariadb= mariadb_connection(mysql_default); + } + else + { + BAIL_OUT("Can't connect to a server. Aborting...."); + } + + for (i=0; i < total; i++) { + if (!mysql_default && (test[i].connection & TEST_CONNECTION_DEFAULT)) + { + diag("MySQL server not running"); + skip(1, "%s", test[i].name); + } else if (!test[i].skipmsg) { + mysql= mysql_default; + if (test[i].connection & TEST_CONNECTION_NEW) + mysql= test_connect(&test[i]); + if (test[i].connection & TEST_CONNECTION_NONE) + mysql= NULL; + + /* run test */ + rc= test[i].function(mysql); + + if (rc == SKIP) + skip(1, "%s", test[i].name); + else + ok(rc == OK, "%s", test[i].name); + + /* if test failed, close and reopen default connection to prevent + errors for further tests */ + if ((rc == FAIL || mysql_errno(mysql_default)) && (test[i].connection & TEST_CONNECTION_DEFAULT)) { + mysql_close(mysql_default); + mysql_default= test_connect(&test[i]); + } + /* clear connection: reset default connection or close extra connection */ + else if (mysql_default && (test[i].connection & TEST_CONNECTION_DEFAULT)) { + if (reset_connection(mysql)) + return; /* default doesn't work anymore */ + } + else if (mysql && !(test[i].connection & TEST_CONNECTION_DONT_CLOSE)) + { + mysql_close(mysql); + } + } else { + skip(1, "%s", test[i].skipmsg); + } + } + if (this_host) + free(this_host); + + if (mysql_default) { + diag("close default"); + mysql_close(mysql_default); + } +} diff --git a/libmariadb/unittest/libmariadb/performance.c b/libmariadb/unittest/libmariadb/performance.c new file mode 100644 index 00000000..f99253f7 --- /dev/null +++ b/libmariadb/unittest/libmariadb/performance.c @@ -0,0 +1,76 @@ +/* +Copyright (c) 2016 MariaDB Corporation AB + +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-1301 USA +*/ + + +/** + Some basic tests of the client API. +*/ + +#include "my_test.h" +#include "ma_common.h" + +static int perf1(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + const char *stmtstr= "SELECT s.emp_no, s.salary, e.emp_no, e.first_name, e.last_name, e.gender FROM salaries s, employees e WHERE s.emp_no = e.emp_no"; + + rc= mysql_select_db(mysql, "employees"); + if (rc) + { + diag("Employees database not installed"); + return SKIP; + } + + stmt= mysql_stmt_init(mysql); + + diag("prepare"); + rc= mysql_stmt_prepare(stmt, SL(stmtstr)); + check_stmt_rc(rc, stmt); + + diag("execute"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + diag("store"); + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + diag("fetch"); + while (!mysql_stmt_fetch(stmt)); + + mysql_stmt_close(stmt); + return OK; +} + +struct my_tests_st my_tests[] = { + {"perf1", perf1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/ps.c b/libmariadb/unittest/libmariadb/ps.c new file mode 100644 index 00000000..111ce2a6 --- /dev/null +++ b/libmariadb/unittest/libmariadb/ps.c @@ -0,0 +1,5315 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" + +/* Utility function to verify the field members */ + +static int test_conc97(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + + diag("Please run this test manually"); + return SKIP; + stmt= mysql_stmt_init(mysql); + + mysql_close(mysql); + + rc= mysql_stmt_reset(stmt); + FAIL_IF(!rc, "Error expected while resetting stmt"); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + mysql= mysql_init(NULL); + + return OK; +} + +static int test_conc83(MYSQL *unused __attribute__((unused))) +{ + MYSQL_STMT *stmt; + int rc; + MYSQL *mysql= mysql_init(NULL); + my_bool reconnect= 1; + + const char *query= "SELECT 1,2,3 FROM DUAL"; + + SKIP_MAXSCALE; + SKIP_XPAND; + + stmt= mysql_stmt_init(mysql); + + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_IF(!(my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0)), "my_test_connect failed"); + + /* 1. Status is inited, so prepare should work */ + + rc= mysql_kill(mysql, mysql_thread_id(mysql)); + + rc= mysql_ping(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + diag("Ok"); + + /* 2. Status is prepared, execute should fail */ + rc= mysql_kill(mysql, mysql_thread_id(mysql)); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_close(stmt); + mysql_close(mysql); + return OK; +} + + +static int test_conc60(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *query= "SELECT * FROM agendas"; + my_bool x= 1; + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void *)&x); + + rc= mysql_stmt_prepare(stmt, SL(query)); + if (rc && mysql_stmt_errno(stmt) == 1146) { + diag("Internal test - customer data not available"); + mysql_stmt_close(stmt); + return SKIP; + } + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_prepare_insert_update(MYSQL *mysql) +{ + 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; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + for (cur_query= testcase; *cur_query; cur_query++) + { + char query[MAX_TEST_QUERY_LENGTH]; + strcpy(query, *cur_query); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 0, "Paramcount is not 0"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + /* try the last query several times */ + if (!cur_query[1]) + { + for (i=0; i < 3;i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + } + mysql_stmt_close(stmt); + } + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + Generalized conversion routine to handle DATE, TIME and DATETIME + conversion using MYSQL_TIME structure +*/ + +static int test_bind_date_conv(MYSQL *mysql, uint row_count) +{ + MYSQL_STMT *stmt= 0; + uint rc, i, count; + MYSQL_BIND my_bind[4]; + my_bool is_null[4]= {0,0,0,0}; + MYSQL_TIME tm[4]; + ulong second_part; + uint year, month, day, hour, minute, sec; + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO test_date VALUES(?, ?, ?, ?)")); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 4, "param_count != 4"); + + /* + We need to bzero bind structure because mysql_stmt_bind_param checks all + its members. + */ + memset(my_bind, '\0', 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_DATETIME; + + 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].buffer_length= sizeof(MYSQL_TIME); + } + + second_part= 0; + + year= 2000; + month= 01; + day= 10; + + hour= 11; + minute= 16; + sec= 20; + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + for (count= 0; count < row_count; count++) + { + for (i= 0; i < (int) array_elements(my_bind); i++) + { + memset(&tm[i], 0, sizeof(MYSQL_TIME)); + 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_stmt_rc(rc, stmt); + } + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + mysql_stmt_close(stmt); + + rc= my_stmt_result(mysql, "SELECT * FROM test_date"); + FAIL_UNLESS(row_count == rc, "rowcount != rc"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_date")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + for (count= 0; count < row_count; count++) + { + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == 0 || rc == MYSQL_DATA_TRUNCATED, "rc != 0 | rc != MYSQL_DATA_TRUNCATED"); + + for (i= 0; i < array_elements(my_bind); i++) + { + FAIL_UNLESS(tm[i].year == 0 || tm[i].year == year+count, "wrong value for year"); + FAIL_UNLESS(tm[i].month == 0 || tm[i].month == month+count, "wrong value for month"); + FAIL_UNLESS(tm[i].day == 0 || tm[i].day == day+count, "wrong value for day"); + FAIL_UNLESS(tm[i].hour == 0 || tm[i].hour % 24 == 0 || tm[i].hour % 24 == hour+count, "wrong value for hour"); + FAIL_UNLESS(tm[i].minute == 0 || tm[i].minute == minute+count, "wrong value for minute"); + FAIL_UNLESS(tm[i].second == 0 || tm[i].second == sec+count, "wrong value for second"); + FAIL_UNLESS(tm[i].second_part == 0 || + tm[i].second_part == second_part+count, "wrong value for second_part"); + } + } + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + return OK; +} + + +/* Test simple prepares of all DML statements */ + +static int test_prepare_simple(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_simple"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_prepare_simple(" + "id int, name varchar(50))"); + check_mysql_rc(rc, mysql); + + /* insert */ + strcpy(query, "INSERT INTO test_prepare_simple VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount is not 2"); + mysql_stmt_close(stmt); + + /* update */ + strcpy(query, "UPDATE test_prepare_simple SET id=? " + "WHERE id=? AND CONVERT(name USING utf8)= ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 3, "Paramcount is not 3"); + mysql_stmt_close(stmt); + + /* delete */ + strcpy(query, "DELETE FROM test_prepare_simple WHERE id=10"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 0, "Paramcount is not 0"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + /* delete */ + strcpy(query, "DELETE FROM test_prepare_simple WHERE id=?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "Paramcount != 1"); + + mysql_stmt_close(stmt); + + /* select */ + strcpy(query, "SELECT * FROM test_prepare_simple WHERE id=? " + "AND CONVERT(name USING utf8)= ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_simple"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_prepare_field_result(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_RES *result; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_field_result"); + check_mysql_rc(rc, mysql); + + 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)"); + check_mysql_rc(rc, mysql); + + /* insert */ + strcpy(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_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "Paramcount != 1"); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, mysql_stmt_error(stmt)); + + if (verify_prepare_field(result, 0, "int_c", "int_c", MYSQL_TYPE_LONG, + "t1", "test_prepare_field_result", schema, 11, 0)) + goto error; + if (verify_prepare_field(result, 1, "var_c", "var_c", MYSQL_TYPE_VAR_STRING, + "t1", "test_prepare_field_result", schema, 50, 0)) + goto error; + if (verify_prepare_field(result, 2, "date", "date_c", MYSQL_TYPE_DATE, + "t1", "test_prepare_field_result", schema, 10, 0)) + goto error; + if (verify_prepare_field(result, 3, "ts_c", "ts_c", MYSQL_TYPE_TIMESTAMP, + "t1", "test_prepare_field_result", schema, 19, 0)) + goto error; + if (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", schema, 4, 0)) + goto error; + + FAIL_IF(mysql_num_fields(result) != 5, "Paramcount != 5"); + mysql_free_result(result); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_field_result"); + check_mysql_rc(rc, mysql); + + return OK; + +error: + mysql_free_result(result); + mysql_stmt_close(stmt); + return FAIL; +} + + +/* Test simple prepare field results */ + +static int test_prepare_syntax(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_syntax"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_prepare_syntax(" + "id int, name varchar(50), extra int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "START TRANSACTION"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_prepare_syntax VALUES(?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(!rc, "error expected"); + + strcpy(query, "SELECT id, name FROM test_prepare_syntax WHERE id=? AND WHERE"); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(!rc, "error expected"); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_syntax"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_prepare(MYSQL *mysql) +{ + 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]; + MYSQL_BIND my_bind[7]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_prepare"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE my_prepare(col1 tinyint, " + "col2 varchar(15), col3 int, " + "col4 smallint, col5 bigint, " + "col6 float, col7 double )"); + check_mysql_rc(rc, mysql); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + /* insert by prepare */ + strcpy(query, "INSERT INTO my_prepare VALUES(?, ?, ?, ?, ?, ?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 7, "Paramcount != 7"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + 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); + check_mysql_rc(rc, mysql); + + /* test the results now, only one row should exist */ + rc= my_stmt_result(mysql, "SELECT * FROM my_prepare"); + FAIL_UNLESS(rc != 1, "rowcount != 1"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, "SELECT * FROM my_prepare", 25); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + /* get the result */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + + FAIL_UNLESS(tiny_data == o_tiny_data, "Wrong value for tiny_data"); + FAIL_UNLESS(is_null[0] == 0, "Wrong value for is_null"); + FAIL_UNLESS(length[0] == 1, "length != 0"); + + FAIL_UNLESS(int_data == o_int_data, "Wrong value for int_data"); + FAIL_UNLESS(length[2] == 4, "length != 4"); + + FAIL_UNLESS(small_data == o_small_data, "Wrong value for small_data"); + FAIL_UNLESS(length[3] == 2, "length != 2"); + + FAIL_UNLESS(big_data == o_big_data, "Wrong value for big_data"); + FAIL_UNLESS(length[4] == 8, "length != 8"); + + FAIL_UNLESS(real_data == o_real_data, "Wrong value for real_data"); + FAIL_UNLESS(length[5] == 4, "length != 4"); + + FAIL_UNLESS(double_data == o_double_data, "Wrong value for double_data"); + FAIL_UNLESS(length[6] == 8, "length != 8"); + + FAIL_UNLESS(strcmp(data, str_data) == 0, "Wrong value for data"); + FAIL_UNLESS(length[1] == len, "length != 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); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "MYSQL_NO_DATA expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_prepare"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_prepare_multi_statements(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + char query[MAX_TEST_QUERY_LENGTH]; + int rc; + + strcpy(query, "select 1; select 'another value'"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_prepare_ext(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char *sql; + int nData= 1; + char tData= 1; + short sData= 10; + longlong bData= 20; + int rowcount= 0; + MYSQL_BIND my_bind[6]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_ext"); + check_mysql_rc(rc, mysql); + + 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); + check_mysql_rc(rc, mysql); + + /* insert by prepare - all integers */ + strcpy(query, "INSERT INTO test_prepare_ext(c1, c2, c3, c4, c5, c6) VALUES(?, ?, ?, ?, ?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 6, "Paramcount != 6"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + /* + * integer to integer + */ + for (nData= 0; nData<10; nData++, tData++, sData++, bData++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + + strcpy(query, "SELECT c1, c2, c3, c4, c5, c6 FROM test_prepare_ext"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + /* get the result */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + + FAIL_UNLESS(nData == rowcount, "Invalid rowcount"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_ext"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_prepare_alter(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL *mysql_new; + int rc, id; + MYSQL_BIND my_bind[1]; + my_bool is_null; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prep_alter"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_prep_alter(id int, name char(20))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_prep_alter values(10, 'venu'), (20, 'mysql')"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_prep_alter VALUES(?, 'monty')"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "Paramcount != 1"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + id= 30; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_new= mysql_init(NULL); + FAIL_IF(!mysql_new, "mysql_init failed"); + FAIL_IF(!(my_test_connect(mysql_new, hostname, username, password, + schema, port, socketname, 0)), "my_test_connect failed"); + rc= mysql_query(mysql_new, "ALTER TABLE test_prep_alter change id id_new varchar(20)"); + diag("Error: %d %s", mysql_errno(mysql_new), mysql_error(mysql_new)); + check_mysql_rc(rc, mysql_new); + mysql_close(mysql_new); + + is_null= 1; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= my_stmt_result(mysql, "SELECT * FROM test_prep_alter"); + FAIL_UNLESS(rc == 4, "rowcount != 4"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prep_alter"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_prepare_resultset(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + MYSQL_RES *result; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_resultset"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_prepare_resultset(id int, \ + name varchar(50), extra double)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + strcpy(query, "SELECT * FROM test_prepare_resultset"); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt), "Paramcount != 0"); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, "Invalid resultset"); + mysql_free_result(result); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prepare_resultset"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Test the direct query execution in the middle of open stmts */ + +static int test_open_direct(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_RES *result; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_open_direct"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_open_direct(id int, name char(6))"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_open_direct values(10, 'mysql')"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "SELECT * FROM test_open_direct"); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "invalid resultset"); + + FAIL_IF(mysql_num_rows(result), "rowcount != 0"); + mysql_free_result(result); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "affected rows != 1"); + + rc= mysql_query(mysql, "SELECT * FROM test_open_direct"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "invalid resultset"); + + FAIL_IF(mysql_num_rows(result) != 1, "rowcount != 1"); + mysql_free_result(result); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "affected rows != 1"); + + rc= mysql_query(mysql, "SELECT * FROM test_open_direct"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid resultset"); + FAIL_IF(mysql_num_rows(result) != 2, "rowcount != 2"); + + mysql_free_result(result); + + mysql_stmt_close(stmt); + + /* run a direct query in the middle of a fetch */ + + strcpy(query, "SELECT * FROM test_open_direct"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "INSERT INTO test_open_direct(id) VALUES(20)"); + FAIL_IF(!rc, "Error expected"); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "INSERT INTO test_open_direct(id) VALUES(20)"); + check_mysql_rc(rc, mysql); + + /* run a direct query with store result */ + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "drop table test_open_direct"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + return OK; +} + +static int test_select_show(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + int rowcount; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_show"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_show(id int(4) NOT NULL primary " + " key, name char(2))"); + check_mysql_rc(rc, mysql); + + strcpy(query, "show columns from test_show"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_stmt_error(stmt)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 0, "Paramcount != 0"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 2, "rowcount != 2"); + + mysql_stmt_close(stmt); + + strcpy(query, "show tables from mysql like ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(!rc, "Error expected"); + + strcpy(query, "show tables like \'test_show\'"); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount != 1"); + mysql_stmt_close(stmt); + + strcpy(query, "describe test_show"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 2, "rowcount != 2"); + mysql_stmt_close(stmt); + + strcpy(query, "show keys from test_show"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount != 1"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_show"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_simple_update(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char szData[25]; + int nData= 1; + MYSQL_RES *result; + MYSQL_BIND my_bind[2]; + ulong length[2]; + int rowcount= 0; + char query[MAX_TEST_QUERY_LENGTH]; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_update(col1 int, " + " col2 varchar(50), col3 int )"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_update VALUES(1, 'MySQL', 100)"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != 1, "Affected rows != 1"); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* insert by prepare */ + strcpy(query, "UPDATE test_update SET col2= ? WHERE col1= ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "Affected_rows != 1"); + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* test the results now, only one row should exist */ + rc= mysql_query(mysql, "SELECT * FROM test_update"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid resultset"); + + while (mysql_fetch_row(result)) + rowcount++; + + FAIL_IF(rowcount != 1, "rowcount != 1"); + + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* Test simple long data handling */ + +static int test_long_data(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, int_data; + char *data= NullS; + MYSQL_RES *result; + MYSQL_BIND my_bind[3]; + int rowcount; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_long_data(col1 int, " + " col2 MEDIUMTEXT, col3 MEDIUMTEXT)"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_long_data(col1, col2) VALUES(?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(!rc, "Error expected"); + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + strcpy(query, "INSERT INTO test_long_data(col1, col2, col3) VALUES(?, ?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 3, "Paramcount != 3"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + int_data= 999; + data= (char *)"Michael"; + + /* supply data in pieces */ + rc= mysql_stmt_send_long_data(stmt, 1, SL(data)); + check_stmt_rc(rc, stmt); + data= (char *)" 'Monty' Widenius"; + rc= mysql_stmt_send_long_data(stmt, 1, SL(data)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_send_long_data(stmt, 2, "Venu (venu@mysql.com)", 4); + check_stmt_rc(rc, stmt); + + /* execute */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* now fetch the results ..*/ + rc= mysql_query(mysql, "SELECT * FROM test_long_data"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rowcount= 0; + while (mysql_fetch_row(result)) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount != 1"); + mysql_free_result(result); + + if (verify_col_data(mysql, "test_long_data", "col1", "999")) + goto error; + if (verify_col_data(mysql, "test_long_data", "col2", "Michael 'Monty' Widenius")) + goto error; + if (verify_col_data(mysql, "test_long_data", "col3", "Venu")) + goto error; + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data"); + check_mysql_rc(rc, mysql); + return OK; + +error: + mysql_stmt_close(stmt); + return FAIL; +} + + +/* Test long data (string) handling */ + +static int test_long_data_str(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i, rowcount= 0; + 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]; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_str"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_long_data_str(id int, longstr MEDIUMTEXT)"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_long_data_str VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + length= 40; + strcpy(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_stmt_rc(rc, stmt); + } + /* execute */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* now fetch the results ..*/ + rc= mysql_query(mysql, "SELECT LENGTH(longstr), longstr FROM test_long_data_str"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + while (mysql_fetch_row(result)) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount != 1"); + + mysql_free_result(result); + + sprintf(data, "%d", i*5); + if (verify_col_data(mysql, "test_long_data_str", "LENGTH(longstr)", data)) + goto error; + strcpy(data, "MySQLMySQLMySQLMySQL"); + if (verify_col_data(mysql, "test_long_data_str", "longstr", data)) + goto error; + + rc= mysql_query(mysql, "DROP TABLE test_long_data_str"); + check_mysql_rc(rc, mysql); + + return OK; + +error: + rc= mysql_query(mysql, "DROP TABLE test_long_data_str"); + check_mysql_rc(rc, mysql); + return FAIL; +} + + +/* Test long data (string) handling */ + +static int test_long_data_str1(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i, rowcount= 0; + char data[255]; + long length; + unsigned long max_blob_length, blob_length, length1; + my_bool true_value; + MYSQL_RES *result; + MYSQL_BIND my_bind[2]; + MYSQL_FIELD *field; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_str"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_long_data_str(longstr MEDIUMTEXT, blb MEDIUMBLOB)"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_long_data_str VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + + memset(my_bind, '\0', sizeof(my_bind)); + + my_bind[0].buffer= data; /* string data */ + my_bind[0].buffer_length= sizeof(data); + my_bind[0].length= (unsigned long *)&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_stmt_rc(rc, stmt); + 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_send_long_data(stmt, 1, data, 2); + check_stmt_rc(rc, stmt); + } + + /* execute */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* now fetch the results ..*/ + rc= mysql_query(mysql, "SELECT LENGTH(longstr), longstr, LENGTH(blb), blb FROM test_long_data_str"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + + mysql_field_seek(result, 1); + field= mysql_fetch_field(result); + max_blob_length= field->max_length; + + FAIL_IF(!result, "Invalid result set"); + + while (mysql_fetch_row(result)) + rowcount++; + + FAIL_IF(rowcount != 1, "rowcount != 1"); + mysql_free_result(result); + + sprintf(data, "%ld", (long)i*length); + if (verify_col_data(mysql, "test_long_data_str", "length(longstr)", data)) + return FAIL; + + sprintf(data, "%d", i*2); + if (verify_col_data(mysql, "test_long_data_str", "length(blb)", data)) + return FAIL; + + /* Test length of field->max_length */ + strcpy(query, "SELECT * from test_long_data_str"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 0, "Paramcount != 0"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_fields(result); + + /* First test what happens if STMT_ATTR_UPDATE_MAX_LENGTH is not used */ + FAIL_IF(field->max_length != 0, "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_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_fields(result); + + diag("max_length: %lu max_blob_length: %lu", (unsigned long)field->max_length, (unsigned long)max_blob_length); + FAIL_UNLESS(field->max_length == max_blob_length, "field->max_length != max_blob_length"); + + /* Fetch results into a data buffer that is smaller than data */ + memset(my_bind, '\0', 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= (unsigned long *)&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); + FAIL_UNLESS(rc == MYSQL_DATA_TRUNCATED, "truncation expected"); + FAIL_UNLESS(my_bind[0].error_value, "No error value"); + FAIL_UNLESS(strlen(data) == 16, "Invalid string length"); + FAIL_UNLESS(blob_length == max_blob_length, "blob_length != max_blob_length"); + + /* Fetch all data */ + memset((my_bind+1), '\0', 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= (unsigned long *)&blob_length; + memset(data, '\0', sizeof(data)); + mysql_stmt_fetch_column(stmt, my_bind+1, 0, 0); + FAIL_UNLESS(strlen(data) == max_blob_length, "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"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* Test long data (binary) handling */ + +static int test_long_data_bin(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, rowcount= 0; + char data[255]; + long length; + MYSQL_RES *result; + MYSQL_BIND my_bind[2]; + char query[MAX_TEST_QUERY_LENGTH]; + + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_bin"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_long_data_bin(id int, longbin long varbinary)"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_long_data_bin VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + length= 10; + strcpy(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_stmt_rc(rc, stmt); + } + } + /* execute */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* now fetch the results ..*/ + rc= mysql_query(mysql, "SELECT LENGTH(longbin), longbin FROM test_long_data_bin"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + while (mysql_fetch_row(result)) + rowcount++; + + FAIL_IF(rowcount != 1, "rowcount != 1"); + mysql_free_result(result); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_bin"); + check_mysql_rc(rc, mysql); + return OK; +} + + +/* Test simple delete */ + +static int test_simple_delete(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, rowcount= 0; + char szData[30]= {0}; + int nData= 1; + MYSQL_RES *result; + MYSQL_BIND my_bind[2]; + ulong length[2]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_simple_delete"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_simple_delete(col1 int, \ + col2 varchar(50), col3 int )"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_simple_delete VALUES(1, 'MySQL', 100)"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != 1, "Affected rows != 1"); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* insert by prepare */ + strcpy(query, "DELETE FROM test_simple_delete WHERE col1= ? AND " + "CONVERT(col2 USING utf8)= ? AND col3= 100"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + + memset(my_bind, '\0', sizeof(my_bind)); + + nData= 1; + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "Affected rows != 1"); + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* test the results now, only one row should exist */ + rc= mysql_query(mysql, "SELECT * FROM test_simple_delete"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + while (mysql_fetch_row(result)) + rowcount++; + + FAIL_IF(rowcount, "rowcount > 0"); + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_simple_delete"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_update(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char szData[25]; + int nData= 1, rowcount= 0; + MYSQL_RES *result; + MYSQL_BIND my_bind[2]; + ulong length[2]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_update(" + "col1 int primary key auto_increment, " + "col2 varchar(50), col3 int )"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_update(col2, col3) VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + nData= 100; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "Affected rows != 1"); + mysql_stmt_close(stmt); + + strcpy(query, "UPDATE test_update SET col2= ? WHERE col3= ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Paramcount != 2"); + nData= 100; + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(mysql_stmt_affected_rows(stmt) != 1, "Affected rows != 1"); + + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* test the results now, only one row should exist */ + rc= mysql_query(mysql, "SELECT * FROM test_update"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + while (mysql_fetch_row(result)) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount != 1"); + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* Test prepare without parameters */ + +static int test_prepare_noparam(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, rowcount= 0; + MYSQL_RES *result; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_prepare"); + check_mysql_rc(rc, mysql); + + + rc= mysql_query(mysql, "CREATE TABLE my_prepare(col1 int, col2 varchar(50))"); + check_mysql_rc(rc, mysql); + + /* insert by prepare */ + strcpy(query, "INSERT INTO my_prepare VALUES(10, 'venu')"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 0, "Paramcount != 0"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* test the results now, only one row should exist */ + rc= mysql_query(mysql, "SELECT * FROM my_prepare"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + while (mysql_fetch_row(result)) + rowcount++; + + FAIL_IF(rowcount != 1, "rowcount != 1"); + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS my_prepare"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* Test simple bind result */ + +static int test_bind_result(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + int nData; + ulong length1; + char szData[100]; + MYSQL_BIND my_bind[2]; + my_bool is_null[2]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_bind_result(col1 int , col2 varchar(50))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_bind_result VALUES(10, 'venu')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_bind_result VALUES(20, 'MySQL')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_bind_result(col2) VALUES('monty')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* fetch */ + + memset(my_bind, '\0', 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]; + + strcpy(query, "SELECT * FROM test_bind_result"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 10, "nData != 10"); + FAIL_UNLESS(strcmp(szData, "venu") == 0, "szData != 'Venu'"); + FAIL_UNLESS(length1 == 4, "length1 != 4"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 20, "nData != 20"); + FAIL_UNLESS(strcmp(szData, "MySQL") == 0, "szData != 'MySQL'"); + FAIL_UNLESS(length1 == 5, "length1 != 5"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(is_null[0], "null flag not set"); + FAIL_UNLESS(strcmp(szData, "monty") == 0, "szData != 'Monty'"); + FAIL_UNLESS(length1 == 5, "length1 != 5"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "MYSQL_NO_DATA expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bind_result_ext(MYSQL *mysql) +{ + 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 query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + + 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))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_bind_result " + "VALUES (19, 2999, 3999, 4999999, " + " 2345.6, 5678.89563, 'venu', 'mysql')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + memset(my_bind, '\0', 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); + + strcpy(query, "select * from test_bind_result"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(t_data == 19, "tdata != 19"); + FAIL_UNLESS(s_data == 2999, "s_data != 2999"); + FAIL_UNLESS(i_data == 3999, "i_data != 3999"); + FAIL_UNLESS(b_data == 4999999, "b_data != 4999999"); + FAIL_UNLESS(strcmp(szData, "venu") == 0, "szData != 'Venu'"); + FAIL_UNLESS(strncmp(bData, "mysql", 5) == 0, "nData != 'mysql'"); + FAIL_UNLESS(szLength == 4, "szLength != 4"); + FAIL_UNLESS(bLength == 5, "bLength != 5"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "MYSQL_NO_DATA expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + return OK; +} + + +/* Test ext bind result */ + +static int test_bind_result_ext1(MYSQL *mysql) +{ + 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]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + + 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))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_bind_result VALUES(120, 2999, 3999, 54, \ + 2.6, 58.89, \ + '206', '6.7')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + memset(my_bind, '\0', 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]; + } + + strcpy(query, "select * from test_bind_result"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(strcmp(t_data, "120") == 0, "t_data != 120"); + FAIL_UNLESS(i_data == 3999, "i_data != 3999"); + FAIL_UNLESS(f_data == 2, "f_data != 2"); + FAIL_UNLESS(strcmp(d_data, "58.89") == 0, "d_data != 58.89"); + FAIL_UNLESS(b_data == 54, "b_data != 54"); + + FAIL_UNLESS(length[0] == 3, "Wrong length"); + FAIL_UNLESS(length[1] == 4, "Wrong length"); + FAIL_UNLESS(length[2] == 2, "Wrong length"); + FAIL_UNLESS(length[3] == 1, "Wrong length"); + FAIL_UNLESS(length[4] == 4, "Wrong length"); + FAIL_UNLESS(length[5] == 5, "Wrong length"); + FAIL_UNLESS(length[6] == 4, "Wrong length"); + FAIL_UNLESS(length[7] == 8, "Wrong length"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "MYSQL_NO_DATA expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bind_result"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bind_negative(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + char *query; + int rc; + MYSQL_BIND my_bind[1]; + int32 my_val= 0; + ulong my_length= 0L; + my_bool my_null= FALSE; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create temporary table t1 (c1 int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1), (-1)"); + check_mysql_rc(rc, mysql); + + query= (char*)"INSERT INTO t1 VALUES (?)"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + /* bind parameters */ + memset(my_bind, '\0', 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= &my_null; + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + my_val= -1; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_buffers(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + int rc; + ulong length; + my_bool is_null; + char buffer[20]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_buffer"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_buffer(str varchar(20))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into test_buffer values('MySQL')\ + , ('Database'), ('Open-Source'), ('Popular')"); + check_mysql_rc(rc, mysql); + + strcpy(query, "select str from test_buffer"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(buffer, '\0', sizeof(buffer)); /* Avoid overruns in printf() */ + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + buffer[1]= 'X'; + rc= mysql_stmt_fetch(stmt); + + FAIL_UNLESS(rc == MYSQL_DATA_TRUNCATED, "rc != MYSQL_DATA_TRUNCATED"); + FAIL_UNLESS(my_bind[0].error_value, "Errorflag not set"); + FAIL_UNLESS(buffer[0] == 'M', "buffer[0] != M"); + FAIL_UNLESS(buffer[1] == 'X', "buffer[1] != X"); + FAIL_UNLESS(length == 5, "length != 5"); + + my_bind[0].buffer_length= 8; + rc= mysql_stmt_bind_result(stmt, my_bind);/* re-bind */ + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(strncmp(buffer, "Database", 8) == 0, "buffer != 'Database'"); + FAIL_UNLESS(length == 8, "length != 8"); + + my_bind[0].buffer_length= 12; + rc= mysql_stmt_bind_result(stmt, my_bind);/* re-bind */ + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(strcmp(buffer, "Open-Source") == 0, "buffer != 'Open-Source'"); + FAIL_UNLESS(length == 11, "Length != 11"); + + my_bind[0].buffer_length= 6; + rc= mysql_stmt_bind_result(stmt, my_bind);/* re-bind */ + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_DATA_TRUNCATED, "rc != MYSQL_DATA_TRUNCATED"); + FAIL_UNLESS(my_bind[0].error_value, "Errorflag not set"); + FAIL_UNLESS(strncmp(buffer, "Popula", 6) == 0, "buffer != 'Popula'"); + FAIL_UNLESS(length == 7, "length != 7"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_buffer"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_xjoin(MYSQL *mysql) +{ + 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"; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2, t3, t4"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t3 (id int(8), param1_id int(8), param2_id int(8)) ENGINE=InnoDB DEFAULT CHARSET=utf8"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 ( id int(8), name_id int(8), value varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t2 (id int(8), name_id int(8), value varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t4(id int(8), value varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t3 values (1, 1, 1), (2, 2, null)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values (1, 1, 'aaa'), (2, null, 'bbb')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t2 values (1, 2, 'ccc')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t4 values (1, 'Name1'), (2, null)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1, t2, t3, t4"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_union_param(MYSQL *mysql) +{ + 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; + + strcpy(my_val, "abc"); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + query= (char*)"select ? as my_col union distinct select ?"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + /* + We need to bzero bind structure because mysql_stmt_bind_param checks all + its members. + */ + memset(my_bind, '\0', 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= &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= &my_null; + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + } + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_union(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *query= "SELECT t1.name FROM t1 UNION " + "SELECT t2.name FROM t2"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, + "CREATE TABLE t1 " + "(id INTEGER NOT NULL PRIMARY KEY, " + " name VARCHAR(20) NOT NULL)"); + check_mysql_rc(rc, mysql); + 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')"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, + "CREATE TABLE t2 " + "(id INTEGER NOT NULL PRIMARY KEY, " + " name VARCHAR(20) NOT NULL)"); + check_mysql_rc(rc, mysql); + 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')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 20, "rc != 20"); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1, t2"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_union2(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= "select col1 FROM t1 where col1=1 union distinct " + "select col1 FROM t1 where col1=2"; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1(col1 INT, \ + col2 VARCHAR(40), \ + col3 SMALLINT, \ + col4 TIMESTAMP)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 0, "rowcount != 0"); + } + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Misc tests to keep pure coverage happy */ + +static int test_pure_coverage(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + int rc; + ulong length; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_pure"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_pure(c1 int, c2 varchar(20))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "START TRANSACTION"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("insert into test_pure(c67788) values(10)")); + FAIL_IF(!rc, "Error expected"); + mysql_stmt_close(stmt); + + /* Query without params and result should allow one to bind 0 arrays */ + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("insert into test_pure(c2) values(10)")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, (MYSQL_BIND*)0); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_bind_result(stmt, (MYSQL_BIND*)0); + FAIL_UNLESS(rc == 1, ""); + + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("insert into test_pure(c2) values(?)")); + check_stmt_rc(rc, stmt); + + /* + We need to bzero bind structure because mysql_stmt_bind_param checks all + its members. + */ + memset(my_bind, '\0', 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); + FAIL_IF(!rc, "Error expected"); + + my_bind[0].buffer_type= MYSQL_TYPE_STRING; + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("select * from test_pure")); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + mysql_query(mysql, "DROP TABLE test_pure"); + return OK; +} + +static int test_insert_select(MYSQL *mysql) +{ + MYSQL_STMT *stmt_insert, *stmt_select; + char *query; + int rc; + uint i; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t2 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t2 values (1)"); + check_mysql_rc(rc, mysql); + + query= (char*)"insert into t1 select a from t2"; + stmt_insert= mysql_stmt_init(mysql); + FAIL_IF(!stmt_insert, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_insert, SL(query)); + check_stmt_rc(rc, stmt_insert); + + query= (char*)"select * from t1"; + stmt_select= mysql_stmt_init(mysql); + FAIL_IF(!stmt_select, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_select, SL(query)); + check_stmt_rc(rc, stmt_select); + + for(i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt_insert); + check_stmt_rc(rc, stmt_insert); + + rc= mysql_stmt_execute(stmt_select); + check_stmt_rc(rc, stmt_select); + rc= 0; + while (mysql_stmt_fetch(stmt_select) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == (int)(i+1), "rc != i+1"); + } + + mysql_stmt_close(stmt_insert); + mysql_stmt_close(stmt_select); + rc= mysql_query(mysql, "drop table t1, t2"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test simple prepare-insert */ + +static int test_insert(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char str_data[50]; + char tiny_data; + MYSQL_RES *result; + MYSQL_BIND my_bind[2]; + ulong length; + + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prep_insert"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_prep_insert(col1 tinyint, \ + col2 varchar(50))"); + check_mysql_rc(rc, mysql); + + /* insert by prepare */ + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO test_prep_insert VALUES(?, ?)")); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Param_count != 2"); + + /* + We need to bzero bind structure because mysql_stmt_bind_param checks all + its members. + */ + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + /* 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_stmt_rc(rc, stmt); + } + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* test the results now, only one row should exist */ + rc= mysql_query(mysql, "SELECT * FROM test_prep_insert"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS((int) tiny_data == rc, "rowcount != tinydata"); + mysql_free_result(result); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_prep_insert"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_join(MYSQL *mysql) +{ + 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)"}; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int , b int);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, + "insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t2 (a int , c int);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, + "insert into t2 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);"); + check_mysql_rc(rc, mysql); + + for (j= 0; j < 9; j++) + { + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query[j])); + check_stmt_rc(rc, stmt); + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 5, "rowcount != 5"); + } + mysql_stmt_close(stmt); + } + + rc= mysql_query(mysql, "DROP TABLE t1, t2"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_left_join_view(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= + "select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x);"; + + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1"); + check_mysql_rc(rc, mysql); + + rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"insert into t1 values (1), (2), (3)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"create view v1 (x) as select a from t1 where a > 1"); + check_mysql_rc(rc, mysql); + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 3, "rowcount != 3"); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test simple sample - manual */ + +static int test_manual_sample(MYSQL *mysql) +{ + unsigned int param_count; + MYSQL_STMT *stmt; + short small_data; + int int_data; + int rc; + char str_data[50]; + ulonglong affected_rows; + MYSQL_BIND my_bind[3]; + my_bool is_null; + char query[MAX_TEST_QUERY_LENGTH]; + + + /* + Sample which is incorporated directly in the manual under Prepared + statements section (Example from mysql_stmt_execute() + */ + + memset(str_data, 0, sizeof(str_data)); + mysql_autocommit(mysql, 1); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_table"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE test_table(col1 int, col2 varchar(50), \ + col3 smallint, \ + col4 timestamp)"); + check_mysql_rc(rc, mysql); + + /* Prepare a insert query with 3 parameters */ + strcpy(query, "INSERT INTO test_table(col1, col2, col3) values(?, ?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + /* Get the parameter count from the statement */ + param_count= mysql_stmt_param_count(stmt); + FAIL_IF(param_count != 3, "param_count != 3"); + + memset(my_bind, '\0', 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 */ + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + /* Specify the data */ + int_data= 10; /* integer */ + strcpy(str_data, "MySQL"); /* string */ + + /* INSERT SMALLINT data as NULL */ + is_null= 1; + + /* Execute the insert statement - 1*/ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + /* Get the total rows affected */ + affected_rows= mysql_stmt_affected_rows(stmt); + FAIL_IF(affected_rows != 1, "affected-rows != 1"); + + /* Re-execute the insert, by changing the values */ + int_data= 1000; + strcpy(str_data, "The most popular open source database"); + small_data= 1000; /* smallint */ + is_null= 0; /* reset */ + + /* Execute the insert statement - 2*/ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + /* Get the total rows affected */ + affected_rows= mysql_stmt_affected_rows(stmt); + + FAIL_IF(affected_rows != 1, "affected_rows != 1"); + + /* Close the statement */ + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + /* DROP THE TABLE */ + rc= mysql_query(mysql, "DROP TABLE test_table"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_create_drop(MYSQL *mysql) +{ + MYSQL_STMT *stmt_create, *stmt_drop, *stmt_select, *stmt_create_select; + char *query; + int rc, i; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t2 (a int);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (a int);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t2 values (3), (2), (1);"); + check_mysql_rc(rc, mysql); + + query= (char*)"create table t1 (a int)"; + stmt_create= mysql_stmt_init(mysql); + FAIL_IF(!stmt_create, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_create, SL(query)); + check_stmt_rc(rc, stmt_create); + + query= (char*)"drop table t1"; + stmt_drop= mysql_stmt_init(mysql); + FAIL_IF(!stmt_drop, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_drop, SL(query)); + check_stmt_rc(rc, stmt_drop); + + query= (char*)"select a in (select a from t2) from t1"; + stmt_select= mysql_stmt_init(mysql); + FAIL_IF(!stmt_select, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_select, SL(query)); + check_stmt_rc(rc, stmt_select); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + query= (char*)"create table t1 select a from t2"; + stmt_create_select= mysql_stmt_init(mysql); + FAIL_IF(!stmt_create_select, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_create_select, SL(query)); + check_stmt_rc(rc, stmt_create_select); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt_create); + check_stmt_rc(rc, stmt_create); + + rc= mysql_stmt_execute(stmt_select); + check_stmt_rc(rc, stmt_select); + + rc= 0; + while (mysql_stmt_fetch(stmt_select) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 0, "rowcount != 0"); + + rc= mysql_stmt_execute(stmt_drop); + check_stmt_rc(rc, stmt_drop); + + rc= mysql_stmt_execute(stmt_create_select); + check_stmt_rc(rc, stmt_create_select); + + rc= mysql_stmt_execute(stmt_select); + check_stmt_rc(rc, stmt_select); + rc= 0; + while (mysql_stmt_fetch(stmt_select) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 3, "rowcount != 3"); + + rc= mysql_stmt_execute(stmt_drop); + check_stmt_rc(rc, stmt_drop); + } + + 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"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test DATE, TIME, DATETIME and TS with MYSQL_TIME conversion */ + +static int test_date(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_date(c1 TIMESTAMP, \ + c2 TIME, \ + c3 DATETIME, \ + c4 DATE)"); + + check_mysql_rc(rc, mysql); + + rc= test_bind_date_conv(mysql, 5); + mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + return rc; +} + + +/* Test all time types to DATE and DATE to all types */ + +static int test_date_date(MYSQL *mysql) +{ + int rc; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_date(c1 DATE, \ + c2 DATE, \ + c3 DATE, \ + c4 DATE)"); + + check_mysql_rc(rc, mysql); + + rc= test_bind_date_conv(mysql, 3); + mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + return rc; +} + +/* Test all time types to TIMESTAMP and TIMESTAMP to all types */ + +static int test_date_ts(MYSQL *mysql) +{ + int rc; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_date(c1 TIMESTAMP, \ + c2 TIMESTAMP, \ + c3 TIMESTAMP, \ + c4 TIMESTAMP)"); + + check_mysql_rc(rc, mysql); + + rc= test_bind_date_conv(mysql, 2); + mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + return rc; +} + + +/* Test all time types to DATETIME and DATETIME to all types */ + +static int test_date_dt(MYSQL *mysql) +{ + int rc; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_date(c1 datetime, " + " c2 datetime, c3 datetime, c4 date)"); + check_mysql_rc(rc, mysql); + + rc= test_bind_date_conv(mysql, 2); + mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + return rc; +} + +/* Test all time types to TIME and TIME to all types */ + +static int test_date_time(MYSQL *mysql) +{ + int rc; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_date(c1 TIME, \ + c2 TIME, \ + c3 TIME, \ + c4 TIME)"); + + check_mysql_rc(rc, mysql); + + rc= test_bind_date_conv(mysql, 3); + mysql_query(mysql, "DROP TABLE IF EXISTS test_date"); + return rc; +} + +/* + Test of basic checks that are performed in server for components + of MYSQL_TIME parameters. +*/ + +static int test_datetime_ranges(MYSQL *mysql) +{ + const char *stmt_text; + int rc, i; + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[6]; + MYSQL_TIME tm[6]; + + if (!is_mariadb) + return SKIP; + + stmt_text= "drop table if exists t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "create table t1 (year datetime, month datetime, day datetime, " + "hour datetime, min datetime, sec datetime)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + stmt_text= "INSERT INTO t1 VALUES (?, ?, ?, ?, ?, ?)"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + FAIL_IF(mysql_stmt_param_count(stmt) != 6, "param_count != 6"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + + FAIL_IF(!mysql_warning_count(mysql), "warnings expected"); + + if (verify_col_data(mysql, "t1", "year", "0000-00-00 00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "month", "0000-00-00 00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "day", "0000-00-00 00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "hour", "0000-00-00 00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "min", "0000-00-00 00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "sec", "0000-00-00 00:00:00")) + goto error; + + mysql_stmt_close(stmt); + + stmt_text= "delete from t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "INSERT INTO t1 (year, month, day) VALUES (?, ?, ?)"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + /* + 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(!mysql_warning_count(mysql), "warnings expected"); + + if (verify_col_data(mysql, "t1", "year", "0000-00-00 00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "month", "0000-00-00 00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "day", "0000-00-00 00:00:00")) + goto error; + + mysql_stmt_close(stmt); + + stmt_text= "drop table t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "create table t1 (day_ovfl time, day time, hour time, min time, sec time)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + stmt_text= "INSERT INTO t1 VALUES (?,?,?,?,?)"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + FAIL_IF(mysql_stmt_param_count(stmt) != 5, "param_count != 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_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + FAIL_IF(mysql_warning_count(mysql) != 2, "warning_count != 2"); + + if (verify_col_data(mysql, "t1", "day_ovfl", "838:59:59")) + goto error; + if (verify_col_data(mysql, "t1", "day", "828:30:30")) + goto error; + if (verify_col_data(mysql, "t1", "hour", "270:30:30")) + goto error; + if (verify_col_data(mysql, "t1", "min", "00:00:00")) + goto error; + if (verify_col_data(mysql, "t1", "sec", "00:00:00")) + goto error; + + mysql_stmt_close(stmt); + stmt_text= "drop table t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +error: + mysql_stmt_close(stmt); + stmt_text= "drop table t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_derived(MYSQL *mysql) +{ + 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"; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (id int(8), primary key (id)) \ +ENGINE=InnoDB DEFAULT CHARSET=utf8"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values (1)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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= &my_null; + my_val= 1; + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_distinct(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= + "SELECT 2+count(distinct b), group_concat(a) FROM t1 group by a"; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int , b int);"); + check_mysql_rc(rc, mysql); + + 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);"); + check_mysql_rc(rc, mysql); + + for (i= 0; i < 3; i++) + { + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 5, "rowcount != 5"); + mysql_stmt_close(stmt); + } + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_do_set(MYSQL *mysql) +{ + MYSQL_STMT *stmt_do, *stmt_set; + char *query; + int rc, i; + + // XPAND doesn't support DO command + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (a int)"); + check_mysql_rc(rc, mysql); + + query= (char*)"do @var:=(1 in (select * from t1))"; + stmt_do= mysql_stmt_init(mysql); + FAIL_IF(!stmt_do, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_do, SL(query)); + check_stmt_rc(rc, stmt_do); + + query= (char*)"set @var=(1 in (select * from t1))"; + stmt_set= mysql_stmt_init(mysql); + FAIL_IF(!stmt_set, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_set, SL(query)); + check_stmt_rc(rc, stmt_set); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt_do); + check_stmt_rc(rc, stmt_do); + rc= mysql_stmt_execute(stmt_set); + check_stmt_rc(rc, stmt_set); + } + + mysql_stmt_close(stmt_do); + mysql_stmt_close(stmt_set); + return OK; +} + +static int test_double_compare(MYSQL *mysql) +{ + 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]; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_double_compare"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_double_compare(col1 tinyint, " + " col2 float, col3 double )"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_double_compare " + "VALUES (1, 10.2, 34.5)"); + check_mysql_rc(rc, mysql); + + strcpy(query, "UPDATE test_double_compare SET col1=100 " + "WHERE col1 = ? AND col2 = ? AND COL3 = ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 3, "param_count != 3"); + + memset(my_bind, '\0', 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]; + + /* double */ + my_bind[2].buffer_type= MYSQL_TYPE_DOUBLE; + my_bind[2].buffer= (void *)&double_data; + + tiny_data= 1; + strcpy(real_data, "10.2"); + length[1]= (ulong)strlen(real_data); + double_data= 34.5; + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt), "affected_rows != 0"); + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* test the results now, only one row should exist */ + rc= mysql_query(mysql, "SELECT * FROM test_double_compare"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS((int)tiny_data == rc, "rowcount != tinydata"); + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_double_compare"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_multi(MYSQL *mysql) +{ + 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; + + /* + We need to bzero bind structure because mysql_stmt_bind_param checks all + its members. + */ + memset(my_bind, '\0', 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"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (a int, b int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t2 (a int, b int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values (3, 3), (2, 2), (1, 1)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t2 values (3, 3), (2, 2), (1, 1)"); + check_mysql_rc(rc, mysql); + + query= (char*)"delete t1, t2 from t1, t2 where t1.a=t2.a and t1.b=10"; + stmt_delete= mysql_stmt_init(mysql); + FAIL_IF(!stmt_delete, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_delete, SL(query)); + check_stmt_rc(rc, 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_stmt_init(mysql); + FAIL_IF(!stmt_update, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_update, SL(query)); + check_stmt_rc(rc, stmt_update); + + query= (char*)"select * from t1"; + stmt_select1= mysql_stmt_init(mysql); + FAIL_IF(!stmt_select1, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_select1, SL(query)); + check_stmt_rc(rc, stmt_select1); + + query= (char*)"select * from t2"; + stmt_select2= mysql_stmt_init(mysql); + FAIL_IF(!stmt_select2, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_select2, SL(query)); + check_stmt_rc(rc, stmt_select2); + + for(i= 0; i < 3; i++) + { + rc= mysql_stmt_bind_param(stmt_update, my_bind); + check_stmt_rc(rc, stmt_update); + + rc= mysql_stmt_execute(stmt_update); + check_stmt_rc(rc, stmt_update); + + rc= mysql_stmt_execute(stmt_delete); + check_stmt_rc(rc, stmt_delete); + + rc= mysql_stmt_execute(stmt_select1); + check_stmt_rc(rc, stmt_select1); + rc= 0; + while (!mysql_stmt_fetch(stmt_select1)) + rc++; + FAIL_UNLESS(rc == 3-param, "rc != 3 - param"); + + rc= mysql_stmt_execute(stmt_select2); + check_stmt_rc(rc, stmt_select2); + rc= 0; + while (!mysql_stmt_fetch(stmt_select2)) + rc++; + FAIL_UNLESS(rc == 3-param, "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"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Multiple stmts .. */ + +static int test_multi_stmt(MYSQL *mysql) +{ + + 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]; + const char *query; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_multi_table"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_multi_table(id int, name char(20))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_multi_table values(10, 'mysql')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + query= "SELECT * FROM test_multi_table WHERE id=?"; + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + stmt2= mysql_stmt_init(mysql); + FAIL_IF(!stmt2, mysql_error(mysql)); + query= "UPDATE test_multi_table SET name='updated' WHERE id=10"; + rc= mysql_stmt_prepare(stmt2, SL(query)); + check_stmt_rc(rc, stmt2); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "param_count != 1"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + id= 10; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + id= 999; + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(id == 10, "id != 10"); + FAIL_UNLESS(strcmp(name, "mysql") == 0, "name != 'mysql'"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, ""); + + /* alter the table schema now */ + stmt1= mysql_stmt_init(mysql); + FAIL_IF(!stmt1, mysql_error(mysql)); + query= "DELETE FROM test_multi_table WHERE id=? AND CONVERT(name USING utf8)=?"; + rc= mysql_stmt_prepare(stmt1, SL(query)); + check_stmt_rc(rc, stmt1); + + FAIL_IF(mysql_stmt_param_count(stmt1) != 2, "param_count != 2"); + + rc= mysql_stmt_bind_param(stmt1, my_bind); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_execute(stmt2); + check_stmt_rc(rc, stmt2); + + FAIL_IF(mysql_stmt_affected_rows(stmt2) != 1, "affected_rows != 1"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(id == 10, "id != 10"); + FAIL_UNLESS(strcmp(name, "updated") == 0, "name != 'updated'"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + FAIL_IF(mysql_stmt_affected_rows(stmt1) != 1, "affected_rows != 1"); + + mysql_stmt_close(stmt1); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + rc= my_stmt_result(mysql, "SELECT * FROM test_multi_table"); + FAIL_UNLESS(rc == 0, "rc != 0"); + + mysql_stmt_close(stmt); + mysql_stmt_close(stmt2); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_multi_table"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Test 'n' statements create and close */ + +static int test_nstmts(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + char query[255]; + int rc; + static uint i, total_stmts= 2000; + MYSQL_BIND my_bind[1]; + + SKIP_SKYSQL; + + mysql_autocommit(mysql, TRUE); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_nstmts"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_nstmts(id int)"); + check_mysql_rc(rc, mysql); + + memset(my_bind, '\0', sizeof(my_bind)); + + my_bind[0].buffer= (void *)&i; + my_bind[0].buffer_type= MYSQL_TYPE_LONG; + + for (i= 0; i < total_stmts; i++) + { + strcpy(query, "insert into test_nstmts values(?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + } + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(" select count(*) from test_nstmts")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + i= 0; + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS( i == total_stmts, "total_stmts != i"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE test_nstmts"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test simple null */ + +static int test_null(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + uint nData; + MYSQL_BIND my_bind[2]; + my_bool is_null[2]; + char query[MAX_TEST_QUERY_LENGTH]; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_null"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_null(col1 int, col2 varchar(50))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "FLUSH TABLES"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "START TRANSACTION"); + check_mysql_rc(rc, mysql); + + /* insert by prepare, wrong column name */ + strcpy(query, "INSERT INTO test_null(col3, col2) VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + FAIL_IF(!rc, "Error expected"); + mysql_stmt_close(stmt); + + strcpy(query, "INSERT INTO test_null(col1, col2) VALUES(?, ?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "param_count != 2"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + /* now, execute the prepared statement to insert 10 records.. */ + for (nData= 0; nData<10; nData++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + + /* 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_stmt_rc(rc, stmt); + + for (nData= 0; nData<10; nData++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + + mysql_stmt_close(stmt); + + /* now fetch the results ..*/ + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + nData*= 2; + rc= my_stmt_result(mysql, "SELECT * FROM test_null");; + FAIL_UNLESS((int) nData == rc, "rc != ndata"); + + /* 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_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_null")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= 0; + is_null[0]= is_null[1]= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + { + FAIL_UNLESS(is_null[0], "!is_null"); + FAIL_UNLESS(is_null[1], "!is_null"); + rc++; + is_null[0]= is_null[1]= 0; + } + FAIL_UNLESS(rc == (int) nData, "rc != nData"); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_null"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_order_param(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *query; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1(a INT, b char(10))"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + query= "select sum(a) + 200, 1 from t1 " + " union distinct " + "select sum(a) + 200, 1 from t1 group by b "; + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + query= "select sum(a) + 200, ? from t1 group by b " + " union distinct " + "select sum(a) + 200, 1 from t1 group by b "; + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + query= "select sum(a) + 200, ? from t1 " + " union distinct " + "select sum(a) + 200, 1 from t1 group by b "; + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_rename(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *query= "rename table t1 to t2, t3 to t4"; + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2, t3, t4"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "create table t1 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(!rc, "Error expected"); + + rc= mysql_query(mysql, "create table t3 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(!rc, "Errr expected"); + + rc= mysql_query(mysql, "rename table t2 to t1, t4 to t3"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t2, t4"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_rewind(MYSQL *mysql) +{ + 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; + + + stmt_text= "CREATE TABLE t1 (a int)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "INSERT INTO t1 VALUES(2),(3),(4)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + stmt_text= "SELECT * FROM t1"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + memset(&my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, &my_bind); + check_stmt_rc(rc, stmt); + + /* retrieve all result sets till we are at the end */ + while(!(rc=mysql_stmt_fetch(stmt))); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "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))); + + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + stmt_text= "DROP TABLE t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + rc= mysql_stmt_free_result(stmt); + rc= mysql_stmt_close(stmt); + return OK; +} + +/* Test simple select */ + +static int test_select(MYSQL *mysql) +{ + 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]; + + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_select(id int, name varchar(50))"); + check_mysql_rc(rc, mysql); + + /* insert a row and commit the transaction */ + rc= mysql_query(mysql, "INSERT INTO test_select VALUES(10, 'venu')"); + check_mysql_rc(rc, mysql); + + /* now insert the second row, and roll back the transaction */ + rc= mysql_query(mysql, "INSERT INTO test_select VALUES(20, 'mysql')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + strcpy(query, "SELECT * FROM test_select WHERE id= ? " + "AND CONVERT(name USING utf8) =?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "param_count != 2"); + + memset(my_bind, '\0', sizeof(my_bind)); + + /* string data */ + nData= 10; + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 1, "rc != 1"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test simple select with prepare */ + +static int test_select_prepare(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_select(id int, name varchar(50))"); + check_mysql_rc(rc, mysql); + + /* insert a row and commit the transaction */ + rc= mysql_query(mysql, "INSERT INTO test_select VALUES(10, 'venu')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_select")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE test_select"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_select(id tinyint, id1 int, " + " id2 float, id3 float, " + " name varchar(50))"); + check_mysql_rc(rc, mysql); + + /* 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')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_select")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test simple show */ + +static int test_select_show_table(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SHOW TABLES FROM mysql")); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt), "param_count != 0"); + + for (i= 1; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + + while (!mysql_stmt_fetch(stmt)); + mysql_stmt_close(stmt); + return OK; +} + +/* Test simple select */ + +static int test_select_version(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT @@version")); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt), "param_count != 0"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + while (!mysql_stmt_fetch(stmt)); + mysql_stmt_close(stmt); + return OK; +} + +static int test_selecttmp(MYSQL *mysql) +{ + 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"; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2, t3"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int , b int);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t2 (a int, b int);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t3 (a int, b int);"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, + "insert into t1 values (0, 100), (1, 2), (1, 3), (2, 2), (2, 7), \ +(2, -1), (3, 10);"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, + "insert into t2 values (0, 0), (1, 1), (2, 1), (3, 1), (4, 1);"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, + "insert into t3 values (3, 3), (2, 2), (1, 1);"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 3, "rowcount != 3"); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1, t2, t3"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_set_option(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_RES *result; + int rc; + + + mysql_autocommit(mysql, TRUE); + + /* LIMIT the rows count to 2 */ + rc= mysql_query(mysql, "SET SQL_SELECT_LIMIT= 2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_limit"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_limit(a tinyint)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_limit VALUES(10), (20), (30), (40)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT * FROM test_limit"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 2, "rowcount != 2"); + mysql_free_result(result); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_limit")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 2, ""); + + mysql_stmt_close(stmt); + + /* RESET the LIMIT the rows count to 0 */ + rc= mysql_query(mysql, "SET SQL_SELECT_LIMIT=DEFAULT"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_limit")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 4, "rowcount != 4"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_limit"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test simple set-variable prepare */ + +static int test_set_variable(MYSQL *mysql) +{ + 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]; + + + mysql_autocommit(mysql, TRUE); + + stmt1= mysql_stmt_init(mysql); + FAIL_IF(!stmt1, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt1, SL("show variables like 'max_error_count'")); + check_stmt_rc(rc, stmt1); + + memset(get_bind, '\0', 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_stmt_rc(rc, stmt1); + + rc= mysql_stmt_bind_result(stmt1, get_bind); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + + def_count= get_count; + + FAIL_UNLESS(strcmp(var, "max_error_count") == 0, "var != max_error_count"); + rc= mysql_stmt_fetch(stmt1); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("set max_error_count=?")); + check_stmt_rc(rc, stmt); + + memset(set_bind, '\0', 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_stmt_rc(rc, stmt); + + set_count= 31; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_commit(mysql); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + + FAIL_UNLESS(get_count == set_count, "get_count != set_count"); + + rc= mysql_stmt_fetch(stmt1); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + /* restore back to default */ + set_count= def_count; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + + FAIL_UNLESS(get_count == set_count, "get_count != set_count"); + + rc= mysql_stmt_fetch(stmt1); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + mysql_stmt_close(stmt1); + return OK; +} + +/* Test SQLmode */ + +static int test_sqlmode(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + char c1[5], c2[5]; + int rc; + int ignore_space= 0; + char query[MAX_TEST_QUERY_LENGTH]; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_piping"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_piping(name varchar(10))"); + check_mysql_rc(rc, mysql); + + /* PIPES_AS_CONCAT */ + strcpy(query, "SET SQL_MODE= \"PIPES_AS_CONCAT\""); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_piping VALUES(?||?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + strcpy(c1, "My"); strcpy(c2, "SQL"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + if (verify_col_data(mysql, "test_piping", "name", "MySQL")) + return FAIL; + + rc= mysql_query(mysql, "DELETE FROM test_piping"); + check_mysql_rc(rc, mysql); + + strcpy(query, "SELECT connection_id ()"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + /* ANSI */ + strcpy(query, "SET SQL_MODE= \"ANSI\""); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO test_piping VALUES(?||?)"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + strcpy(c1, "My"); strcpy(c2, "SQL"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + if (verify_col_data(mysql, "test_piping", "name", "MySQL")) + return FAIL; + + /* ANSI mode spaces ... + skip, if ignore_space was set + */ + query_int_variable(mysql, "@@sql_mode LIKE '%IGNORE_SPACE%'", &ignore_space); + + if (!ignore_space) + { + strcpy(query, "SELECT connection_id ()"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + } + /* IGNORE SPACE MODE */ + strcpy(query, "SET SQL_MODE= \"IGNORE_SPACE\""); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + strcpy(query, "SELECT connection_id ()"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_piping"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test mysql_stmt_close for open stmts */ + +static int test_stmt_close(MYSQL *mysql) +{ + 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]; + my_bool reconnect= 1; + + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + + /* set AUTOCOMMIT to ON*/ + mysql_autocommit(mysql, TRUE); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_stmt_close"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_stmt_close(id int)"); + check_mysql_rc(rc, mysql); + + strcpy(query, "DO \"nothing\""); + stmt1= mysql_stmt_init(mysql); + FAIL_IF(!stmt1, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt1, SL(query)); + check_stmt_rc(rc, stmt1); + + FAIL_IF(mysql_stmt_param_count(stmt1), "param_count != 0"); + + strcpy(query, "INSERT INTO test_stmt_close(id) VALUES(?)"); + stmt_x= mysql_stmt_init(mysql); + FAIL_IF(!stmt_x, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt_x, SL(query)); + check_stmt_rc(rc, stmt_x); + + FAIL_IF(mysql_stmt_param_count(stmt_x) != 1, "param_count != 1"); + + strcpy(query, "UPDATE test_stmt_close SET id= ? WHERE id= ?"); + stmt3= mysql_stmt_init(mysql); + FAIL_IF(!stmt3, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt3, SL(query)); + check_stmt_rc(rc, stmt3); + + FAIL_IF(mysql_stmt_param_count(stmt3) != 2, "param_count != 2"); + + strcpy(query, "SELECT * FROM test_stmt_close WHERE id= ?"); + stmt2= mysql_stmt_init(mysql); + FAIL_IF(!stmt2, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt2, SL(query)); + check_stmt_rc(rc, stmt2); + + FAIL_IF(mysql_stmt_param_count(stmt2) != 1, "param_count != 1"); + + rc= mysql_stmt_close(stmt1); + + /* + 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); + + /* + We need to bzero bind structure because mysql_stmt_bind_param checks all + its members. + */ + memset(my_bind, '\0', 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_stmt_rc(rc, stmt_x); + + rc= mysql_stmt_execute(stmt_x); + check_stmt_rc(rc, stmt_x); + + FAIL_IF(mysql_stmt_affected_rows(stmt_x) != 1, "affected_rows != 1"); + + rc= mysql_stmt_close(stmt_x); + check_stmt_rc(rc, stmt_x); + + rc= mysql_query(mysql, "SELECT id FROM test_stmt_close"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 1, "rwcount != 1"); + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_stmt_close"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_new_date(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + int rc; + char buffer[50]; + my_bool reconnect= 1; + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + + /* set AUTOCOMMIT to ON*/ + mysql_autocommit(mysql, TRUE); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a date, b date)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (now(), now() + INTERVAL 1 day)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, "SELECT if(1, a, b) FROM t1", 26); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_length= 50; + bind[0].buffer= (void *)buffer; + bind[0].buffer_type= MYSQL_TYPE_STRING; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc != MYSQL_NO_DATA, "NO DATA expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_long_data1(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + MYSQL_BIND bind[1]; + char query[MAX_TEST_QUERY_LENGTH]; + const char *data= "12345"; + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS tld"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE tld (col1 int, " + "col2 MEDIUMTEXT)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO tld VALUES (1,'test')"); + check_mysql_rc(rc, mysql); + + strcpy(query, "UPDATE tld SET col2=? WHERE col1=1"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_STRING; + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_send_long_data(stmt, 0, data, 6); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS tld"); + check_mysql_rc(rc, mysql); + return OK; +} + +int test_blob_9000(MYSQL *mysql) +{ + MYSQL_BIND bind[1]; + MYSQL_STMT *stmt; + int rc; + char buffer[9200]; + const char *query= "INSERT INTO tb9000 VALUES (?)"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS tb9000"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE tb9000 (a blob)"); + check_mysql_rc(rc, mysql); + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + + memset(bind, 0, sizeof(MYSQL_BIND)); + memset(buffer, 'C', 9200); + bind[0].buffer= buffer; + bind[0].buffer_length= 9200; + bind[0].buffer_type= MYSQL_TYPE_STRING; + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS tb9000"); + check_mysql_rc(rc, mysql); + return OK; +} + +int test_fracseconds(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *str= "SELECT NOW(6)"; + char buffer[60], buffer1[60]; + MYSQL_BIND bind[2]; + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(str)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer= buffer; + bind[0].buffer_length=60; + bind[0].buffer_type= MYSQL_TYPE_STRING; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(strlen(buffer) != 26, "Expected timestamp with length of 26"); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a timestamp(6), b time(6))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES ('2012-04-25 10:20:49.0194','10:20:49.0194' )"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, "SELECT a,b FROM t1", 18); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + bind[0].buffer= buffer; + bind[1].buffer= buffer1; + bind[0].buffer_length= bind[1].buffer_length= 60; + bind[0].buffer_type= bind[1].buffer_type= MYSQL_TYPE_STRING; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(strcmp(buffer, "2012-04-25 10:20:49.019400") != 0, "Wrong result"); + FAIL_IF(strcmp(buffer1, "10:20:49.019400") != 0, "Wrong result"); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + + return OK; +} + +int test_notrunc(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + my_bool trunc= 1; + MYSQL_BIND bind[2]; + char buffer[5], buffer2[5]; + int rc; + my_bool error= 0; + unsigned long len= 1; + + const char *query= "SELECT '1234567890', 'foo' FROM DUAL"; + + mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, &trunc); + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + strcpy(buffer, "bar"); + + memset(bind, 0, sizeof(MYSQL_BIND) * 2); + bind[0].buffer_type= MYSQL_TYPE_NULL; + bind[0].buffer= buffer; + bind[0].buffer_length= 1; + bind[0].length= &len; + bind[0].flags|= MADB_BIND_DUMMY; + bind[0].error= &error; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= buffer2; + bind[1].buffer_length= 5; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + mysql_stmt_store_result(stmt); + + rc= mysql_stmt_fetch(stmt); + mysql_stmt_close(stmt); + + FAIL_IF(rc!= 0, "expected rc= 0"); + FAIL_IF(strcmp(buffer, "bar"), "Bind dummy failed"); + FAIL_IF(strcmp(buffer2, "foo"), "Invalid second buffer"); + + return OK; +} + +static int test_bit2tiny(MYSQL *mysql) +{ + MYSQL_BIND bind[2]; + char data[11]; + unsigned long length[2]; + my_bool is_null[2], error[2]; + const char *query = "SELECT val FROM justbit"; + MYSQL_STMT *stmt; + int rc; + + mysql_query(mysql, "DROP TABLE IF EXISTS justbit"); + mysql_query(mysql, "CREATE TABLE justbit(val bit(1) not null)"); + mysql_query(mysql, "INSERT INTO justbit values (1)"); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + memset(bind, '\0', sizeof(bind)); + + bind[0].buffer_type= MYSQL_TYPE_TINY; + bind[0].buffer= &data[0]; + bind[0].buffer_length= 1; + bind[0].is_null= &is_null[0]; + bind[0].length= &length[0]; + bind[0].error= &error[0]; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_fetch(stmt); + + FAIL_IF(data[0] != 1, "Value should be 1"); + + mysql_stmt_free_result(stmt); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS justbit"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_reexecute(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND ps_params[3]; /* input parameter buffers */ + int int_data[3]; /* input/output values */ + int rc; + + if (!mariadb_connection(mysql)) + return SKIP; + + /* set up stored procedure */ + rc = mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + rc = mysql_query(mysql, + "CREATE PROCEDURE p1(" + " IN p_in INT, " + " OUT p_out INT, " + " INOUT p_inout INT) " + "BEGIN " + " SELECT p_in, p_out, p_inout; " + " SET p_in = 100, p_out = 200, p_inout = 300; " + " SELECT p_in, p_out, p_inout; " + "END"); + check_mysql_rc(rc, mysql); + + /* initialize and prepare CALL statement with parameter placeholders */ + stmt = mysql_stmt_init(mysql); + if (!stmt) + { + diag("Could not initialize statement"); + exit(1); + } + rc = mysql_stmt_prepare(stmt, "CALL p1(?, ?, ?)", 16); + check_stmt_rc(rc, stmt); + + /* initialize parameters: p_in, p_out, p_inout (all INT) */ + 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; + + ps_params[1].buffer_type = MYSQL_TYPE_LONG; + ps_params[1].buffer = (char *) &int_data[1]; + ps_params[1].length = 0; + ps_params[1].is_null = 0; + + ps_params[2].buffer_type = MYSQL_TYPE_LONG; + ps_params[2].buffer = (char *) &int_data[2]; + ps_params[2].length = 0; + ps_params[2].is_null = 0; + + /* bind parameters */ + rc = mysql_stmt_bind_param(stmt, ps_params); + check_stmt_rc(rc, stmt); + + /* assign values to parameters and execute statement */ + int_data[0]= 10; /* p_in */ + int_data[1]= 20; /* p_out */ + int_data[2]= 30; /* p_inout */ + + rc = mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc = mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_prepare_error(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + + rc= mysql_stmt_prepare(stmt, SL("SELECT 1 FROM tbl_not_exists")); + FAIL_IF(!rc, "Expected error"); + + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_prepare(stmt, SL("SELECT 1 FROM tbl_not_exists")); + FAIL_IF(!rc, "Expected error"); + + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_prepare(stmt, SL("SET @a:=1")); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + return OK; +} + +static int test_conc349(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + enum mysql_stmt_state state; + + rc= mysql_stmt_attr_get(stmt, STMT_ATTR_STATE, &state); + FAIL_IF(state != MYSQL_STMT_INITTED, "expected status MYSQL_STMT_INITTED"); + + rc= mysql_stmt_prepare(stmt, SL("SET @a:=1")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_get(stmt, STMT_ATTR_STATE, &state); + FAIL_IF(state != MYSQL_STMT_PREPARED, "expected status MYSQL_STMT_PREPARED"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_get(stmt, STMT_ATTR_STATE, &state); + FAIL_IF(state != MYSQL_STMT_EXECUTED, "expected status MYSQL_STMT_EXECUTED"); + + mysql_stmt_close(stmt); + return OK; +} + +static int test_conc565(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_FIELD *fields_binary, *fields_text; + MYSQL_RES *result; + int rc; + unsigned int i; + my_bool x=1; + my_bool error= 0; + + rc= mysql_query(mysql, "CREATE TEMPORARY TABLE t1 (a year, b tinyint unsigned, c smallint unsigned, d mediumint unsigned, e int unsigned, f bigint unsigned)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (2020, 127, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, "select a,b,c,d,e,f from t1", -1); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void *)&x); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_store_result(stmt); + fields_binary= mariadb_stmt_fetch_fields(stmt); + + rc= mysql_query(mysql, "SELECT a,b,c,d,e,f FROM t1"); + result= mysql_store_result(mysql); + fields_text= mysql_fetch_fields(result); + + for (i=0; i < mysql_field_count(mysql); i++) + { + if (fields_binary[i].length != fields_text[i].length || + fields_binary[i].max_length != fields_text[i].max_length) + { + diag("Sizes differ for column %d (type= %d)", i, fields_binary[i].type); + diag("Binary (length=%ld max_length=%ld) != Text(length=%ld max_length=%ld", + fields_binary[i].length, fields_binary[i].max_length, + fields_text[i].length, fields_text[i].max_length); + error= 1; + goto end; + } + } +end: + mysql_free_result(result); + mysql_stmt_close(stmt); + + return error ? FAIL : OK; +} + +struct my_tests_st my_tests[] = { + {"test_conc565", test_conc565, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc349", test_conc349, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_prepare_error", test_prepare_error, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_reexecute", test_reexecute, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_bit2tiny", test_bit2tiny, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc97", test_conc97, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc83", test_conc83, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc60", test_conc60, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_notrunc", test_notrunc, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_fracseconds", test_fracseconds, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_blob_9000", test_blob_9000, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_long_data1", test_long_data1, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_insert_update", test_prepare_insert_update, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_simple", test_prepare_simple, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_syntax", test_prepare_syntax, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_field_result", test_prepare_field_result, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare", test_prepare, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_ext", test_prepare_ext, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_multi_statements", test_prepare_multi_statements, TEST_CONNECTION_NEW, 0, NULL , NULL}, + {"test_prepare_alter", test_prepare_alter, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_resultset", test_prepare_resultset, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_open_direct", test_open_direct, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_select_show", test_select_show, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_select", test_select, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_long_data", test_long_data, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_long_data_str", test_long_data_str, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_long_data_str1", test_long_data_str1, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_long_data_bin", test_long_data_bin, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_simple_update", test_simple_update, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_simple_delete", test_simple_delete, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_update", test_update, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_prepare_noparam", test_prepare_noparam, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bind_result", test_bind_result, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bind_result_ext", test_bind_result_ext, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bind_result_ext1", test_bind_result_ext1, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bind_negative", test_bind_negative, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_buffers", test_buffers, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_xjoin", test_xjoin, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_union", test_union, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_union2", test_union2, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_union_param", test_union_param, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_pure_coverage", test_pure_coverage, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_insert_select", test_insert_select, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_insert", test_insert, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_join", test_join, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_left_join_view", test_left_join_view, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_manual_sample", test_manual_sample, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_create_drop", test_create_drop, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_date", test_date, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_date_ts", test_date_ts, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_date_dt", test_date_dt, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_date_date", test_date_date, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_date_time", test_date_time, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_datetime_ranges", test_datetime_ranges, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_derived", test_derived, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_distinct", test_distinct, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_do_set", test_do_set, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_double_compare", test_double_compare, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_multi", test_multi, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_multi_stmt", test_multi_stmt, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_nstmts", test_nstmts, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_null", test_null, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_order_param", test_order_param, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_rename", test_rename, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_rewind", test_rewind, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_select_prepare", test_select_prepare, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_select_show_table", test_select_show_table, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_select_version", test_select_version, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_selecttmp", test_selecttmp, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_set_option", test_set_option, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_set_variable", test_set_variable, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_sqlmode", test_sqlmode, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_stmt_close", test_stmt_close, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_new_date", test_new_date, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/ps_bugs.c b/libmariadb/unittest/libmariadb/ps_bugs.c new file mode 100644 index 00000000..efe3d447 --- /dev/null +++ b/libmariadb/unittest/libmariadb/ps_bugs.c @@ -0,0 +1,5846 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" +#include <math.h> + +#define MY_INT64_NUM_DECIMAL_DIGITS 21 +#define MAX_INDEXES 64 + +/* A workaround for Sun Forte 5.6 on Solaris x86 */ + +static int cmp_double(double *a, double *b) +{ + return *a == *b; + return OK; +} + +/* Test BUG#1115 (incorrect string parameter value allocation) */ + +static int test_conc67(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + const char *query= "SELECT a,b FROM conc67 WHERE a=?"; + int rc, i; + MYSQL_BIND bind[2]; + char val[20]; + MYSQL_BIND rbind; + MYSQL_RES *res; + ulong prefetch_rows= 1000; + ulong cursor_type= CURSOR_TYPE_READ_ONLY; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc67"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE conc67 (a int, b text)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO conc67 VALUES (1, 'foo')"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor_type); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREFETCH_ROWS, &prefetch_rows); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + memset(&rbind, 0, sizeof(MYSQL_BIND)); + i= 1; + rbind.buffer_type= MYSQL_TYPE_LONG; + rbind.buffer= &i; + rbind.buffer_length= 4; + mysql_stmt_bind_param(stmt, &rbind); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + res= mysql_stmt_result_metadata(stmt); + mysql_free_result(res); + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + + i= 0; + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= &i; + bind[0].buffer_length= 4; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer= &val; + bind[1].buffer_length= 20; + + mysql_stmt_bind_result(stmt, bind); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(i != 1, "expected value 1 for first row"); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc != MYSQL_NO_DATA, "Eof expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc67"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug1115(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, rowcount; + MYSQL_BIND my_bind[1]; + ulong length[1]; + char szData[11]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + + 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)"); + check_mysql_rc(rc, mysql); + 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) "); + check_mysql_rc(rc, mysql); + 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)"); + check_mysql_rc(rc, mysql); + + strcpy(query, "SELECT * FROM test_select WHERE " + "CONVERT(session_id USING utf8)= ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "Paramcount != 1"); + + memset(my_bind, '\0', sizeof(MYSQL_BIND)); + + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount=%d != 1"); + + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 0, "rowcount != 0"); + + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount != 1"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + + return OK; +} +/* Test BUG#1180 (optimized away part of WHERE clause) */ + +static int test_bug1180(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, rowcount; + MYSQL_BIND my_bind[1]; + ulong length[1]; + char szData[11]; + char query[MAX_TEST_QUERY_LENGTH]; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_select(session_id char(9) NOT NULL)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO test_select VALUES (\"abc\")"); + check_mysql_rc(rc, mysql); + + strcpy(query, "SELECT * FROM test_select WHERE ?= \"1111\" and " + "session_id= \"abc\""); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "Paramcount != 1"); + + memset(my_bind, '\0', sizeof(MYSQL_BIND)); + + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 0, "rowcount != 0"); + + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 1, "rowcount != 1"); + + strcpy(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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rowcount= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 0, "rowcount != 0"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_select"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* + Test BUG#1644 (Insertion of more than 3 NULL columns with parameter + binding fails) +*/ + +static int test_bug1644(MYSQL *mysql) +{ + 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]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS foo_dfr"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, + "CREATE TABLE foo_dfr(col1 int, col2 int, col3 int, col4 int);"); + check_mysql_rc(rc, mysql); + + strcpy(query, "INSERT INTO foo_dfr VALUES (?, ?, ?, ? )"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 4, "Paramcount != 4"); + + memset(my_bind, '\0', sizeof(MYSQL_BIND) * 4); + + 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + isnull= 1; + for (i= 0 ; i < 4 ; i++) + my_bind[i].is_null= &isnull; + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "SELECT * FROM foo_dfr"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid resultset"); + + FAIL_IF(mysql_num_rows(result) != 3, "rowcount != 3"); + + mysql_data_seek(result, 0); + + row= mysql_fetch_row(result); + FAIL_IF(!row, "row = NULL"); + for (i= 0 ; i < 4 ; i++) + { + FAIL_UNLESS(strcmp(row[i], "22") == 0, "Wrong value"); + } + row= mysql_fetch_row(result); + FAIL_IF(!row, "Invalid row"); + for (i= 0 ; i < 4 ; i++) + { + FAIL_UNLESS(row[i] == 0, "row[i] != 0"); + } + row= mysql_fetch_row(result); + FAIL_IF(!row, "Invalid row"); + for (i= 0 ; i < 4 ; i++) + { + FAIL_UNLESS(strcmp(row[i], "88") == 0, "row[i] != 88"); + } + row= mysql_fetch_row(result); + FAIL_IF(row, "row != NULL"); + + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS foo_dfr"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug11037(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *stmt_text; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (id int not null)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values (1)"); + check_mysql_rc(rc, mysql); + + stmt_text= "select id FROM t1"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + + /* expected error */ + rc = mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc==1, "Error expected"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc==MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc==MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* Bug#11183 "mysql_stmt_reset() doesn't reset information about error" */ + +static int test_bug11183(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + char bug_statement[]= "insert into t1 values (1)"; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (a int)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(bug_statement)); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + /* Trying to execute statement that should fail on execute stage */ + rc= mysql_stmt_execute(stmt); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_reset(stmt); + FAIL_IF(mysql_stmt_errno(stmt) != 0, "stmt->error != 0"); + + rc= mysql_query(mysql, "create table t1 (a int)"); + check_mysql_rc(rc, mysql); + + /* Trying to execute statement that should pass ok */ + if (mysql_stmt_execute(stmt)) + { + mysql_stmt_reset(stmt); + FAIL_IF(mysql_stmt_errno(stmt) == 0, "stmt->error != 0"); + } + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug12744(MYSQL *mysql) +{ + MYSQL_STMT *stmt = NULL; + int rc; + + SKIP_MAXSCALE; + + stmt = mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, "SET @a:=1", 9); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + /* set reconnect, kill and ping to reconnect */ + rc= mysql_query(mysql, "SET @a:=1"); + check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_OPT_RECONNECT, "1"); + check_mysql_rc(rc, mysql); + rc= mysql_kill(mysql, mysql_thread_id(mysql)); + + rc= mysql_ping(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + return OK; +} + +static int test_bug1500(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[3]; + int rc= 0; + int32 int_data[3]= {2, 3, 4}; + const char *data; + const char *query; + + // XPAND doesn't support AGAINST + SKIP_XPAND + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bg1500"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_bg1500 (i INT)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_bg1500 VALUES (1), (2)"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + query= "SELECT i FROM test_bg1500 WHERE i IN (?, ?, ?)"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 3, "paramcount != 3"); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE test_bg1500"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_bg1500 (s VARCHAR(25), FULLTEXT(s)) engine=MyISAM"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, + "INSERT INTO test_bg1500 VALUES ('Gravedigger'), ('Greed'), ('Hollow Dogs')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + query= "SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (?)"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "paramcount != 1"); + + data= "Dogs"; + my_bind[0].buffer_type= MYSQL_TYPE_STRING; + my_bind[0].buffer= (void *) data; + my_bind[0].buffer_length= (unsigned long)strlen(data); + my_bind[0].is_null= 0; + my_bind[0].length= 0; + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + mysql_stmt_close(stmt); + + /* This should work too */ + query= "SELECT s FROM test_bg1500 WHERE MATCH (s) AGAINST (CONCAT(?, 'digger'))"; + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "paramcount != 1"); + + data= "Grave"; + my_bind[0].buffer_type= MYSQL_TYPE_STRING; + my_bind[0].buffer= (void *) data; + my_bind[0].buffer_length= (unsigned long)strlen(data); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bg1500"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug15510(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *query= "select 1 from dual where 1/0"; + + SKIP_MYSQL(mysql); + + rc= mysql_query(mysql, "set @@sql_mode='ERROR_FOR_DIVISION_BY_ZERO'"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(mysql_warning_count(mysql), "Warning expected"); + + /* Cleanup */ + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "set @@sql_mode=''"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + Bug #15518 - Reusing a stmt that has failed during prepare + does not clear error +*/ + +static int test_bug15518(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + + stmt= mysql_stmt_init(mysql); + + /* + The prepare of foo should fail with errno 1064 since + it's not a valid query + */ + rc= mysql_stmt_prepare(stmt, "foo", 3); + FAIL_UNLESS(rc && mysql_stmt_errno(stmt) && mysql_errno(mysql), "Error expected"); + + /* + Use the same stmt and reprepare with another query that + succeeds + */ + rc= mysql_stmt_prepare(stmt, "SHOW STATUS", 12); + FAIL_UNLESS(!rc || mysql_stmt_errno(stmt) || mysql_errno(mysql), "Error not expected"); + + rc= mysql_stmt_close(stmt); + check_mysql_rc(rc, mysql); + /* + part2, when connection to server has been closed + after first prepare + */ + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, "foo", 3); + FAIL_UNLESS(rc && mysql_stmt_errno(stmt) && mysql_errno(mysql), "Error expected"); + + /* Close connection to server */ + mysql_close(mysql); + + /* + Use the same stmt and reprepare with another query that + succeeds. The prepare should fail with error 2013 since + connection to server has been closed. + */ + rc= mysql_stmt_prepare(stmt, "SHOW STATUS", 12); + FAIL_UNLESS(rc && mysql_stmt_errno(stmt), "Error expected"); + + mysql_stmt_close(stmt); + + return OK; +} + +/* + Bug #15613: "libmysqlclient API function mysql_stmt_prepare returns wrong + field length" +*/ + +static int test_bug15613(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmt_text; + MYSQL_RES *metadata; + MYSQL_FIELD *field; + int rc; + + //https://jira.mariadb.org/browse/XPT-273 + SKIP_XPAND; + + /* I. Prepare the table */ + rc= mysql_query(mysql, "set names latin1"); + check_mysql_rc(rc, mysql); + 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)"); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + metadata= mysql_stmt_result_metadata(stmt); + field= mysql_fetch_fields(metadata); + FAIL_UNLESS(field[0].length == 65535, "length != 65535"); + FAIL_UNLESS(field[1].length == 255, "length != 255"); + FAIL_UNLESS(field[2].length == 16777215, "length != 166777215"); + FAIL_UNLESS(field[3].length == 4294967295UL, "length != 4294967295UL"); + FAIL_UNLESS(field[4].length == 255, "length != 255"); + FAIL_UNLESS(field[5].length == 255, "length != 255"); + FAIL_UNLESS(field[6].length == 255, "length != 255"); + mysql_free_result(metadata); + mysql_stmt_free_result(stmt); + + /* III. Cleanup */ + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "set names default"); + check_mysql_rc(rc, mysql); + mysql_stmt_close(stmt); + + return OK; +} + +static int test_bug16144(MYSQL *mysql) +{ + const my_bool flag_orig= (my_bool) 0xde; + my_bool flag= flag_orig; + MYSQL_STMT *stmt; + + /* 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); + FAIL_UNLESS(flag == flag_orig, "flag != flag_orig"); + + mysql_stmt_close(stmt); + + return OK; +} + +/* + This tests for various mysql_stmt_send_long_data bugs described in #1664 +*/ + +static int test_bug1664(MYSQL *mysql) +{ + 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(?, ?)"; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_long_data(col1 int, col2 MEDIUMTEXT)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "Param count != 2"); + + memset(my_bind, '\0', sizeof(my_bind)); + + my_bind[0].buffer_type= MYSQL_TYPE_STRING; + my_bind[0].buffer= (void *)str_data; + my_bind[0].buffer_length= (unsigned long)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_stmt_rc(rc, stmt); + + 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, SL(data)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + if (verify_col_data(mysql, "test_long_data", "col1", "1")) + goto error; + if (verify_col_data(mysql, "test_long_data", "col2", "")) + goto error; + rc= mysql_query(mysql, "DELETE FROM test_long_data"); + check_mysql_rc(rc, mysql); + + /* This should pass OK */ + data= (char *)"Data"; + rc= mysql_stmt_send_long_data(stmt, 0, SL(data)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + if (verify_col_data(mysql, "test_long_data", "col1", "1")) + goto error; + if (verify_col_data(mysql, "test_long_data", "col2", "Data")) + goto error; + + /* clean up */ + rc= mysql_query(mysql, "DELETE FROM test_long_data"); + check_mysql_rc(rc, mysql); + + /* + 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_stmt_rc(rc, stmt); + + if (verify_col_data(mysql, "test_long_data", "col1", "2")) + goto error; + if (verify_col_data(mysql, "test_long_data", "col2", str_data)) + goto error; + + /* clean up */ + rc= mysql_query(mysql, "DELETE FROM test_long_data"); + check_mysql_rc(rc, mysql); + + /* + Now we are sending other long data. It should not be + concatenated to previous. + */ + + data= (char *)"SomeOtherData"; + rc= mysql_stmt_send_long_data(stmt, 0, SL(data)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + if (verify_col_data(mysql, "test_long_data", "col1", "2")) + goto error; + if (verify_col_data(mysql, "test_long_data", "col2", "SomeOtherData")) + goto error; + + mysql_stmt_close(stmt); + + /* clean up */ + rc= mysql_query(mysql, "DELETE FROM test_long_data"); + check_mysql_rc(rc, mysql); + + /* Now let us test how mysql_stmt_reset works. */ + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + data= (char *)"SomeData"; + rc= mysql_stmt_send_long_data(stmt, 0, SL(data)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + if (verify_col_data(mysql, "test_long_data", "col1", "2")) + goto error; + if (verify_col_data(mysql, "test_long_data", "col2", str_data)) + goto error; + + mysql_stmt_close(stmt); + + /* Final clean up */ + rc= mysql_query(mysql, "DROP TABLE test_long_data"); + check_mysql_rc(rc, mysql); + + return OK; + +error: + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE test_long_data"); + return FAIL; +} +/* Test a misc bug */ + +static int test_ushort_bug(MYSQL *mysql) +{ + 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; + const char *query= "SELECT * FROM test_ushort"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ushort"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_ushort(a smallint unsigned, \ + b smallint unsigned, \ + c smallint unsigned, \ + d smallint unsigned)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, + "INSERT INTO test_ushort VALUES(35999, 35999, 35999, 200)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(short_value == 35999, "short_value != 35999"); + FAIL_UNLESS(s_length == 2, "length != 2"); + + FAIL_UNLESS(long_value == 35999, "long_value != 35999"); + FAIL_UNLESS(l_length == 4, "length != 4"); + + FAIL_UNLESS(longlong_value == 35999, "longlong_value != 35999"); + FAIL_UNLESS(ll_length == 8, "length != 8"); + + FAIL_UNLESS(tiny_value == 200, "tiny_value != 200"); + FAIL_UNLESS(t_length == 1, "length != 1"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ushort"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug1946(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *query= "INSERT INTO prepare_command VALUES (?)"; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS prepare_command"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE prepare_command(ID INT)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_real_query(mysql, SL(query)); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE prepare_command"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug20152(MYSQL *mysql) +{ + MYSQL_BIND my_bind[1]; + MYSQL_STMT *stmt; + MYSQL_TIME tm; + int rc; + const char *query= "INSERT INTO t1 (f1) VALUES (?)"; + + + 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(MYSQL_TIME)); + + 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"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (f1 DATE)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + FAIL_UNLESS(tm.hour == 14 && tm.minute == 9 && tm.second == 42, "time != 14:09:42"); + return OK; +} + +static int test_bug2247(MYSQL *mysql) +{ + 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 }; + + + /* create table and insert few rows */ + rc= mysql_query(mysql, drop); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, create); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(insert)); + check_stmt_rc(rc, stmt); + for (i= 0; i < NUM_ROWS; ++i) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + exp_count= mysql_stmt_affected_rows(stmt); + FAIL_UNLESS(exp_count == 1, "exp_count != 1"); + + rc= mysql_query(mysql, SELECT); + check_mysql_rc(rc, mysql); + /* + 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); + FAIL_IF(!res, "Invalid result set"); + + FAIL_UNLESS(mysql_affected_rows(mysql) == NUM_ROWS, "affected_rows != NUM_ROWS"); + FAIL_UNLESS(exp_count == mysql_stmt_affected_rows(stmt), "affected_rows != exp_count"); + + rc= mysql_query(mysql, update); + check_mysql_rc(rc, mysql); + FAIL_UNLESS(mysql_affected_rows(mysql) == NUM_ROWS, "affected_rows != NUM_ROWS"); + FAIL_UNLESS(exp_count == mysql_stmt_affected_rows(stmt), "affected_rows != exp_count"); + + mysql_free_result(res); + mysql_stmt_close(stmt); + + /* check that mysql_stmt_store_result modifies mysql_stmt_affected_rows */ + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(SELECT)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); exp_count= mysql_stmt_affected_rows(stmt); + FAIL_UNLESS(exp_count == NUM_ROWS, "exp_count != NUM_ROWS"); + + rc= mysql_query(mysql, insert); + check_mysql_rc(rc, mysql); + FAIL_UNLESS(mysql_affected_rows(mysql) == 1, "affected_rows != 1"); + FAIL_UNLESS(exp_count == mysql_stmt_affected_rows(stmt), "affected_rows != exp_count"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, drop); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + Test for bug#2248 "mysql_fetch without prior mysql_stmt_execute hangs" +*/ + +static int test_bug2248(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *query1= "SELECT DATABASE()"; + const char *query2= "INSERT INTO test_bug2248 VALUES (10)"; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_bug2248"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_bug2248 (id int)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query1)); + check_stmt_rc(rc, stmt); + + /* This should not hang */ + rc= mysql_stmt_fetch(stmt); + FAIL_IF(!rc, "Error expected"); + + /* And this too */ + rc= mysql_stmt_store_result(stmt); + FAIL_IF(!rc, "Error expected"); + + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query2)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + /* This too should not hang but should return proper error */ + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == 1, "rc != 1"); + + /* This too should not hang but should not bark */ + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + /* This should return proper error */ + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == 1, "rc != 1"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE test_bug2248"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + 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 int test_bug23383(MYSQL *mysql) +{ + 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; + unsigned long long row_count; + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (i INT UNIQUE)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, insert_query); + check_mysql_rc(rc, mysql); + row_count= mysql_affected_rows(mysql); + FAIL_UNLESS(row_count == 2, "row_count != 2"); + + rc= mysql_query(mysql, insert_query); + FAIL_IF(!rc, "Error expected"); + row_count= mysql_affected_rows(mysql); + FAIL_UNLESS(row_count == (unsigned long long)-1, "rowcount != -1"); + + rc= mysql_query(mysql, update_query); + check_mysql_rc(rc, mysql); + row_count= mysql_affected_rows(mysql); + FAIL_UNLESS(row_count == 0, ""); + + rc= mysql_query(mysql, "DELETE FROM t1"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(insert_query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + row_count= mysql_stmt_affected_rows(stmt); + FAIL_UNLESS(row_count == 2, "row_count != 2"); + + rc= mysql_stmt_execute(stmt); + FAIL_UNLESS(rc != 0, ""); + row_count= mysql_stmt_affected_rows(stmt); + FAIL_UNLESS(row_count == (unsigned long long)-1, "rowcount != -1"); + + rc= mysql_stmt_prepare(stmt, SL(update_query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + row_count= mysql_stmt_affected_rows(stmt); + FAIL_UNLESS(row_count == 0, "rowcount != 0"); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/* + Bug#27592 (stack overrun when storing datetime value using prepared statements) +*/ + +static int test_bug27592(MYSQL *mysql) +{ + const int NUM_ITERATIONS= 40; + int i; + int rc; + MYSQL_STMT *stmt= NULL; + MYSQL_BIND bind[1]; + MYSQL_TIME time_val; + + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "CREATE TABLE t1(c2 DATETIME)"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO t1 VALUES (?)")); + check_stmt_rc(rc, 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_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + + mysql_stmt_close(stmt); + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + + return OK; +} + +/* + Bug#28934: server crash when receiving malformed com_execute packets +*/ + +static int test_bug28934(MYSQL *mysql) +{ + my_bool error= 0; + MYSQL_BIND bind[5]; + MYSQL_STMT *stmt; + int rc, cnt; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1(id int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values(1),(2),(3),(4),(5)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("select * from t1 where id in(?,?,?,?,?)")); + check_stmt_rc(rc, 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; + } + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + stmt->param_count=2; + error= mysql_stmt_execute(stmt); + FAIL_UNLESS(error != 0, "Error expected"); + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug3035(MYSQL *mysql) +{ + 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; + + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + memset(bind_array, '\0', 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_rc(rc, stmt); + + stmt_text= "INSERT INTO t1 (i8, ui8, i16, ui16, i32, ui32, i64, ui64) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + 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_stmt_rc(rc, stmt); + 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_stmt_rc(rc, stmt); + 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + 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_stmt_rc(rc, stmt); + FAIL_UNLESS(int8_val == int8_min, "int8_val != int8_min"); + FAIL_UNLESS(uint8_val == uint8_min, "uint8_val != uint8_min"); + FAIL_UNLESS(int16_val == int16_min, "int16_val != int16_min"); + FAIL_UNLESS(uint16_val == uint16_min, "uint16_val != uint16_min"); + FAIL_UNLESS(int32_val == int32_min, "int32_val != int32_min"); + FAIL_UNLESS(uint32_val == uint32_min, "uint32_val != uint32_min"); + FAIL_UNLESS(int64_val == int64_min, "int64_val != int64_min"); + FAIL_UNLESS(uint64_val == uint64_min, "uint64_val != uint64_min"); + FAIL_UNLESS(double_val == (longlong) uint64_min, "double_val != uint64_min"); + double_tmp= ulonglong2double(uint64_val); + FAIL_UNLESS(cmp_double(&udouble_val,&double_tmp), "udouble_val != double_tmp"); + FAIL_UNLESS(!strcmp(longlong_as_string, "0"), "longlong_as_string != '0'"); + FAIL_UNLESS(!strcmp(ulonglong_as_string, "0"), "ulonglong_as_string != '0'"); + + rc= mysql_stmt_fetch(stmt); + + FAIL_UNLESS(rc == MYSQL_DATA_TRUNCATED || rc == 0, "rc != 0,MYSQL_DATA_TRUNCATED"); + + FAIL_UNLESS(int8_val == int8_max, "int8_val != int8_max"); + FAIL_UNLESS(uint8_val == uint8_max, "uint8_val != uint8_max"); + FAIL_UNLESS(int16_val == int16_max, "int16_val != int16_max"); + FAIL_UNLESS(uint16_val == uint16_max, "uint16_val != uint16_max"); + FAIL_UNLESS(int32_val == int32_max, "int32_val != int32_max"); + FAIL_UNLESS(uint32_val == uint32_max, "uint32_val != uint32_max"); + FAIL_UNLESS(int64_val == int64_max, "int64_val != int64_max"); + FAIL_UNLESS(uint64_val == uint64_max, "uint64_val != uint64_max"); + FAIL_UNLESS(double_val == (longlong) uint64_val, "double_val != uint64_val"); + double_tmp= ulonglong2double(uint64_val); + FAIL_UNLESS(cmp_double(&udouble_val,&double_tmp), "udouble_val != double_tmp"); + FAIL_UNLESS(!strcmp(longlong_as_string, "-1"), "longlong_as_string != '-1'"); + FAIL_UNLESS(!strcmp(ulonglong_as_string, "18446744073709551615"), "ulonglong_as_string != '18446744073709551615'"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, ""); + + mysql_stmt_close(stmt); + + stmt_text= "DROP TABLE t1"; + mysql_real_query(mysql, SL(stmt_text)); + return OK; +} + +/* + Test for BUG#3420 ("select id1, value1 from t where id= ? or value= ?" + returns all rows in the table) +*/ + +static int test_ps_conj_select(MYSQL *mysql) +{ + 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]; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (id1 int(11) NOT NULL default '0', " + "value2 varchar(100), value1 varchar(100))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values (1, 'hh', 'hh'), " + "(2, 'hh', 'hh'), (1, 'ii', 'ii'), (2, 'ii', 'ii')"); + check_mysql_rc(rc, mysql); + + strcpy(query, "select id1, value1 from t1 where id1= ? or " + "CONVERT(value1 USING utf8)= ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "param_count != 2"); + + /* Always bzero all members of bind parameter */ + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + int_data= 1; + strcpy(str_data, "hh"); + str_length= (unsigned long)strlen(str_data); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc=0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 3, "rc != 3"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test for NULL as PS parameter (BUG#3367, BUG#3371) */ + +static int test_ps_null_param(MYSQL *mysql) +{ + 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; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ps_nulls"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_ps_nulls(col1 int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_ps_nulls values (1), (null)"); + check_mysql_rc(rc, mysql); + + /* Always bzero all members of bind parameter */ + memset(&in_bind, '\0', sizeof(in_bind)); + memset(&out_bind, '\0', 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]; + strcpy(query, *cur_query); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + diag("statement: %s", query); + check_stmt_rc(rc, stmt); + FAIL_IF(mysql_stmt_param_count(stmt) != 1, "param_count != 1"); + + rc= mysql_stmt_bind_param(stmt, &in_bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_bind_result(stmt, &out_bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc != MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + FAIL_UNLESS(out_is_null, "!out_is_null"); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + mysql_stmt_close(stmt); + } + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ps_nulls"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* + 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); \ + FAIL_UNLESS(r_metadata != NULL, ""); \ + rc= mysql_stmt_fetch(stmt); \ + check_stmt_rc(rc,stmt); \ + FAIL_UNLESS((r_int_data == i1) && (r_str_length == l1) && \ + (strcmp(r_str_data, s1) == 0), "test_ps_query_cache_result failure"); \ + rc= mysql_stmt_fetch(stmt); \ + check_stmt_rc(rc,stmt); \ + FAIL_UNLESS((r_int_data == i2) && (r_str_length == l2) && \ + (strcmp(r_str_data, s2) == 0), "test_ps_query_cache_result failure"); \ + rc= mysql_stmt_fetch(stmt); \ + check_stmt_rc(rc,stmt); \ + FAIL_UNLESS((r_int_data == i3) && (r_str_length == l3) && \ + (strcmp(r_str_data, s3) == 0), "test_ps_query_cache_result failure"); \ + rc= mysql_stmt_fetch(stmt); \ + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); \ + mysql_free_result(r_metadata); + +/* reads Qcache_hits from server and returns its value */ +static int query_cache_hits(MYSQL *mysql) +{ + MYSQL_RES *res; + MYSQL_ROW row; + int rc; + uint result; + + rc= mysql_query(mysql, "show status like 'qcache_hits'"); + check_mysql_rc(rc, mysql); + res= mysql_use_result(mysql); + + row= mysql_fetch_row(res); + + result= atoi(row[1]); + mysql_free_result(res); + return result; +} + + +/* + Test that prepared statements make use of the query cache just as normal + statements (BUG#735). +*/ +static int test_ps_query_cache(MYSQL *mysql) +{ + 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; + + diag("test needs to be fixed"); + return SKIP; + /* prepare the table */ + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1 (id1 int(11) NOT NULL default '0', " + "value2 varchar(100), value1 varchar(100))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into t1 values (1, 'hh', 'hh'), " + "(2, 'hh', 'hh'), (1, 'ii', 'ii'), (2, 'ii', 'ii')"); + check_mysql_rc(rc, mysql); + + 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"); + check_mysql_rc(rc, mysql); + break; + case TEST_QCACHE_OFF_ON: + rc= mysql_query(lmysql, "set global query_cache_size=0"); + check_mysql_rc(rc, mysql); + break; + case TEST_QCACHE_ON_WITH_OTHER_CONN: + lmysql= test_connect(NULL); + FAIL_IF(!lmysql, "Opening new connection failed"); + break; + } + + strcpy(query, "select id1, value1 from t1 where id1= ? or " + "CONVERT(value1 USING utf8)= ?"); + stmt= mysql_stmt_init(lmysql); + FAIL_IF(!stmt, mysql_error(lmysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 2, "param_count != 2"); + + switch (iteration) { + case TEST_QCACHE_OFF_ON: + rc= mysql_query(lmysql, "set global query_cache_size=1000000"); + check_mysql_rc(rc, mysql); + break; + case TEST_QCACHE_ON_OFF: + rc= mysql_query(lmysql, "set global query_cache_size=0"); + check_mysql_rc(rc, mysql); + default: + break; + } + + memset(p_bind, '\0', 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_stmt_rc(rc, stmt); + p_int_data= 1; + strcpy(p_str_data, "hh"); + p_str_length= (unsigned long)strlen(p_str_data); + + memset(r_bind, '\0', 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_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + test_ps_query_cache_result(1, "hh", 2, 2, "hh", 2, 1, "ii", 2); + r_metadata= mysql_stmt_result_metadata(stmt); + FAIL_UNLESS(r_metadata != NULL, ""); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + FAIL_UNLESS((r_int_data == 1) && (r_str_length == 2) && + (strcmp(r_str_data, "hh") == 0), "test_ps_query_cache_result failure"); \ + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + FAIL_UNLESS((r_int_data == 2) && (r_str_length == 2) && + (strcmp(r_str_data, "hh") == 0), "test_ps_query_cache_result failure"); \ + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc,stmt); + FAIL_UNLESS((r_int_data == 1) && (r_str_length == 2) && + (strcmp(r_str_data, "ii") == 0), "test_ps_query_cache_result failure"); \ + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + mysql_free_result(r_metadata); + + + /* now retry with the same parameter values and see qcache hits */ + hits1= query_cache_hits(lmysql); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); 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 */ + FAIL_UNLESS(hits2-hits1 == 1, "hits2 != hits1 + 1"); + break; + case TEST_QCACHE_OFF_ON: + case TEST_QCACHE_ON_OFF: /* should not have hit */ + FAIL_UNLESS(hits2-hits1 == 0, "hits2 != hits1"); + break; + } + + /* now modify parameter values and see qcache hits */ + strcpy(p_str_data, "ii"); + p_str_length= (unsigned long)strlen(p_str_data); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + 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 */ + FAIL_UNLESS(hits2-hits1 == 0, "hits2 != hits1"); + break; + case TEST_QCACHE_ON_WITH_OTHER_CONN: /* should have hit */ + FAIL_UNLESS(hits1-hits2 == 1, "hits2 != hits1+1"); + break; + } + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + 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 */ + FAIL_UNLESS(hits2-hits1 == 1, "hits2 != hits1+1"); + break; + case TEST_QCACHE_OFF_ON: + case TEST_QCACHE_ON_OFF: /* should not have hit */ + FAIL_UNLESS(hits2-hits1 == 0, "hits2 != hits1"); + break; + case TEST_QCACHE_ON_WITH_OTHER_CONN: /* should have hit */ + FAIL_UNLESS(hits2-hits1 == 1, "hits2 != hits1+1"); + break; + } + + } /* for(iteration=...) */ + + if (lmysql != mysql) + mysql_close(lmysql); + + rc= mysql_query(mysql, "set global query_cache_size=0"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug3117(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND buffer; + longlong lii; + ulong length; + my_bool is_null; + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (id int auto_increment primary key)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT LAST_INSERT_ID()")); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (NULL)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + memset(&buffer, '\0', 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_stmt_rc(rc, stmt); + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(is_null == 0 && lii == 1, "is_null != 0 || lii != 1"); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (NULL)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(is_null == 0 && lii == 2, "is_null != 0 || lii != 2"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/** + Bug#36004 mysql_stmt_prepare resets the list of warnings +*/ + +static int test_bug36004(MYSQL *mysql) +{ + int rc, warning_count= 0; + MYSQL_STMT *stmt; + SKIP_MAXSCALE; + SKIP_MYSQL(mysql); // don't send expected warnings + + if (mysql_get_server_version(mysql) < 60000) { + diag("Test requires MySQL Server version 6.0 or above"); + return SKIP; + } + + rc= mysql_query(mysql, "drop table if exists inexistant"); + check_mysql_rc(rc, mysql); + + FAIL_UNLESS(mysql_warning_count(mysql) == 1, ""); + query_int_variable(mysql, "@@warning_count", &warning_count); + FAIL_UNLESS(warning_count, "Warning expected"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("select 1")); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(mysql_warning_count(mysql) == 0, "No warning expected"); + query_int_variable(mysql, "@@warning_count", &warning_count); + FAIL_UNLESS(warning_count, "warning expected"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(mysql_warning_count(mysql) == 0, "No warning expected"); + mysql_stmt_close(stmt); + + query_int_variable(mysql, "@@warning_count", &warning_count); + FAIL_UNLESS(warning_count, "Warning expected"); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("drop table if exists inexistant")); + check_stmt_rc(rc, stmt); + + query_int_variable(mysql, "@@warning_count", &warning_count); + FAIL_UNLESS(warning_count == 0, "No warning expected"); + mysql_stmt_close(stmt); + + return OK; +} + +static int test_bug3796(MYSQL *mysql) +{ + 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; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + /* Create and fill test table */ + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "CREATE TABLE t1 (a INT, b VARCHAR(30))"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "INSERT INTO t1 VALUES(1, 'ONE'), (2, 'TWO')"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + /* 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + /* Bind input buffers */ + memset(my_bind, '\0', sizeof(my_bind)); + my_bind[0].buffer_type= MYSQL_TYPE_STRING; + my_bind[0].buffer= (void *) concat_arg0; + my_bind[0].buffer_length= (unsigned long)strlen(concat_arg0); + + mysql_stmt_bind_param(stmt, my_bind); + + /* Execute the select statement */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + 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); + check_stmt_rc(rc, stmt); + strcpy(canonical_buff, concat_arg0); + strcat(canonical_buff, "ONE"); + FAIL_UNLESS(strlen(canonical_buff) == out_length && + strncmp(out_buff, canonical_buff, out_length) == 0, ""); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + strcpy(canonical_buff + strlen(concat_arg0), "TWO"); + FAIL_UNLESS(strlen(canonical_buff) == out_length && + strncmp(out_buff, canonical_buff, out_length) == 0, ""); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug4026(MYSQL *mysql) +{ + 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; + + + /* 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + /* Bind input buffers */ + memset(my_bind, '\0', sizeof(MYSQL_BIND) * 2); + memset(&time_in, '\0', sizeof(MYSQL_TIME)); + memset(&time_out, '\0', sizeof(MYSQL_TIME)); + memset(&datetime_in, '\0', sizeof(MYSQL_TIME)); + memset(&datetime_out, '\0', sizeof(MYSQL_TIME)); + 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_stmt_rc(rc, stmt); + 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); + FAIL_UNLESS(rc == 0, "rc != 0"); + FAIL_UNLESS(memcmp(&time_in, &time_out, sizeof(time_in)) == 0, "time_in != time_out"); + FAIL_UNLESS(memcmp(&datetime_in, &datetime_out, sizeof(datetime_in)) == 0, "datetime_in != datetime_out"); + mysql_stmt_close(stmt); + + return OK; +} + +static int test_bug4030(MYSQL *mysql) +{ + 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; + + + /* 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + /* Bind output buffers */ + memset(my_bind, '\0', sizeof(my_bind)); + memset(&time_canonical, '\0', sizeof(time_canonical)); + memset(&time_out, '\0', sizeof(time_out)); + memset(&date_canonical, '\0', sizeof(date_canonical)); + memset(&date_out, '\0', sizeof(date_out)); + memset(&datetime_canonical, '\0', sizeof(datetime_canonical)); + memset(&datetime_out, '\0', 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); + FAIL_UNLESS(rc == 0, "rc != 0"); + FAIL_UNLESS(memcmp(&time_canonical, &time_out, sizeof(time_out)) == 0, "time_canonical != time_out"); + FAIL_UNLESS(memcmp(&date_canonical, &date_out, sizeof(date_out)) == 0, "date_canoncical != date_out"); + FAIL_UNLESS(memcmp(&datetime_canonical, &datetime_out, sizeof(datetime_out)) == 0, "datetime_canonical != datetime_out"); + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug4079(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + const char *stmt_text; + uint32 res; + int rc; + + /* 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + /* Execute the select statement */ + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + /* Bind input buffers */ + memset(my_bind, '\0', 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); + FAIL_UNLESS(rc == 1, "rc != 1"); + /* buggy version of libmysql hanged up here */ + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug4172(MYSQL *mysql) +{ + 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; + + 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + + diag("expected %s %s %s", row[0], row[1], row[2]); + diag("fetched %s %s %s", f, d, e); + FAIL_UNLESS(!strcmp(f, row[0]) && !strcmp(d, row[1]) && !strcmp(e, row[2]), ""); + + mysql_free_result(res); + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug4231(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + MYSQL_TIME tm[2]; + const char *stmt_text; + int rc; + + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "CREATE TABLE t1 (a int)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "INSERT INTO t1 VALUES (1)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + stmt_text= "SELECT a FROM t1 WHERE ? = ?"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + /* Bind input buffers */ + memset(my_bind, '\0', sizeof(my_bind)); memset(tm, '\0', 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_stmt_rc(rc, stmt); + /* + 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_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + + /* binds are unequal, no rows should be returned */ + FAIL_UNLESS(rc == MYSQL_NO_DATA, "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); + FAIL_UNLESS(rc == 0, "rc != 0"); + + mysql_stmt_close(stmt); + stmt_text= "DROP TABLE t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug4236(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + MYSQL_STMT backup; + + 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); /* 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); + FAIL_IF(!rc, "Error expected"); + + /* Restore original statement id to be able to reprepare it */ + stmt->stmt_id= backup.stmt_id; + + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug5126(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[2]; + int32 c1, c2; + const char *stmt_text; + int rc; + + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "CREATE TABLE t1 (a mediumint, b int)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "INSERT INTO t1 VALUES (8386608, 1)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + stmt_text= "SELECT a, b FROM t1"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + /* Bind output buffers */ + memset(my_bind, '\0', 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); + FAIL_UNLESS(rc == 0, "rc != 0"); + FAIL_UNLESS(c1 == 8386608 && c2 == 1, "c1 != 8386608 || c2 != 1"); + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug5194(MYSQL *mysql) +{ + 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; + + SKIP_MAXSCALE; + + stmt_text= "drop table if exists t1"; + rc= mysql_real_query(mysql, SL(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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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); + + FAIL_IF(my_bind == 0 || query == 0 || param_str == 0, "Not enough memory"); + + 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= (int)strlen(param_str); + + /* setup bind array */ + memset(my_bind, '\0', 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 + (unsigned long)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) /* expected error */ + break; + + check_stmt_rc(rc, stmt); + + /* bind the parameter array and execute the query */ + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_reset(stmt); + } + + free(param_str); + free(query); + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + free(my_bind); + stmt_text= "drop table t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug5315(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + stmt_text= "SELECT 1"; + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_change_user(mysql, username, password, schema); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_execute(stmt); + FAIL_UNLESS(rc != 0, "Error expected"); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug5399(MYSQL *mysql) +{ + /* + 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; + + + memset(my_bind, '\0', 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, SL(buff)); + check_stmt_rc(rc, *stmt); mysql_stmt_bind_result(*stmt, my_bind); + } + + for (stmt= stmt_list; stmt != stmt_list + NUM_OF_USED_STMT; ++stmt) + { + rc= mysql_stmt_execute(*stmt); + check_stmt_rc(rc, *stmt); + rc= mysql_stmt_store_result(*stmt); + check_stmt_rc(rc, *stmt); + rc= mysql_stmt_fetch(*stmt); + FAIL_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 + return OK; +} + +static int test_bug6046(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + short b= 1; + MYSQL_BIND my_bind[1]; + + + stmt_text= "DROP TABLE IF EXISTS t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "CREATE TABLE t1 (a int, b int)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "INSERT INTO t1 VALUES (1,1),(2,2),(3,1),(4,2)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_stmt_rc(rc, stmt); + b= 1; + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + mysql_stmt_store_result(stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug6049(MYSQL *mysql) +{ + 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; + + + stmt_text= "SELECT MAKETIME(-25, 12, 12)"; + + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + memset(my_bind, '\0', 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); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(strcmp(row[0], (char*) buffer) == 0, "row[0] != buffer"); + + mysql_free_result(res); + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug6058(MYSQL *mysql) +{ + 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; + + + stmt_text= "SELECT CAST('0000-00-00' AS DATE)"; + + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + memset(my_bind, '\0', 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); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(strcmp(row[0], buffer) == 0, "row[0] != buffer"); + + mysql_free_result(res); + mysql_stmt_close(stmt); + return OK; +} + + +static int test_bug6059(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + + SKIP_SKYSQL; + + stmt_text= "SELECT 'foo' INTO OUTFILE 'x.3'"; + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(mysql_stmt_field_count(stmt) == 0, ""); + mysql_stmt_close(stmt); + return OK; +} + +static int test_bug6096(MYSQL *mysql) +{ + 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; + + + stmt_text= "drop table if exists t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt_text= "select * from t1"; + + /* Run select in prepared and non-prepared mode and compare metadata */ + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + query_result= mysql_store_result(mysql); + query_field_list= mysql_fetch_fields(query_result); + FAIL_IF(!query_field_list, "fetch_fields failed"); + query_field_count= mysql_num_fields(query_result); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); 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); + FAIL_UNLESS(stmt_field_count == query_field_count, ""); + + + /* Bind and fetch the data */ + + memset(my_bind, '\0', 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); + diag("rc=%d", rc); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Bug#7990 - mysql_stmt_close doesn't reset mysql->net.last_error */ + +static int test_bug7990(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + + 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 + */ + FAIL_UNLESS(rc && mysql_stmt_errno(stmt) && mysql_errno(mysql), "Error expected"); + mysql_stmt_close(stmt); + return OK; +} + +/* Bug#8330 - mysql_stmt_execute crashes (libmysql) */ + +static int test_bug8330(MYSQL *mysql) +{ + 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]= {1,2}; + + stmt_text= "drop table if exists t1"; + /* in case some previous test failed */ + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "create table t1 (a int, b int)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + memset(my_bind, '\0', sizeof(my_bind)); + for (i=0; i < 2; i++) + { + stmt[i]= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt[i], SL(query)); + check_stmt_rc(rc, stmt[i]); + my_bind[i].buffer_type= MYSQL_TYPE_LONG; + 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_stmt_rc(rc, stmt[0]); + rc= mysql_stmt_execute(stmt[1]); + FAIL_UNLESS(rc && mysql_stmt_errno(stmt[1]) == CR_COMMANDS_OUT_OF_SYNC, "Error expected"); + rc= mysql_stmt_execute(stmt[0]); + check_stmt_rc(rc, stmt[0]); + mysql_stmt_close(stmt[0]); + mysql_stmt_close(stmt[1]); + + stmt_text= "drop table t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test misc field information, bug: #74 */ + +static int test_field_misc(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_RES *result; + int rc; + + + rc= mysql_query(mysql, "SELECT @@autocommit"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 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_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT @@autocommit")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 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); + + // XPAND doesn't support @@max_error_count + SKIP_XPAND + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT @@max_error_count")); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, "Invalid result set"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + if (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)) + goto error; + + mysql_free_result(result); + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT @@max_allowed_packet")); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, "Invalid result set"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + if (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)) + goto error; + + mysql_free_result(result); + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT @@sql_warnings")); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, "Invalid result set"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + if (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 */ + goto error; + + mysql_free_result(result); + mysql_stmt_close(stmt); + return OK; + +error: + mysql_free_result(result); + mysql_stmt_close(stmt); + return FAIL; +} + +/* Test a memory overrun bug */ + +static int test_mem_overun(MYSQL *mysql) +{ + char buffer[10000], field[12]; + MYSQL_STMT *stmt; + MYSQL_RES *field_res, *res; + int rc, i, length; + + /* + Test a memory overrun bug when a table had 1000 fields with + a row of data + */ + rc= mysql_query(mysql, "drop table if exists t_mem_overun"); + check_mysql_rc(rc, mysql); + + strcpy(buffer, "create table t_mem_overun("); + for (i= 0; i < 1000; i++) + { + snprintf(field, sizeof(field), "c%d int, ", i); + strcat(buffer, field); + } + length= (int)strlen(buffer); + buffer[length-2]= ')'; + buffer[--length]= '\0'; + + rc= mysql_real_query(mysql, buffer, length); + check_mysql_rc(rc, mysql); + + strcpy(buffer, "insert into t_mem_overun values("); + for (i= 0; i < 1000; i++) + { + strcat(buffer, "1, "); + } + length= (int)strlen(buffer); + buffer[length-2]= ')'; + buffer[--length]= '\0'; + + rc= mysql_real_query(mysql, buffer, length); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "select * from t_mem_overun"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + rc= 0; + while (mysql_fetch_row(res)) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + mysql_free_result(res); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("select * from t_mem_overun")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + field_res= mysql_stmt_result_metadata(stmt); + FAIL_IF(!field_res, "Invalid result set"); + + FAIL_UNLESS( 1000 == mysql_num_fields(field_res), "fields != 1000"); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, ""); + + mysql_free_result(field_res); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table if exists t_mem_overun"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug8722(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *stmt_text; + + /* Prepare test data */ + stmt_text= "drop table if exists t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "drop view if exists v1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + 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, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "INSERT INTO t1 VALUES (1,2,3,4,5,6,7,8,9,10)"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + stmt_text= "CREATE VIEW v1 AS SELECT * FROM t1"; + rc= mysql_real_query(mysql, SL(stmt_text)); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + stmt_text= "select * from v1"; + rc= mysql_stmt_prepare(stmt, SL(stmt_text)); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + stmt_text= "drop table if exists t1, v1"; + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test DECIMAL conversion */ + +static int test_decimal_bug(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + char data[30]; + int rc; + my_bool is_null; + + mysql_autocommit(mysql, TRUE); + + rc= mysql_query(mysql, "drop table if exists test_decimal_bug"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table test_decimal_bug(c1 decimal(10, 2))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into test_decimal_bug value(8), (10.22), (5.61)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("select c1 from test_decimal_bug where c1=?")); + check_stmt_rc(rc, stmt); + + /* + We need to bzero bind structure because mysql_stmt_bind_param checks all + its members. + */ + memset(my_bind, '\0', sizeof(my_bind)); + + memset(data, 0, sizeof(data)); + 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_stmt_rc(rc, stmt); + + strcpy(data, "8.0"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + data[0]= 0; + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(strcmp(data, "8.00") == 0, "data != '8.00'"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + strcpy(data, "5.61"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + data[0]= 0; + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(strcmp(data, "5.61") == 0, "data != '5.61'"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + is_null= 1; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + strcpy(data, "10.22"); is_null= 0; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + data[0]= 0; + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(strcmp(data, "10.22") == 0, "data != '10.22'"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop table if exists test_decimal_bug"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test EXPLAIN bug (#115, reported by mark@mysql.com & georg@php.net). */ + +static int test_explain_bug(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_RES *result; + int rc; + + if (!is_mariadb) + return SKIP; + + mysql_autocommit(mysql, TRUE); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_explain"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_explain(id int, name char(2))"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("explain test_explain")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 2, "rowcount != 2"); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, "Invalid result set"); + + FAIL_UNLESS(6 == mysql_num_fields(result), "fields != 6"); + + if (verify_prepare_field(result, 0, "Field", "COLUMN_NAME", + mysql_get_server_version(mysql) <= 50000 ? + MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING, + 0, 0, + mysql_get_server_version(mysql) <= 50400 ? "" : "information_schema", + 64, 0)) + goto error; + + if (verify_prepare_field(result, 1, "Type", "COLUMN_TYPE", MYSQL_TYPE_BLOB, + 0, 0, + mysql_get_server_version(mysql) <= 50400 ? "" : "information_schema", + 0, 0)) + goto error; + + if (verify_prepare_field(result, 2, "Null", "IS_NULLABLE", + mysql_get_server_version(mysql) <= 50000 ? + MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING, + 0, 0, + mysql_get_server_version(mysql) <= 50400 ? "" : "information_schema", + 3, 0)) + goto error; + + if (verify_prepare_field(result, 3, "Key", "COLUMN_KEY", + mysql_get_server_version(mysql) <= 50000 ? + MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING, + 0, 0, + mysql_get_server_version(mysql) <= 50400 ? "" : "information_schema", + 3, 0)) + goto error; + + if ( mysql_get_server_version(mysql) >= 50027 ) + { + /* The patch for bug#23037 changes column type of DEAULT to blob */ + if (verify_prepare_field(result, 4, "Default", "COLUMN_DEFAULT", + MYSQL_TYPE_BLOB, 0, 0, + mysql_get_server_version(mysql) <= 50400 ? "" : "information_schema", + 0, 0)) + goto error; + } + else + { + if (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, + mysql_get_server_version(mysql) <= 50400 ? "" : "information_schema", + mysql_get_server_version(mysql) >= 50027 ? 0 :64, 0)) + goto error; + } + + if (verify_prepare_field(result, 5, "Extra", "EXTRA", + mysql_get_server_version(mysql) <= 50000 ? + MYSQL_TYPE_STRING : MYSQL_TYPE_VAR_STRING, + 0, 0, + mysql_get_server_version(mysql) <= 50400 ? "" : "information_schema", + 27, 0)) + goto error; + + mysql_free_result(result); + mysql_stmt_close(stmt); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("explain select id, name FROM test_explain")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (!mysql_stmt_fetch(stmt)) + rc++; + FAIL_UNLESS(rc == 1, "rowcount != 1"); + + result= mysql_stmt_result_metadata(stmt); + FAIL_IF(!result, "Invalid result set"); + + FAIL_UNLESS(10 == mysql_num_fields(result), "fields != 10"); + + if (verify_prepare_field(result, 0, "id", "", MYSQL_TYPE_LONGLONG, "", "", "", 3, 0)) + goto error; + + if (verify_prepare_field(result, 1, "select_type", "", MYSQL_TYPE_VAR_STRING, "", "", "", 19, 0)) + goto error; + + if (verify_prepare_field(result, 2, "table", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_CHAR_LEN, 0)) + goto error; + + if (verify_prepare_field(result, 3, "type", "", MYSQL_TYPE_VAR_STRING, "", "", "", 10, 0)) + goto error; + + if (verify_prepare_field(result, 4, "possible_keys", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_CHAR_LEN*MAX_KEY, 0)) + goto error; + + if ( verify_prepare_field(result, 5, "key", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_CHAR_LEN, 0)) + goto error; + + if (mysql_get_server_version(mysql) <= 50000) + { + if (verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_LONGLONG, "", "", "", 3, 0)) + goto error; + } + else if (mysql_get_server_version(mysql) <= 60000) + { + if (verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_VAR_STRING, "", "", "", NAME_CHAR_LEN*MAX_KEY, 0)) + goto error; + } + else + { + if (verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_VAR_STRING, "", "", "", (MAX_KEY_LENGTH_DECIMAL_WIDTH + 1) * MAX_KEY, 0)) + goto error; + } + + if (verify_prepare_field(result, 7, "ref", "", MYSQL_TYPE_VAR_STRING, "", "", "", + NAME_CHAR_LEN*16, 0)) + goto error; + + if (verify_prepare_field(result, 8, "rows", "", MYSQL_TYPE_LONGLONG, "", "", "", 10, 0)) + goto error; + + if (verify_prepare_field(result, 9, "Extra", "", MYSQL_TYPE_VAR_STRING, "", "", "", 255, 0)) + goto error; + + mysql_free_result(result); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_explain"); + check_mysql_rc(rc, mysql); + return OK; +error: + mysql_free_result(result); + mysql_stmt_close(stmt); + return FAIL; +} + +static int test_sshort_bug(MYSQL *mysql) +{ + 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; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_sshort"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_sshort(a smallint signed, \ + b smallint signed, \ + c smallint unsigned, \ + d smallint unsigned)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_sshort VALUES(-5999, -5999, 35999, 200)"); + check_mysql_rc(rc, mysql); + + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_sshort")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(short_value == -5999, "sv != -5999"); + FAIL_UNLESS(s_length == 2, "s_length != 2"); + + FAIL_UNLESS(long_value == -5999, "l_v != -5999"); + FAIL_UNLESS(l_length == 4, "l_length != 4"); + + FAIL_UNLESS(longlong_value == 35999, "llv != 35999"); + FAIL_UNLESS(ll_length == 8, "ll_length != 8"); + + FAIL_UNLESS(tiny_value == 200, "t_v != 200"); + FAIL_UNLESS(t_length == 1, "t_length != 1"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_sshort"); + check_mysql_rc(rc, mysql); + return OK; +} + + +/* Test a misc tinyint-signed conversion bug */ + +static int test_stiny_bug(MYSQL *mysql) +{ + 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; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_stiny"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_stiny(a tinyint signed, \ + b tinyint signed, \ + c tinyint unsigned, \ + d tinyint unsigned)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_stiny VALUES(-128, -127, 255, 0)"); + check_mysql_rc(rc, mysql); + + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM test_stiny")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(short_value == -128, "s_v != -128"); + FAIL_UNLESS(s_length == 2, "s_length != 2"); + + FAIL_UNLESS(long_value == -127, "l_v != -127"); + FAIL_UNLESS(l_length == 4, "l_length != 4"); + + FAIL_UNLESS(longlong_value == 255, "llv != 255"); + FAIL_UNLESS(ll_length == 8, "ll_length != 8"); + + FAIL_UNLESS(tiny_value == 0, "t_v != 0"); + FAIL_UNLESS(t_length == 1, "t_length != 1"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_stiny"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_bug53311(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + int i; + const char *query= "INSERT INTO bug53311 VALUES (1)"; + SKIP_MAXSCALE; + SKIP_XPAND; + + rc= mysql_options(mysql, MYSQL_OPT_RECONNECT, "1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bug53311"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE bug53311 (a int)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + for (i=0; i < 2; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + } + + /* kill connection */ + rc= mysql_kill(mysql, mysql_thread_id(mysql)); + + rc= mysql_stmt_execute(stmt); + FAIL_IF(rc == 0, "Error expected"); + FAIL_IF(mysql_stmt_errno(stmt) == 0, "Errno != 0 expected"); + rc= mysql_stmt_close(stmt); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS bug53311"); + check_mysql_rc(rc, mysql); + + return OK; +} +#define PREPARE_SQL "EXPLAIN SELECT t1.*, t2.* FROM test AS t1, test AS t2" + +#ifdef NOT_IN_USE +static int test_metadata(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE test(id INT, label CHAR(1), PRIMARY KEY(id)) ENGINE=MYISAM"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test(id, label) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')"); + check_mysql_rc(rc, mysql); + printf("Client=%s\n", mysql_get_client_info()); + printf("Server=%s\n", mysql_get_server_info(mysql)); + + { + MYSQL_STMT * stmt = mysql_stmt_init(mysql); + if (!stmt) { + fprintf(stderr, "Failed to init stmt: Error: %s\n", mysql_error(mysql)); + goto end; + } + if (mysql_stmt_prepare(stmt, PREPARE_SQL, sizeof(PREPARE_SQL) - 1)) { + fprintf(stderr, "Failed to prepare stmt: Error: %s\n", mysql_stmt_error(stmt)); + goto end2; + } + if (mysql_stmt_execute(stmt)) { + fprintf(stderr, "Failed to execute stmt: Error: %s\n", mysql_stmt_error(stmt)); + goto end2; + } + { + MYSQL_FIELD * field = NULL; + MYSQL_RES * res = mysql_stmt_result_metadata(stmt); + if (!res) { + fprintf(stderr, "Failed to get metadata: Error: %s\n", mysql_stmt_error(stmt)); + goto end2; + } + while ((field = mysql_fetch_field(res))) { + printf("name=%s\n", field->name); + printf("catalog=%s\n", field->catalog); + } + mysql_free_result(res); + + } +end2: + mysql_stmt_close(stmt); + } +end: + return 0; +} +#endif + +static int test_conc_5(MYSQL *mysql) +{ + const char *query= "SELECT a FROM t1"; + MYSQL_RES *res; + MYSQL_STMT *stmt; + MYSQL_FIELD *fields; + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, "couldn't allocate memory"); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + res= mysql_stmt_result_metadata(stmt); + FAIL_IF(!res, "Can't obtain resultset"); + + fields= mysql_fetch_fields(res); + FAIL_IF(!fields, "Can't obtain fields"); + + FAIL_IF(strcmp("def", fields[0].catalog), "unexpected value for field->catalog"); + + mysql_free_result(res); + mysql_stmt_close(stmt); + return OK; +} + +static int test_conc141(MYSQL *mysql) +{ + int rc; + const char *query= "CALL p_conc141"; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc141"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE conc141 (KeyVal int not null primary key)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO conc141 VALUES(1)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p_conc141"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE PROCEDURE p_conc141()\n" + "BEGIN\n" + "select * from conc141;\n" + "insert into conc141(KeyVal) VALUES(1);\n" + "END"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + /* skip first result */ + rc= mysql_stmt_next_result(stmt); + FAIL_IF(rc==-1, "No more results and error expected"); + mysql_stmt_free_result(stmt); + FAIL_IF(mysql_stmt_errno(stmt), "No Error expected"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc141"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p_conc141"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc154(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + const char *stmtstr= "SELECT * FROM t1"; + int rc; + + /* 1st: empty result set without free_result */ + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a varchar(20))"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmtstr)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + /* 2nd: empty result set with free_result */ + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmtstr)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + /* 3rd: non empty result without free_result */ + rc= mysql_query(mysql, "INSERT INTO t1 VALUES ('test_conc154')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmtstr)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + /* 4th non empty result set with free_result */ + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmtstr)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc155(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind; + char buffer[50]; + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a TEXT)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES ('zero terminated string')"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL("SELECT a FROM t1")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(buffer, 'X', 50); + memset(&bind, 0, sizeof(MYSQL_BIND)); + + bind.buffer= buffer; + bind.buffer_length= 50; + bind.buffer_type= MYSQL_TYPE_STRING; + + rc= mysql_stmt_bind_result(stmt, &bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + if (strlen(buffer) != strlen("zero terminated string")) + { + diag("Wrong buffer length"); + return FAIL; + } + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc168(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + + MYSQL_BIND bind; + char buffer[100]; + int rc; + + //https://jira.mariadb.org/browse/XPT-273 + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc168"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE conc168(a datetime(3))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO conc168 VALUES ('2016-03-09 07:51:49.000'),('2016-03-09 07:51:49.001'),('2016-03-09 07:51:49.010')"); + check_mysql_rc(rc, mysql); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + bind.buffer= buffer; + bind.buffer_type= MYSQL_TYPE_STRING; + bind.buffer_length= 100; + + rc= mysql_stmt_prepare(stmt, SL("SELECT a FROM conc168")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, &bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(strcmp(buffer, "2016-03-09 07:51:49.000"), "expected: 2016-03-09 07:51:49.000"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(strcmp(buffer, "2016-03-09 07:51:49.001"), "expected: 2016-03-09 07:51:49.001"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(strcmp(buffer, "2016-03-09 07:51:49.010"), "expected: 2016-03-09 07:51:49.010"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc168"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc167(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + + MYSQL_BIND bind[3]; + char buffer[100]; + int bit1=0, bit2=0; + int rc; + const char *stmt_str= "SELECT a,b,c FROM conc168"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS conc168"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE conc168(a bit, b bit, c varchar(10))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO conc168 VALUES (1,0, 'test12345')"); + check_mysql_rc(rc, mysql); + + memset(bind, 0, 3 * sizeof(MYSQL_BIND)); + bind[0].buffer= &bit1; + bind[0].buffer_type= MYSQL_TYPE_BIT; + bind[0].buffer_length= sizeof(int); + bind[1].buffer= &bit2; + bind[1].buffer_type= MYSQL_TYPE_BIT; + bind[1].buffer_length= sizeof(int); + bind[2].buffer= buffer; + bind[2].buffer_type= MYSQL_TYPE_STRING; + bind[2].buffer_length= 100; + + rc= mysql_stmt_prepare(stmt, SL(stmt_str)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + diag("bit=%d %d char=%s", bit1, bit2, buffer); + + mysql_stmt_close(stmt); + return OK; +} + +static int test_conc177(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + MYSQL_BIND bind[2]; + const char *stmt_str= "SELECT a,b FROM t1"; + char buf1[128], buf2[128]; + + // https://jira.mariadb.org/browse/XPT-286 + SKIP_XPAND + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a double zerofill default 8.8,b float zerofill default 8.8)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (DEFAULT, DEFAULT)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_str)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + bind[0].buffer= &buf1; + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer_length= 128; + bind[1].buffer= &buf2; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer_length= 128; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + mysql_stmt_close(stmt); + + diag("buf1 %s\nbuf2 %s", buf1, buf2); + + FAIL_IF(strcmp(buf1, "00000000000000000008.8"), "Expected 00000000000000000008.8"); + FAIL_IF(strcmp(buf2, "0000000008.8"), "Expected 0000000008.8"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int(8) zerofill default 1, b int(4) zerofill default 1)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (DEFAULT, DEFAULT)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_str)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + bind[0].buffer= &buf1; + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer_length= 128; + bind[1].buffer= &buf2; + bind[1].buffer_type= MYSQL_TYPE_STRING; + bind[1].buffer_length= 128; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + mysql_stmt_close(stmt); + + diag("buf1 %s\nbuf2 %s", buf1, buf2); + + FAIL_IF(strcmp(buf1, "00000001"), "Expected 00000001"); + FAIL_IF(strcmp(buf2, "0001"), "Expected 0001"); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc179(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *stmtstr= "select 1 as ' '"; + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmtstr)); + check_stmt_rc(rc, stmt); + + if (mysql_get_server_version(mysql) >= 100100) + { + FAIL_IF(mysql_warning_count(mysql) < 1, "expected 1 or more warnings"); + FAIL_IF(mysql_stmt_warning_count(stmt) < 1, "expected 1 or more warnings"); + } + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc182(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + MYSQL_BIND bind[2]; + char buf1[22]; + MYSQL_RES *result; + MYSQL_ROW row; + + stmt= mysql_stmt_init(mysql); + rc= mariadb_stmt_execute_direct(stmt, "DROP TABLE IF EXISTS t1", -1); + check_stmt_rc(rc, stmt); + rc= mariadb_stmt_execute_direct(stmt, "DROP TABLE IF EXISTS t1", -1); + check_stmt_rc(rc, stmt); + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1", -1); + check_stmt_rc(rc, stmt); + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1", -1); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_close(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "SELECT row_count()"); + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + diag("buf: %s", row[0]); + mysql_free_result(result); + + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, "SELECT row_count()", -1); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + + memset(bind, 0, 2 * sizeof(MYSQL_BIND)); + bind[0].buffer= &buf1; + bind[0].buffer_length= bind[1].buffer_length= 20; + bind[0].buffer_type= bind[1].buffer_type= MYSQL_TYPE_STRING; + + rc= mysql_stmt_bind_result(stmt, bind); + + while(!mysql_stmt_fetch(stmt)) + diag("b1: %s", buf1); + rc= mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc181(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + MYSQL_BIND bind; + const char *stmt_str= "SELECT a FROM t1"; + float f=1; + my_bool err= 0; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES(1073741825)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(stmt_str)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + bind.buffer= &f; + bind.error= &err; + bind.buffer_type= MYSQL_TYPE_FLOAT; + rc= mysql_stmt_bind_result(stmt, &bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + diag("rc=%d err=%d float=%f, %d", rc, err, f, MYSQL_DATA_TRUNCATED); + + rc= mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc198(MYSQL *mysql) +{ + MYSQL_STMT *stmt1, *stmt2; + MYSQL_BIND my_bind[1]; + int32 a; + int rc; + int num_rows= 0; + ulong type; + ulong prefetch_rows= 3; + + + 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)"); + check_mysql_rc(rc, mysql); + + stmt1= mysql_stmt_init(mysql); + stmt2= mysql_stmt_init(mysql); + /* Not implemented in 5.0 */ + type= (ulong) CURSOR_TYPE_SCROLLABLE; + rc= mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (void*) &type); + FAIL_UNLESS(rc, "Error expected"); + rc= mysql_stmt_attr_set(stmt2, STMT_ATTR_CURSOR_TYPE, (void*) &type); + FAIL_UNLESS(rc, "Error expected"); + + type= (ulong) CURSOR_TYPE_READ_ONLY; + rc= mysql_stmt_attr_set(stmt1, STMT_ATTR_CURSOR_TYPE, (void*) &type); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_attr_set(stmt2, STMT_ATTR_CURSOR_TYPE, (void*) &type); + check_stmt_rc(rc, stmt2); + rc= mysql_stmt_attr_set(stmt1, STMT_ATTR_PREFETCH_ROWS, + (void*) &prefetch_rows); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_attr_set(stmt2, STMT_ATTR_PREFETCH_ROWS, + (void*) &prefetch_rows); + check_stmt_rc(rc, stmt2); + rc= mysql_stmt_prepare(stmt1, "SELECT * FROM t1 ORDER by id ASC" , -1); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_prepare(stmt2, "SELECT * FROM t1 ORDER by id DESC", -1); + check_stmt_rc(rc, stmt2); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_execute(stmt2); + check_stmt_rc(rc, stmt2); + + memset(my_bind, '\0', 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(stmt1, my_bind); + mysql_stmt_bind_result(stmt2, my_bind); + + while ((rc= mysql_stmt_fetch(stmt1)) == 0) + ++num_rows; + FAIL_UNLESS(num_rows == 9, "num_rows != 9"); + + num_rows= 0; + while ((rc= mysql_stmt_fetch(stmt2)) == 0) + ++num_rows; + FAIL_UNLESS(num_rows == 9, "num_rows != 9"); + + rc= mysql_stmt_close(stmt1); + rc= mysql_stmt_close(stmt2); + FAIL_UNLESS(rc == 0, ""); + + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc205(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[3]; + char data[8]; + ulong length[3]; + int rc, int_col; + short smint_col; + my_bool is_null[3]; + const char *query = "SELECT text_col, smint_col, int_col FROM test_conc205"; + + rc= mysql_query(mysql, "drop table if exists test_conc205"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE test_conc205 (text_col TEXT, smint_col SMALLINT, int_col INT)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO test_conc205 VALUES('data01', 21893, 1718038908), ('data2', -25734, -1857802040)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', sizeof(my_bind)); + my_bind[0].buffer_type= MYSQL_TYPE_STRING; + my_bind[0].buffer= (void *)data; + my_bind[0].buffer_length= sizeof(data); + my_bind[0].is_null= &is_null[0]; + my_bind[0].length= &length[0]; + + my_bind[1].buffer_type= MYSQL_TYPE_SHORT; + my_bind[1].buffer= &smint_col; + my_bind[1].buffer_length= 2; + my_bind[1].is_null= &is_null[1]; + my_bind[1].length= &length[1]; + + my_bind[2].buffer_type= MYSQL_TYPE_LONG; + my_bind[2].buffer= &int_col; + my_bind[2].buffer_length= 4; + my_bind[2].is_null= &is_null[2]; + my_bind[2].length= &length[2]; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(length[0] != 6, "Wrong fetched string length"); + FAIL_IF(length[1] != 2, "Wrong fetched short length"); + FAIL_IF(length[2] != 4, "Wrong fetched int length"); + + FAIL_IF(strncmp(data, "data01", length[0] + 1) != 0, "Wrong string value"); + FAIL_IF(smint_col != 21893, "Expected 21893"); + FAIL_IF(int_col != 1718038908, "Expected 1718038908"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(length[0] != 5, "Wrong fetched string length"); + FAIL_IF(length[1] != 2, "Wrong fetched short length"); + FAIL_IF(length[2] != 4, "Wrong fetched int length"); + + FAIL_IF(strncmp(data, "data2", length[0] + 1) != 0, "Wrong string value"); + FAIL_IF(smint_col != -25734, "Expected -25734"); + FAIL_IF(int_col != -1857802040, "Expected -1857802040"); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc != MYSQL_NO_DATA, "Expected MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "drop table test_conc205"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc217(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + + SKIP_MAXSCALE; + rc= mariadb_stmt_execute_direct(stmt, "SELECT 1 FROM nonexisting_table", -1); + FAIL_IF(rc==0, "Expected error\n"); + rc= mysql_query(mysql, "drop table if exists t_count"); + check_mysql_rc(rc, mysql); + mysql_stmt_close(stmt); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc208(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + int data; + MYSQL_BIND bind; + + rc= mysql_stmt_prepare(stmt, "SELECT \"100\" UNION SELECT \"88\" UNION SELECT \"389789\"", -1); + check_stmt_rc(rc, stmt); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + bind.buffer_type= MYSQL_TYPE_LONG; + bind.buffer= (void *)&data; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, &bind); + + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + { + diag("data=%d", data); + FAIL_IF(data != 100 && data != 88 && data != 389789, "Wrong value"); + } + mysql_stmt_close(stmt); + return OK; +} + +static int test_mdev14165(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_FIELD *fields; + MYSQL_RES *result; + my_bool val= 1; + MYSQL_BIND bind[1]; + char buf1[52]; + + rc= mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, &val); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + rc= mysql_query(mysql, "CREATE TABLE t1 (i INT(20) ZEROFILL)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (2),(1)"); + check_mysql_rc(rc, mysql); + rc= mysql_stmt_prepare(stmt, "SELECT i FROM t1", -1); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer_length= 51; + bind[0].buffer= buf1; + + mysql_stmt_bind_result(stmt, bind); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &val); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + + fields= mysql_fetch_fields(result); + + FAIL_IF(fields[0].length < 20, "Expected length=20"); + FAIL_IF(fields[0].max_length < 20, "Expected max_length=20"); + + mysql_stmt_fetch(stmt); + + FAIL_UNLESS(strcmp(buf1, "00000000000000000002") == 0, "Wrong result"); + mysql_free_result(result); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_compress(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + + rc= mariadb_stmt_execute_direct(stmt, SL("SELECT 1 FROM DUAL")); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + return OK; +} + +static int equal_MYSQL_TIME(MYSQL_TIME *tm1, MYSQL_TIME *tm2) +{ + return tm1->day==tm2->day && tm1->hour==tm2->hour && tm1->minute==tm2->minute && + tm1->month==tm2->month && tm1->neg==tm2->neg && tm1->second==tm2->second && + tm1->second_part==tm2->second_part && tm1->time_type==tm2->time_type && tm1->year==tm2->year; +} + +static int test_str_to_int(MYSQL *mysql) +{ + int i; + struct st_atoi_test{ + const char *str_value; + int int_value; + int rc; + } atoi_tests[]= + { + {"0", 0, 0}, + {" 1",1, 0}, + {"123 ",123, 0}, + {"10.2",10, MYSQL_DATA_TRUNCATED}, + {"a", 0, MYSQL_DATA_TRUNCATED}, + {"1 2 3", 1, MYSQL_DATA_TRUNCATED}, + {NULL, 0, 0} + }; + + for(i=0; atoi_tests[i].str_value; i++) + { + int rc; + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + struct st_atoi_test *test= &atoi_tests[i]; + char sql[256]; + int int_value; + + snprintf(sql, sizeof(sql), "SELECT '%s'",test->str_value); + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, sql, (ulong)strlen(sql)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_store_result(stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= &int_value; + bind[0].buffer_length= sizeof(int_value); + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + + diag("test: str='%s', expected/returned value =%d/%d, expected/returned rc=%d/%d", + test->str_value, test->int_value, int_value, test->rc, rc); + FAIL_UNLESS(rc == test->rc, "unexpected return code"); + FAIL_UNLESS(int_value == test->int_value, "unexpected int value"); + mysql_stmt_close(stmt); + } + return OK; +} + + +static int test_codbc138(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + MYSQL_TIME tm; + int i= 0; + + struct st_time_test { + const char *statement; + MYSQL_TIME tm; + } time_test[]={ + { "SELECT DATE_ADD('2018-02-01', INTERVAL -188 DAY)", + { 2017,7,28,0,0,0,0L,0, MYSQL_TIMESTAMP_DATE } + }, + { "SELECT '2001-02-03 11:12:13.123456'", + { 2001,2,3,11,12,13,123456L,0, MYSQL_TIMESTAMP_DATETIME } + }, + { "SELECT '2001-02-03 11:12:13.123'", + { 2001,2,3,11,12,13,123000L,0, MYSQL_TIMESTAMP_DATETIME } + }, + { "SELECT '-11:12:13'", + { 0,0,0,11,12,13,0,1, MYSQL_TIMESTAMP_TIME } + }, + { "SELECT ' '", + { 0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR } + }, + { "SELECT '1--'", + { 0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR } + }, + { "SELECT '-2001-01-01'", + { 0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR } + }, + { "SELECT '-11:00'", + { 0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR } + }, + {"SELECT '1972-04-22'", + {1972,4,22, 0,0,0, 0,0,MYSQL_TIMESTAMP_DATE} + }, + {"SELECT ' 1972-04-22 '", + {1972,4,22, 0,0,0, 0,0,MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '1972-04-22a'", + {1972,4,22, 0,0,0, 0,0,MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '0000-00-00'", + {0,0,0, 0,0,0 ,0,0,MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '1970-01-00'", + {1970,1,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '0069-12-31'", + {69,12,31, 0,0,0, 0,0, MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '69-12-31'", + {2069,12,31, 0,0,0, 0,0, MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '68-12-31'", + {2068,12,31, 0,0,0, 0,0, MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '70-01-01'", + {1970,1,1, 0,0,0, 0,0, MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '2010-1-1'", + {2010,1,1, 0,0,0, 0,0, MYSQL_TIMESTAMP_DATE} + }, + + {"SELECT '10000-01-01'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1979-a-01'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1979-01-32'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1979-13-01'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1YYY-01-01'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1979-0M-01'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1979-00-'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1979-00'", + {0,0,0, 0,0,0, 0,0,MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1979'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '79'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR} + }, + + {"SELECT '10:15:00'", + {0,0,0, 10,15,0, 0,0, MYSQL_TIMESTAMP_TIME} + }, + {"SELECT '10:15:01'", + {0,0,0, 10,15,1, 0,0, MYSQL_TIMESTAMP_TIME} + }, + {"SELECT '00:00:00'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_TIME} + }, + {"SELECT '0:0:0'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_TIME} + }, + {"SELECT '10:15:01.'", + {0,0,0, 10,15,1, 0,0, MYSQL_TIMESTAMP_TIME}, + }, + {"SELECT '25:59:59'", + {0,0,0, 25,59,59, 0,0, MYSQL_TIMESTAMP_TIME}, + }, + {"SELECT '838:59:59'", + {0,0,0, 838,59,59, 0,0, MYSQL_TIMESTAMP_TIME}, + }, + {"SELECT '-838:59:59'", + {0,0,0, 838,59,59, 0, 1, MYSQL_TIMESTAMP_TIME}, + }, + + {"SELECT '00:60:00'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR}, + }, + {"SELECT '839:00:00'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR}, + }, + {"SELECT '-839:00:00'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR}, + }, + {"SELECT '-10:15:a'", + { 0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR }, + }, + {"SELECT '1999-12-31 23:59:59.9999999'", + {1999,12,31, 23,59,59, 999999, 0, MYSQL_TIMESTAMP_DATETIME}, + }, + {"SELECT '00-08-11 8:46:40'", + {2000,8,11, 8,46,40, 0,0, MYSQL_TIMESTAMP_DATETIME}, + }, + {"SELECT '1999-12-31 25:59:59.999999'", + {0,0,0, 0,0,0, 0,0, MYSQL_TIMESTAMP_ERROR }, + }, + { NULL,{ 0 } } + }; + + while (time_test[i].statement) + { + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(time_test[i].statement)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_store_result(stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_DATETIME; + bind[0].buffer= &tm; + bind[0].buffer_length= sizeof(MYSQL_TIME); + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + diag("test: %s %d %d", time_test[i].statement, tm.time_type, time_test[i].tm.time_type); + if (time_test[i].tm.time_type == MYSQL_TIMESTAMP_ERROR) + { + FAIL_UNLESS(tm.time_type == MYSQL_TIMESTAMP_ERROR, "MYSQL_TIMESTAMP_ERROR expected"); + } + else + FAIL_UNLESS(equal_MYSQL_TIME(&tm, &time_test[i].tm), "time_in != time_out"); + mysql_stmt_close(stmt); + i++; + } + + return OK; +} + +static int test_conc334(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_RES *result; + MYSQL_FIELD *field; + int rc; + + rc= mysql_stmt_prepare(stmt, SL("SHOW ENGINES")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + result= mysql_stmt_result_metadata(stmt); + if (!result) + { + diag("Couldn't retrieve result set"); + mysql_stmt_close(stmt); + return FAIL; + } + + mysql_field_seek(result, 0); + + while ((field= mysql_fetch_field(result))) + { + FAIL_IF(field->name_length == 0, "Invalid name length (0)"); + FAIL_IF(field->table_length == 0, "Invalid name length (0)"); + } + mysql_free_result(result); + mysql_stmt_close(stmt); + + return OK; +} +static int test_conc344(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int, b int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1,1), (2,2),(3,3),(4,4),(5,5)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL("SELECT * FROM t1 ORDER BY a")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + while (!mysql_stmt_fetch(stmt)); + FAIL_IF(mysql_stmt_num_rows(stmt) != 5, "expected 5 rows"); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + diag("num_rows: %lld", mysql_stmt_num_rows(stmt)); + FAIL_IF(mysql_stmt_num_rows(stmt) != 1, "expected 1 row"); + + mysql_stmt_close(stmt); + return OK; +} + + +static int test_conc_fraction(MYSQL *mysql) +{ + MYSQL_TIME tm; + MYSQL_BIND bind[1]; + char query[1024]; + int i; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + unsigned long frac= 0; + + for (i=0; i < 10; i++, frac=frac*10+i) + { + unsigned long expected= 0; + sprintf(query, "SELECT '2018-11-05 22:25:59.%ld'", frac); + + diag("%d: %s", i, query); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc = mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_DATETIME; + bind[0].buffer= &tm; + bind[0].buffer_length= sizeof(MYSQL_TIME); + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + diag("second_part: %ld", tm.second_part); + + expected= i > 6 ? 123456 : frac * (unsigned int)powl(10, (6 - i)); + + if (tm.second_part != expected) + { + diag("Error: tm.second_part=%ld expected=%ld", tm.second_part, expected); + return FAIL; + } + } + mysql_stmt_close(stmt); + return OK; +} + +static int test_zerofill_1byte(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + MYSQL_BIND bind; + char buffer[3]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int zerofill)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES(1)"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL("SELECT a FROM t1")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + bind.buffer_type= MYSQL_TYPE_STRING; + bind.buffer= buffer; + bind.buffer_length= 1; + + rc= mysql_stmt_bind_result(stmt, &bind); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc != 101, "expected truncation warning"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc424(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + my_bool max_len= 1; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_table1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE test_table1 (test_int INT, b int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO test_table1 values(10,11),(11,12)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS testCursor"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE PROCEDURE testCursor()\n" + "BEGIN\n" + "DECLARE test_int INT;\n" + "DECLARE b INT;\n" + "DECLARE done INT DEFAULT FALSE;\n" + "DECLARE testCursor CURSOR\n" + "FOR\n" + "SELECT test_int,b FROM test_table1;\n" + "DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;\n" + "OPEN testCursor;\n" + + " read_loop: LOOP\n" + " FETCH testCursor INTO test_int, b;\n" + " IF done THEN\n" + " LEAVE read_loop;\n" + " END IF;\n" + " SELECT test_int,b;" + " END LOOP;\n" + "CLOSE testCursor;\n" + "END"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL("CALL testCursor()")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &max_len); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + do { + if (mysql_stmt_field_count(stmt)) + { + MYSQL_RES *res= mysql_stmt_result_metadata(stmt); + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc, "Wrong return code"); + mysql_free_result(res); + } + rc= mysql_stmt_next_result(stmt); + + } while (!rc); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP PROCEDURE testCursor"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE test_table1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_maxparam(MYSQL *mysql) +{ + const char *query= "INSERT INTO t1 VALUES (?)"; + int rc; + char *buffer; + int i; + int val= 1; + size_t mem= strlen(query) + 1 + 4 * 65535 + 1; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_BIND* bind; + + bind = calloc(sizeof(MYSQL_BIND), 65535); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + + buffer= calloc(1, mem); + strcpy(buffer, query); + for (i=0; i < 65534.; i++) + strcat(buffer, ",(?)"); + rc= mysql_stmt_prepare(stmt, SL(buffer)); + check_stmt_rc(rc, stmt); + + for (i=0; i < 65534; i++) + { + bind[i].buffer_type= MYSQL_TYPE_LONG; + bind[i].buffer= &val; + } + + rc= mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 65535, "Expected affected_rows=65535"); + + strcat(buffer, ",(?)"); + rc= mysql_stmt_prepare(stmt, SL(buffer)); + free(buffer); + FAIL_IF(!rc, "Error expected"); + FAIL_IF(mysql_stmt_errno(stmt) != ER_PS_MANY_PARAM, "Expected ER_PS_MANY_PARAM error"); + + mysql_stmt_close(stmt); + free(bind); + return OK; +} + +static int test_mdev_21920(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_BIND bind[1]; + int rc; + char buffer[128]; + + rc= mysql_stmt_prepare(stmt, SL("SELECT ''")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + buffer[0]= 1; + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer= buffer; + bind[0].buffer_length= 127; + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(buffer[0] != 0, "Expected empty string"); + + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_returning(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_RES *result; + int rc; + + diag("MDEV-23768 not fixed yet"); + mysql_stmt_close(stmt); + return SKIP; + + rc= mysql_query(mysql, "CREATE TEMPORARY TABLE t1 (a int not null auto_increment primary key, b json)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 (a,b) VALUES (NULL, '[incorrect json]') RETURNING a"); + check_mysql_rc(rc, mysql); + + if (!rc) diag("should have fail"); + + result= mysql_store_result(mysql); + mysql_free_result(result); + + diag("Error: %s", mysql_error(mysql)); + + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO t1 (a,b) VALUES (NULL, '[incorrect json]') RETURNING a")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_close(stmt); + + return OK; +} + +static int test_conc504(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + const char *sp= "CREATE PROCEDURE p1()\n" \ + "BEGIN\n"\ + " SELECT 1;\n"\ + " SELECT 2;\n"\ + " SELECT 3;\n"\ + "END"; + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, sp); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt, SL("CALL p1()")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_store_result(stmt); + FAIL_IF(mysql_stmt_num_rows(stmt) != 1, "Expected 1 row"); + + mysql_stmt_next_result(stmt); + mysql_stmt_store_result(stmt); + FAIL_IF(mysql_stmt_num_rows(stmt) != 1, "Expected 1 row"); + + mysql_stmt_next_result(stmt); + mysql_stmt_store_result(stmt); + FAIL_IF(mysql_stmt_num_rows(stmt) != 1, "Expected 1 row"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP PROCEDURE p1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_conc512(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + MYSQL_BIND bind; + float f; + + rc= mysql_query(mysql, "drop table if exists t1"); + + rc= mysql_real_query(mysql, SL("CREATE TABLE t1 (a int)")); + + rc= mysql_real_query(mysql, SL("INSERT INTO t1 VALUES (1073741825)")); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL("SELECT a FROM t1")); + check_stmt_rc(rc, stmt); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + bind.buffer= &f; + bind.buffer_type= MYSQL_TYPE_FLOAT; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, &bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(rc != 101, "Truncation expected"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc525(MYSQL *mysql) +{ + FILE *fp; + MYSQL_STMT *stmt; + int rc; + + rc= mysql_query(mysql, "create temporary table t1 (a blob)"); + check_mysql_rc(rc, mysql); + + /* create a dummy import file */ + if (!(fp= fopen("./test.csv", "w"))) + { + diag("couldn't create file './test.csv'"); + return FAIL; + } + fprintf(fp, "1\n2\n"); + fclose(fp); + + /* Test: prepare and execute + should fail due to non existing file */ + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL("LOAD DATA LOCAL INFILE './test.notexist' INTO table t1")); + + if (rc && mysql_stmt_errno(stmt) == ER_UNSUPPORTED_PS) + { + diag("Server doesn't support LOAD LOCAL INFILE in binary protocol."); + mysql_stmt_close(stmt); + return SKIP; + } + + rc= mysql_stmt_execute(stmt); + FAIL_IF(!rc, "Error expected (file does not exist)"); + + mysql_stmt_close(stmt); + + /* Test: prepare and execute + 2 rows should be inserted */ + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL("LOAD DATA LOCAL INFILE './test.csv' INTO table t1")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 2, "Expected 2 inserted rows"); + + mysql_stmt_close(stmt); + stmt= mysql_stmt_init(mysql); + + /* Test: execute_direct + 2 rows should be inserted */ + rc= mariadb_stmt_execute_direct(stmt, SL("LOAD DATA LOCAL INFILE './test.csv' INTO table t1")); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_affected_rows(stmt) != 2, "Expected 2 inserted rows"); + + /* Cleanup */ + mysql_stmt_close(stmt); + unlink("./test.csv"); + + return OK; +} + +static int test_conc566(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt = mysql_stmt_init(mysql); + unsigned long cursor = CURSOR_TYPE_READ_ONLY; + const char* query= "call sp()"; + + SKIP_SKYSQL; + + rc= mysql_query(mysql,"drop procedure if exists sp"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql,"create procedure sp() select 1"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt,query,-1); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &cursor); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql,"drop procedure sp"); + check_mysql_rc(rc, mysql); + return OK; +} + +#define MDEV19838_MAX_PARAM_COUNT 32 +#define MDEV19838_FIELDS_COUNT 17 + +static int test_mdev19838(MYSQL *mysql) +{ + int rc; + MYSQL_BIND bind[MDEV19838_MAX_PARAM_COUNT]; + unsigned int i, paramCount = 1; + char charvalue[] = "012345678901234567890123456789012345"; + MYSQL_STMT *stmt; + + SKIP_MAXSCALE; + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + + rc = mysql_query(mysql, "CREATE temporary 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)" + ")"); + check_mysql_rc(rc, mysql); + + stmt = mysql_stmt_init(mysql); + + 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 = sizeof charvalue; + 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_stmt_rc(rc, stmt); + + 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 */ + FAIL_UNLESS(rc != 0, "rc!=0"); + + mysql_stmt_close(stmt); + stmt = mysql_stmt_init(mysql); + } + + 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 */ + FAIL_UNLESS(rc != 0, "rc!=0"); + mysql_stmt_close(stmt); + + stmt = mysql_stmt_init(mysql); + /* 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_stmt_rc(rc, stmt); + + /* 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); + + mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶mCount); + + rc = mysql_stmt_bind_param(stmt, bind); + + rc = mariadb_stmt_execute_direct(stmt, "INSERT INTO mdev19838" + "(f1, f2, f3, f4, f5, f6, f7, f8, f9)" + " VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?)", -1); + + /* Expecting an error */ + FAIL_UNLESS(rc != 0, "rc"); + } + + /* 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); + + mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶mCount); + + if (paramCount > 0) + { + rc = mysql_stmt_bind_param(stmt, bind); + check_stmt_rc(rc, stmt); + } + + 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. + */ + FAIL_UNLESS(rc == 0, ""); + } + + mysql_stmt_close(stmt); + return OK; +} + +my_bool conc623_param_callback(void *data __attribute((unused)), + MYSQL_BIND *bind __attribute((unused)), + unsigned int row_nr __attribute((unused))) +{ + return 1; +} + +static int test_conc623(MYSQL *mysql) +{ + int rc; + unsigned int paramcount= 1; + unsigned int array_size= 2; + MYSQL_BIND bind; + + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + + rc= mysql_query(mysql, "CREATE TEMPORARY TABLE t1 (a int)"); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CB_USER_DATA, mysql); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_PREBIND_PARAMS, ¶mcount); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_attr_set(stmt, STMT_ATTR_CB_PARAM, conc623_param_callback); + check_stmt_rc(rc, stmt); + + bind.buffer_type= MYSQL_TYPE_LONG; + rc= mysql_stmt_bind_param(stmt, &bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_prepare(stmt, SL("INSERT INTO t1 VALUES (?)")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + if (!rc) + { + diag("Error expected from callback function"); + mysql_stmt_close(stmt); + return FAIL; + } + + diag("Error (expected) %s", mysql_stmt_error(stmt)); + mysql_stmt_close(stmt); + return OK; +} + +static int test_conc627(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + + SKIP_MYSQL(mysql); + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL("show grants for mysqltest_8")); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + mysql_stmt_store_result(stmt); + FAIL_IF(!mysql_stmt_errno(stmt), "Expected error"); + FAIL_IF(strcmp(mysql_error(mysql), mysql_stmt_error(stmt)), "Error messages differ"); + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_conc633(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL *my= NULL; + int ret= FAIL; + int rc; + + SKIP_MYSQL(mysql); + + stmt= mysql_stmt_init(mysql); + + if (!mariadb_stmt_execute_direct(stmt, SL("SÄLECT 1"))) + { + diag("Syntax error expected"); + goto end; + } + + if (mysql_errno(mysql) != mysql_stmt_errno(stmt)) + { + diag("Different error codes. mysql_errno= %d, mysql_stmt_errno=%d", + mysql_errno(mysql), mysql_stmt_errno(stmt)); + goto end; + } + + if ((long)stmt->stmt_id != -1) + { + diag("Error: expected stmt_id=-1"); + goto end; + } + + if (!(my= test_connect(NULL))) + { + diag("Can establish connection (%s)", mysql_error(my)); + goto end; + } + + rc= mysql_query(my, "CREATE OR REPLACE TABLE conc633 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET @@lock_wait_timeout=3"); + + rc= mysql_query(my, "LOCK TABLES conc633 WRITE"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET @@lock_wait_timeout=3"); + check_mysql_rc(rc, mysql); + + if (!mariadb_stmt_execute_direct(stmt, SL("INSERT INTO conc633 VALUES (1)"))) + { + diag("lock wait timeout error expected"); + goto end; + } + + if (stmt->state != MYSQL_STMT_PREPARED) + { + diag("Error: stmt hasn't prepared status"); + goto end; + } + + if ((long)stmt->stmt_id == -1) + { + diag("Error: no stmt_id assigned"); + goto end; + } + + rc= mysql_query(my, "UNLOCK TABLES"); + check_mysql_rc(rc, mysql); + rc= mysql_query(my, "DROP TABLE conc633"); + check_mysql_rc(rc, mysql); + + ret= OK; + +end: + if (my) + mysql_close(my); + mysql_stmt_close(stmt); + return ret; +} + +struct my_tests_st my_tests[] = { + {"test_conc633", test_conc633, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc623", test_conc623, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc627", test_conc627, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_mdev19838", test_mdev19838, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc525", test_conc525, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc566", test_conc566, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc512", test_conc512, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc504", test_conc504, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_returning", test_returning, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_mdev_21920", test_mdev_21920, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_maxparam", test_maxparam, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc424", test_conc424, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc344", test_conc344, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc334", test_conc334, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_compress", test_compress, TEST_CONNECTION_NEW, CLIENT_COMPRESS, NULL, NULL}, + {"test_zerofill_1byte", test_zerofill_1byte, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_codbc138", test_codbc138, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc208", test_conc208, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_mdev14165", test_mdev14165, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc208", test_conc208, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc217", test_conc217, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc205", test_conc205, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc198", test_conc198, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc182", test_conc182, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc181", test_conc181, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc179", test_conc179, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc177", test_conc177, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc167", test_conc167, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc168", test_conc168, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc155", test_conc155, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_conc154", test_conc154, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_conc141", test_conc141, TEST_CONNECTION_NEW, 0, NULL , NULL}, + {"test_conc67", test_conc67, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_conc_5", test_conc_5, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug1115", test_bug1115, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug1180", test_bug1180, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug1644", test_bug1644, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11037", test_bug11037, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11183", test_bug11183, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug12744", test_bug12744, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug1500", test_bug1500, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug15510", test_bug15510, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug15518", test_bug15518, TEST_CONNECTION_NEW | TEST_CONNECTION_DONT_CLOSE, CLIENT_MULTI_STATEMENTS, NULL , NULL}, + {"test_bug15613", test_bug15613, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug16144", test_bug16144, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug1664", test_bug1664, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug1946", test_bug1946, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug2247", test_bug2247, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug2248", test_bug2248, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug20152", test_bug20152, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug23383", test_bug23383, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug27592", test_bug27592, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug28934", test_bug28934, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug36004", test_bug36004, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug3035", test_bug3035, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug3117", test_bug3117, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug3796", test_bug3796, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug4026", test_bug4026, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug4030", test_bug4030, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug4079", test_bug4079, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug4172", test_bug4172, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug4231", test_bug4231, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug4236", test_bug4236, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug5126", test_bug5126, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug5194", test_bug5194, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug5315", test_bug5315, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug5399", test_bug5399, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug6046", test_bug6046, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug6049", test_bug6049, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug6058", test_bug6058, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug6059", test_bug6059, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug6096", test_bug6096, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug7990", test_bug7990, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug8330", test_bug8330, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug8722", test_bug8722, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_ps_conj_select", test_ps_conj_select, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_ps_null_param", test_ps_null_param, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_ps_query_cache", test_ps_query_cache, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_ushort_bug", test_ushort_bug, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_field_misc", test_field_misc, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_mem_overun", test_mem_overun, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_decimal_bug", test_decimal_bug, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_explain_bug", test_explain_bug, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_sshort_bug", test_sshort_bug, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_stiny_bug", test_stiny_bug, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug53311", test_bug53311, TEST_CONNECTION_NEW, 0, NULL , NULL}, + {"test_conc_fraction", test_conc_fraction, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_str_to_int", test_str_to_int, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/ps_new.c b/libmariadb/unittest/libmariadb/ps_new.c new file mode 100644 index 00000000..ea228f1c --- /dev/null +++ b/libmariadb/unittest/libmariadb/ps_new.c @@ -0,0 +1,526 @@ +/************************************************************************************ + Copyright (C) 2012 Monty Program AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see <http://www.gnu.org/licenses> + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + *************************************************************************************/ + +#include "my_test.h" + +/* Utility function to verify the field members */ + + +static int test_multi_result(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND ps_params[3]; /* input parameter buffers */ + MYSQL_BIND rs_bind[3]; + int int_data[3]; /* input/output values */ + my_bool is_null[3]; /* output value nullability */ + int rc, i; + + //https://jira.mariadb.org/browse/XPT-268 + SKIP_XPAND; + + /* set up stored procedure */ + rc = mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + rc = mysql_query(mysql, + "CREATE PROCEDURE p1(" + " IN p_in INT, " + " OUT p_out INT, " + " INOUT p_inout INT) " + "BEGIN " + " SELECT p_in, p_out, p_inout; " + " SET p_in = 100, p_out = 200, p_inout = 300; " + " SELECT p_in, p_out, p_inout; " + "END"); + check_mysql_rc(rc, mysql); + + /* initialize and prepare CALL statement with parameter placeholders */ + stmt = mysql_stmt_init(mysql); + if (!stmt) + { + diag("Could not initialize statement"); + exit(1); + } + rc = mysql_stmt_prepare(stmt, "CALL p1(?, ?, ?)", 16); + check_stmt_rc(rc, stmt); + + /* initialize parameters: p_in, p_out, p_inout (all INT) */ + 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; + + ps_params[1].buffer_type = MYSQL_TYPE_LONG; + ps_params[1].buffer = (char *) &int_data[1]; + ps_params[1].length = 0; + ps_params[1].is_null = 0; + + ps_params[2].buffer_type = MYSQL_TYPE_LONG; + ps_params[2].buffer = (char *) &int_data[2]; + ps_params[2].length = 0; + ps_params[2].is_null = 0; + + /* bind parameters */ + rc = mysql_stmt_bind_param(stmt, ps_params); + check_stmt_rc(rc, stmt); + + /* assign values to parameters and execute statement */ + int_data[0]= 10; /* p_in */ + int_data[1]= 20; /* p_out */ + int_data[2]= 30; /* p_inout */ + + rc = mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_field_count(stmt) != 3, "expected 3 fields"); + + memset(rs_bind, 0, sizeof (MYSQL_BIND) * 3); + for (i=0; i < 3; i++) + { + rs_bind[i].buffer = (char *) &(int_data[i]); + rs_bind[i].buffer_length = sizeof (int_data); + rs_bind[i].buffer_type = MYSQL_TYPE_LONG; + rs_bind[i].is_null = &is_null[i]; + } + rc= mysql_stmt_bind_result(stmt, rs_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_IF(int_data[0] != 10 || int_data[1] != 20 || int_data[2] != 30, + "expected 10 20 30"); + rc= mysql_stmt_next_result(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_bind_result(stmt, rs_bind); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(mysql_stmt_field_count(stmt) != 3, "expected 3 fields"); + FAIL_IF(int_data[0] != 100 || int_data[1] != 200 || int_data[2] != 300, + "expected 100 200 300"); + + FAIL_IF(mysql_stmt_next_result(stmt) != 0, "expected more results"); + rc= mysql_stmt_bind_result(stmt, rs_bind); + + rc= mysql_stmt_fetch(stmt); + FAIL_IF(mysql_stmt_field_count(stmt) != 2, "expected 2 fields"); + FAIL_IF(int_data[0] != 200 || int_data[1] != 300, + "expected 200 300"); + + FAIL_IF(mysql_stmt_next_result(stmt) != 0, "expected more results"); + FAIL_IF(mysql_stmt_field_count(stmt) != 0, "expected 0 fields"); + + rc= mysql_stmt_close(stmt); + rc = mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + return OK; +} + +int test_sp_params(MYSQL *mysql) +{ + int i, rc; + MYSQL_STMT *stmt; + int a[] = {10,20,30}; + MYSQL_BIND bind[3]; + const char *stmtstr= "CALL P1(?,?,?)"; + char res[3][20]; + + //https://jira.mariadb.org/browse/XPT-268 + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + 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"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt,SL(stmtstr)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 3, "expected param_count=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); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(res, 0, 60); + + memset(bind, 0, sizeof(MYSQL_BIND) * 3); + for (i=0; i < 3; i++) + { + bind[i].buffer_type= MYSQL_TYPE_STRING; + bind[i].buffer_length= 20; + bind[i].buffer= res[i]; + } + + do { + if (mysql->server_status & SERVER_PS_OUT_PARAMS) + { + diag("out param result set"); + FAIL_IF(mysql_stmt_field_count(stmt) != 2, "expected 2 columns"); + FAIL_IF(strcmp(stmt->fields[0].org_name, "p_out") != 0, "wrong field name"); + FAIL_IF(strcmp(stmt->fields[1].org_name, "p_inout") != 0, "wrong field name"); + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(strcmp(res[0],"This is OUT param") != 0, "comparison failed"); + FAIL_IF(strcmp(res[1],"200") != 0, "comparison failed"); + } + else + if (mysql_stmt_field_count(stmt)) + { + diag("sp result set"); + FAIL_IF(mysql_stmt_field_count(stmt) != 3, "expected 3 columns"); + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_IF(strcmp(res[0],"200") != 0, "comparison failed"); + FAIL_IF(strcmp(res[1],"300") != 0, "comparison failed"); + FAIL_IF(strcmp(res[2],"OUT param") != 0, "comparison failed"); + + } + } while (mysql_stmt_next_result(stmt) == 0); + + rc= mysql_stmt_close(stmt); + return OK; +} + +int test_sp_reset(MYSQL *mysql) +{ + int i, rc; + MYSQL_STMT *stmt; + int a[] = {10,20,30}; + MYSQL_BIND bind[3]; + const char *stmtstr= "CALL P1(?,?,?)"; + + //https://jira.mariadb.org/browse/XPT-268 + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + 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"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt,SL(stmtstr)); + check_stmt_rc(rc, stmt); + + FAIL_IF(mysql_stmt_param_count(stmt) != 3, "expected param_count=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); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + + /*connection shouldn't be blocked now */ + + rc= mysql_query(mysql, "DROP PROCEDURE p1"); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_close(stmt); + return OK; +} + +int test_sp_reset1(MYSQL *mysql) +{ + int rc; + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + + char tmp[20]; + const char *stmtstr= "CALL P1(?)"; + + // https://jira.mariadb.org/browse/XPT-268 + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE PROCEDURE p1(OUT p_out VARCHAR(19))" + "BEGIN " + " SET p_out = 'foo';" + " SELECT 'foo' FROM DUAL;" + " SELECT 'bar' FROM DUAL;" + "END"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt,SL(stmtstr)); + check_stmt_rc(rc, stmt); + + memset(tmp, 0, sizeof(tmp)); + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer= tmp; + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer_length= 4; + + mysql_stmt_bind_param(stmt, bind); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_next_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + /* mysql_stmt_reset should set statement in prepared state. + * this means: all subsequent result sets should be flushed. + * Let's try! + */ + rc= mysql_stmt_reset(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "DROP PROCEDURE p1"); + check_mysql_rc(rc, mysql); + + mysql_stmt_close(stmt); + return OK; +} + +int test_sp_reset2(MYSQL *mysql) +{ + int rc, i; + MYSQL_STMT *stmt; + MYSQL_BIND bind[4]; + long l[4]; + const char *stmtstr= "CALL P1()"; + + memset(l, 0, sizeof(l)); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE PROCEDURE p1()" + "BEGIN " + " SET @a:=1;" + " INSERT INTO t1 VALUES(1);" + " SELECT 1 FROM DUAL;" + " SELECT 2,3 FROM DUAL;" + " INSERT INTO t1 VALUES(2);" + " SELECT 3,4,5 FROM DUAL;" + " SELECT 4,5,6,7 FROM DUAL;" + "END"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt,SL(stmtstr)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(bind, 0, sizeof(MYSQL_BIND) * 4); + for (i=0; i < 4; i++) + { + bind[i].buffer_type= MYSQL_TYPE_LONG; + bind[i].buffer= &l[i]; + } + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + while (rc != MYSQL_NO_DATA) + { + rc= mysql_stmt_fetch(stmt); + diag("l=%ld", l[0]); + } + + rc= mysql_stmt_next_result(stmt); + check_stmt_rc(rc, stmt); + + /* now rebind since we expect 2 columns */ + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + while (rc != MYSQL_NO_DATA) + { + rc= mysql_stmt_fetch(stmt); + diag("l=%ld l=%ld", l[0], l[1]); + } + + + rc= mysql_stmt_next_result(stmt); + check_stmt_rc(rc, stmt); + + /* now rebind since we expect 2 columns */ + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + + while (rc != MYSQL_NO_DATA) + { + rc= mysql_stmt_fetch(stmt); + diag("l=%ld l=%ld l=%ld", l[0], l[1], l[2]); + } + + rc= mysql_stmt_close(stmt); + + + rc= mysql_query(mysql, "DROP PROCEDURE p1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +int test_query(MYSQL *mysql) +{ + int rc; + int i; + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + + char tmp[20]; + const char *stmtstr= "CALL P1(?)"; + + // https://jira.mariadb.org/browse/XPT-268 + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE PROCEDURE p1(OUT p_out VARCHAR(19))" + "BEGIN " + " SET p_out = 'foo';" + " SELECT 1 FROM DUAL;" + "END"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + check_mysql_rc(rc, mysql); + + rc= mysql_stmt_prepare(stmt,SL(stmtstr)); + check_stmt_rc(rc, stmt); + + for (i=0; i < 1000; i++) + { + int status; + memset(tmp, 0, sizeof(tmp)); + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer= tmp; + bind[0].buffer_type= MYSQL_TYPE_STRING; + bind[0].buffer_length= 4; + + mysql_stmt_bind_param(stmt, bind); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + do { + if (stmt->field_count) + { + mysql_stmt_bind_result(stmt, bind); + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + while(mysql_stmt_fetch(stmt) == 0); + + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + } + status= mysql_stmt_next_result(stmt); + if (status == 1) + check_stmt_rc(status, stmt); + } while (status == 0); + + rc= mysql_stmt_reset(stmt); + if (rc) + diag("reset failed after %d iterations", i); + check_stmt_rc(rc, stmt); + } + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_query", test_query, TEST_CONNECTION_DEFAULT, CLIENT_MULTI_RESULTS , NULL , NULL}, + {"test_sp_params", test_sp_params, TEST_CONNECTION_DEFAULT, CLIENT_MULTI_STATEMENTS, NULL , NULL}, + {"test_sp_reset", test_sp_reset, TEST_CONNECTION_DEFAULT, CLIENT_MULTI_STATEMENTS, NULL , NULL}, + {"test_sp_reset1", test_sp_reset1, TEST_CONNECTION_DEFAULT, CLIENT_MULTI_STATEMENTS, NULL , NULL}, + {"test_sp_reset2", test_sp_reset2, TEST_CONNECTION_DEFAULT, CLIENT_MULTI_STATEMENTS, NULL , NULL}, + {"test_multi_result", test_multi_result, TEST_CONNECTION_DEFAULT, CLIENT_MULTI_STATEMENTS, NULL , NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/result.c b/libmariadb/unittest/libmariadb/result.c new file mode 100644 index 00000000..c4a56438 --- /dev/null +++ b/libmariadb/unittest/libmariadb/result.c @@ -0,0 +1,1105 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +/** + Some basic tests of the client API. +*/ + +#include "my_test.h" + +static int client_store_result(MYSQL *mysql) +{ + MYSQL_RES *result; + int rc, rowcount= 0; + + rc= mysql_query(mysql, "SELECT 'foo' FROM DUAL UNION SELECT 'bar' FROM DUAL"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + /* since we use store result, we should be able execute other api calls */ + rc= mysql_ping(mysql); + FAIL_IF(rc, "mysql_ping failed"); + + while (mysql_fetch_row(result)) + rowcount++; + + FAIL_IF(rowcount != 2, "rowcount != 2"); + + mysql_free_result(result); + + return OK; +} + +static int client_use_result(MYSQL *mysql) +{ + MYSQL_RES *result; + int rc, rowcount= 0; + + rc= mysql_query(mysql, "SELECT 'foo' FROM DUAL UNION SELECT 'bar' FROM DUAL"); + check_mysql_rc(rc, mysql); + + /* get the result */ + result= mysql_use_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + /* since we use use result, we shouldn't be able execute other api calls */ + rc= mysql_ping(mysql); + FAIL_IF(!rc, "Error expected"); + + while (mysql_fetch_row(result)) + rowcount++; + + FAIL_IF(rowcount != 2, "rowcount != 2"); + + mysql_free_result(result); + + return OK; +} + +static int test_free_result(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + char c2[5]; + ulong bl1, l2; + int rc, c1, bc1; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "drop table if exists test_free_result"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table test_free_result(" + "c1 int primary key auto_increment)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into test_free_result values(), (), ()"); + check_mysql_rc(rc, mysql); + + strcpy(query, "select * from test_free_result"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + FAIL_UNLESS(strncmp(c2, "1", 1) == 0, "c2 != '1'"); + FAIL_UNLESS(l2 == 1, "l2 != 1"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + FAIL_UNLESS(c1 == 2, "c1 != 2"); + FAIL_UNLESS(l2 == 4, "l2 != 4"); + + rc= mysql_query(mysql, "drop table test_free_result"); + FAIL_IF(!rc, "Error commands out of sync expected"); + + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "drop table test_free_result"); + check_mysql_rc(rc, mysql); /* should be successful */ + + mysql_stmt_close(stmt); + + return OK; +} + + +/* Test mysql_stmt_store_result() */ + +static int test_free_store_result(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + MYSQL_BIND my_bind[1]; + char c2[5]; + ulong bl1, l2; + int rc, c1, bc1; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "drop table if exists test_free_result"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table test_free_result(c1 int primary key auto_increment)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "insert into test_free_result values(), (), ()"); + check_mysql_rc(rc, mysql); + + strcpy(query, "select * from test_free_result"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + FAIL_UNLESS(strncmp(c2, "1", 1) == 0, "c2 != '1'"); + FAIL_UNLESS(l2 == 1, "l2 != 1"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + 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_stmt_rc(rc, stmt); + FAIL_UNLESS(c1 == 2, "c1 != 2"); + FAIL_UNLESS(l2 == 4, "l2 != 4"); + + rc= mysql_stmt_free_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_query(mysql, "drop table test_free_result"); + check_mysql_rc(rc, mysql); + + mysql_stmt_close(stmt); + + return OK; +} + +static int test_store_result(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + int32 nData; + char szData[100]; + MYSQL_BIND my_bind[2]; + ulong length, length1; + my_bool is_null[2]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_store_result(col1 int , col2 varchar(50))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_store_result VALUES(10, 'venu'), (20, 'mysql')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_store_result(col2) VALUES('monty')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + /* fetch */ + memset(my_bind, '\0', 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; + + strcpy(query, "SELECT * FROM test_store_result"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 10, "nData != 10"); + FAIL_UNLESS(strcmp(szData, "venu") == 0, "szData != 'Venu'"); + FAIL_UNLESS(length1 == 4, "length1 != 4"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 20, "nData != 20"); + FAIL_UNLESS(strcmp(szData, "mysql") == 0, "szDaza != 'mysql'"); + FAIL_UNLESS(length1 == 5, "length1 != 5"); + + length= 99; + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(is_null[0], "isnull set"); + FAIL_UNLESS(strcmp(szData, "monty") == 0, "szData != 'monty'"); + FAIL_UNLESS(length1 == 5, "length1 != 5"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 10, "nData != 10"); + FAIL_UNLESS(strcmp(szData, "venu") == 0, "szData != 'Venu'"); + FAIL_UNLESS(length1 == 4, "length1 != 4"); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 20, "nData != 20"); + FAIL_UNLESS(strcmp(szData, "mysql") == 0, "szDaza != 'mysql'"); + FAIL_UNLESS(length1 == 5, "length1 != 5"); + + length= 99; + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(is_null[0], "isnull set"); + FAIL_UNLESS(strcmp(szData, "monty") == 0, "szData != 'monty'"); + FAIL_UNLESS(length1 == 5, "length1 != 5"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* Test simple bind store result */ + +static int test_store_result1(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_store_result(col1 int , col2 varchar(50))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_store_result VALUES(10, 'venu'), (20, 'mysql')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_store_result(col2) VALUES('monty')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + strcpy(query, "SELECT * FROM test_store_result"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 3, "rowcount != 3"); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= 0; + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rc++; + FAIL_UNLESS(rc == 3, "rowcount != 3"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +/* Another test for bind and store result */ + +static int test_store_result2(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + int nData; + ulong length; + MYSQL_BIND my_bind[1]; + char query[MAX_TEST_QUERY_LENGTH]; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_store_result(col1 int , col2 varchar(50))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_store_result VALUES(10, 'venu'), (20, 'mysql')"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_store_result(col2) VALUES('monty')"); + check_mysql_rc(rc, mysql); + + rc= mysql_commit(mysql); + check_mysql_rc(rc, mysql); + + memset(my_bind, '\0', 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; + + strcpy((char *)query , "SELECT col1 FROM test_store_result where col1= ?"); + stmt= mysql_stmt_init(mysql); + FAIL_IF(!stmt, mysql_error(mysql)); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, my_bind); + check_stmt_rc(rc, stmt); + + nData= 10; length= 0; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + nData= 0; + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 10, "nData != 10"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + + nData= 20; + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + nData= 0; + rc= mysql_stmt_store_result(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + + FAIL_UNLESS(nData == 20, "nData != 20"); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(rc == MYSQL_NO_DATA, "rc != MYSQL_NO_DATA"); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_store_result"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug11718(MYSQL *mysql) +{ + 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"; + + rc= mysql_query(mysql, "drop table if exists t1, t2"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (f1 int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t2 (f2 int, f3 numeric(8))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values (1), (2)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t2 values (1,20050101), (2,20050202)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + res = mysql_store_result(mysql); + + FAIL_UNLESS(res->fields[0].type == MYSQL_TYPE_DATE, "type != MYSQL_TYPE_DATE"); + mysql_free_result(res); + rc= mysql_query(mysql, "drop table t1, t2"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug19671(MYSQL *mysql) +{ + MYSQL_RES *result; + int rc; + + mysql_query(mysql, "set sql_mode=''"); + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "drop view if exists v1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1(f1 int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create view v1 as select va.* from t1 va"); + check_mysql_rc(rc, mysql); + + result= mysql_list_fields(mysql, "v1", NULL); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 0, ""); + + if (verify_prepare_field(result, 0, "f1", "f1", MYSQL_TYPE_LONG, + "v1", "v1", schema, 11, "0")) { + mysql_free_result(result); + diag("verify_prepare_field failed"); + return FAIL; + } + + mysql_free_result(result); + check_mysql_rc(mysql_query(mysql, "drop view v1"), mysql); + check_mysql_rc(mysql_query(mysql, "drop table t1"), mysql); + return OK; +} + +/* + 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 int test_bug21726(MYSQL *mysql) +{ + 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; + unsigned long long insert_id; + const char *select_query= "SELECT * FROM t1"; + MYSQL_RES *result; + + rc= mysql_query(mysql, create_table[0]); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, create_table[1]); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, create_table[2]); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, update_query); + check_mysql_rc(rc, mysql); + insert_id= mysql_insert_id(mysql); + FAIL_UNLESS(insert_id == 2, "insert_id != 2"); + + rc= mysql_query(mysql, update_query); + check_mysql_rc(rc, mysql); + insert_id= mysql_insert_id(mysql); + FAIL_UNLESS(insert_id == 3, "insert_id != 3"); + + rc= mysql_query(mysql, select_query); + check_mysql_rc(rc, mysql); + insert_id= mysql_insert_id(mysql); + FAIL_UNLESS(insert_id == 3, "insert_id != 3"); + result= mysql_store_result(mysql); + mysql_free_result(result); + + return OK; +} + +/* Bug#6761 - mysql_list_fields doesn't work */ + +static int test_bug6761(MYSQL *mysql) +{ + const char *stmt_text; + MYSQL_RES *res; + int rc; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + stmt_text= "CREATE TABLE t1 (a int, b char(255), c decimal)"; + rc= mysql_real_query(mysql, stmt_text, (unsigned long)strlen(stmt_text)); + check_mysql_rc(rc, mysql); + + res= mysql_list_fields(mysql, "t1", "%"); + FAIL_UNLESS(res && mysql_num_fields(res) == 3, "num_fields != 3"); + mysql_free_result(res); + + stmt_text= "DROP TABLE t1"; + rc= mysql_real_query(mysql, stmt_text, (unsigned long)strlen(stmt_text)); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test field flags (verify .NET provider) */ + +static int test_field_flags(MYSQL *mysql) +{ + int rc; + MYSQL_RES *result; + MYSQL_FIELD *field; + + // https://jira.mariadb.org/browse/XPT-287 + SKIP_XPAND; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_flags"); + check_mysql_rc(rc, mysql); + + 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))"); + check_mysql_rc(rc, mysql); + + /* with table name included with TRUE column name */ + rc= mysql_query(mysql, "SELECT * FROM test_field_flags"); + check_mysql_rc(rc, mysql); + + result= mysql_use_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + mysql_field_seek(result, 0); + + field= mysql_fetch_field(result); + FAIL_UNLESS(field->flags & NOT_NULL_FLAG && + field->flags & PRI_KEY_FLAG && + field->flags & AUTO_INCREMENT_FLAG, "Wrong flags for field 0"); + + field= mysql_fetch_field(result); + FAIL_UNLESS(field->flags & NOT_NULL_FLAG, "Wrong flags for field 1"); + + field= mysql_fetch_field(result); + FAIL_UNLESS(field->flags & UNIQUE_KEY_FLAG, "Wrong flags for field 2"); + + field= mysql_fetch_field(result); + FAIL_UNLESS(field->flags & MULTIPLE_KEY_FLAG, "Wrong flags for field 3"); + + field= mysql_fetch_field(result); + FAIL_UNLESS(field->flags & NOT_NULL_FLAG, "Wrong flags for field 4"); + + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_flags"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test real and alias names */ + +static int test_field_names(MYSQL *mysql) +{ + int rc; + MYSQL_RES *result; + + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_names1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_names2"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_field_names1(id int, name varchar(50))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_field_names2(id int, name varchar(50))"); + check_mysql_rc(rc, mysql); + + /* with table name included with TRUE column name */ + rc= mysql_query(mysql, "SELECT id as 'id-alias' FROM test_field_names1"); + check_mysql_rc(rc, mysql); + + result= mysql_use_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 0, "rowcount != 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"); + check_mysql_rc(rc, mysql); + + result= mysql_use_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 0, "rowcount != 0"); + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_names1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_field_names2"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test FUNCTION field info / DATE_FORMAT() table_name . */ + +static int test_func_fields(MYSQL *mysql) +{ + int rc; + MYSQL_RES *result; + MYSQL_FIELD *field; + + + rc= mysql_autocommit(mysql, TRUE); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_dateformat"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE test_dateformat(id int, \ + ts timestamp)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO test_dateformat(id) values(10)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT ts FROM test_dateformat"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + field= mysql_fetch_field(result); + FAIL_IF(!field, "Invalid field"); + FAIL_UNLESS(strcmp(field->table, "test_dateformat") == 0, "field->table != 'test_dateformat'"); + + field= mysql_fetch_field(result); + FAIL_IF(field, "no more fields expected"); + + mysql_free_result(result); + + /* DATE_FORMAT */ + rc= mysql_query(mysql, "SELECT DATE_FORMAT(ts, '%Y') AS 'venu' FROM test_dateformat"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + field= mysql_fetch_field(result); + FAIL_IF(!field, "Invalid field"); + FAIL_UNLESS(field->table[0] == '\0', "field->table != ''"); + + field= mysql_fetch_field(result); + FAIL_IF(field, "no more fields expected"); + + mysql_free_result(result); + + /* FIELD ALIAS TEST */ + rc= mysql_query(mysql, "SELECT DATE_FORMAT(ts, '%Y') AS 'YEAR' FROM test_dateformat"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + field= mysql_fetch_field(result); + FAIL_IF(!field, "Invalid field"); + FAIL_UNLESS(strcmp(field->name, "YEAR") == 0, "name != 'YEAR'"); + FAIL_UNLESS(field->org_name[0] == '\0', "org_name != ''"); + + field= mysql_fetch_field(result); + FAIL_IF(field, "no more fields expected"); + + mysql_free_result(result); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_dateformat"); + check_mysql_rc(rc, mysql); + return OK; +} + +/* Test mysql_list_fields() */ + +static int test_list_fields(MYSQL *mysql) +{ + MYSQL_RES *result; + int rc; + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1(c1 int primary key auto_increment, c2 char(10) default 'mysql')"); + check_mysql_rc(rc, mysql); + + result= mysql_list_fields(mysql, "t1", NULL); + FAIL_IF(!result, "Invalid result set"); + + rc= 0; + while (mysql_fetch_row(result)) + rc++; + FAIL_UNLESS(rc == 0, "rowcount != 0"); + + if (verify_prepare_field(result, 0, "c1", "c1", MYSQL_TYPE_LONG, + "t1", "t1", + schema, 11, "0")) + goto error; + + if (verify_prepare_field(result, 1, "c2", "c2", MYSQL_TYPE_STRING, + "t1", "t1", + schema, 10, "mysql")) + goto error; + + mysql_free_result(result); + check_mysql_rc(mysql_query(mysql, "drop table t1"), mysql); + return OK; + +error: + mysql_free_result(result); + check_mysql_rc(mysql_query(mysql, "drop table t1"), mysql); + return FAIL; +} + +/* Test correct max length for MEDIUMTEXT and LONGTEXT columns */ + +static int test_bug9735(MYSQL *mysql) +{ + MYSQL_RES *res; + int rc; + + + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (a mediumtext, b longtext) " + "character set latin1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "select * from t1"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + if (verify_prepare_field(res, 0, "a", "a", MYSQL_TYPE_BLOB, + "t1", "t1", schema, (1U << 24)-1, 0)) + goto error; + if (verify_prepare_field(res, 1, "b", "b", MYSQL_TYPE_BLOB, + "t1", "t1", schema, ~0U, 0)) + goto error; + mysql_free_result(res); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + return OK; +error: + mysql_free_result(res); + rc= mysql_query(mysql, "drop table t1"); + return FAIL; +} + +/* + Check that mysql_next_result works properly in case when one of + the statements used in a multi-statement query is erroneous +*/ + +static int test_bug9992(MYSQL *mysql) +{ + MYSQL_RES* res ; + int rc; + + /* Sic: SHOW DATABASE is incorrect syntax. */ + rc= mysql_query(mysql, "SHOW TABLES; SHOW DATABASE; SELECT 1;"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_UNLESS(res, "Invalid resultset"); + mysql_free_result(res); + rc= mysql_next_result(mysql); + FAIL_UNLESS(rc == 1, "Error expected"); /* Got errors, as expected */ + + return OK; +} + +/* Test the support of multi-statement executions */ + +static int test_multi_statements(MYSQL *mysql) +{ + 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}; + my_bool reconnect= 1; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + /* + First test that we get an error for multi statements + (Because default connection is not opened with CLIENT_MULTI_STATEMENTS) + */ + mysql_local= mysql; + mysql = test_connect(NULL); + rc= mysql_query(mysql, query); /* syntax error */ + FAIL_IF(!rc, "Error expected"); + + rc= mysql_next_result(mysql); + FAIL_UNLESS(rc == -1, "rc != -1"); + rc= mysql_more_results(mysql); + FAIL_UNLESS(rc == 0, "rc != 0"); + + mysql_close(mysql); + mysql= mysql_local; + + mysql_options(mysql_local, MYSQL_OPT_RECONNECT, &reconnect); + + rc= mysql_query(mysql_local, query); + check_mysql_rc(rc, mysql); + + for (count= 0 ; count < array_elements(rows) ; count++) + { + if ((result= mysql_store_result(mysql_local))) + { + mysql_free_result(result); + } + + exp_value= (uint) mysql_affected_rows(mysql_local); + FAIL_IF(rows[count] != exp_value, "row[count] != exp_value"); + if (count != array_elements(rows) -1) + { + rc= mysql_more_results(mysql_local); + FAIL_IF(!rc, "More results expected"); + rc= mysql_next_result(mysql_local); + check_mysql_rc(rc, mysql_local); + } + else + { + rc= mysql_more_results(mysql_local); + FAIL_UNLESS(rc == 0, "rc != 0"); + rc= mysql_next_result(mysql_local); + FAIL_UNLESS(rc == -1, "rc != -1"); + } + } + + /* check that errors abort multi statements */ + + rc= mysql_query(mysql_local, "select 1+1+a;select 1+1"); + FAIL_IF(!rc, "Error expected"); + rc= mysql_more_results(mysql_local); + FAIL_UNLESS(rc == 0, "rc != 0"); + rc= mysql_next_result(mysql_local); + FAIL_UNLESS(rc == -1, "rc != -1"); + + rc= mysql_query(mysql_local, "select 1+1;select 1+1+a;select 1"); + check_mysql_rc(rc, mysql); + result= mysql_store_result(mysql_local); + FAIL_IF(!result, "Invalid result set"); + mysql_free_result(result); + rc= mysql_more_results(mysql_local); + FAIL_UNLESS(rc == 1, "rc != 1"); + rc= mysql_next_result(mysql_local); + FAIL_UNLESS(rc > 0, "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"); + check_mysql_rc(rc, mysql); + result= mysql_store_result(mysql_local); + FAIL_IF(!result, "Invalid result set"); + 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"); + check_mysql_rc(rc, mysql); + result= mysql_store_result(mysql_local); + mysql_free_result(result); + + rc= mysql_next_result(mysql_local); + FAIL_UNLESS(rc > 0, "rc <= 0"); + + rc= mysql_next_result(mysql_local); + FAIL_UNLESS(rc < 0, "rc >= 0"); + + return OK; +} + +static int test_conc160(MYSQL *mysql) +{ + MYSQL_RES *result; + MYSQL_FIELD *field; + int rc; + + rc= mysql_query(mysql, "SELECT cast(1.234 AS DECIMAL)"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + field= mysql_fetch_field(result); + + FAIL_UNLESS(field->flags & NUM_FLAG, "Numeric flag not set"); + + mysql_free_result(result); + return OK; +} + + + +struct my_tests_st my_tests[] = { + {"test_conc160", test_conc160, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"client_store_result", client_store_result, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"client_use_result", client_use_result, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_free_result", test_free_result, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_free_store_result", test_free_store_result, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_store_result", test_store_result, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_store_result1", test_store_result1, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_store_result2", test_store_result2, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug11718", test_bug11718, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug19671", test_bug19671, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug21726", test_bug21726, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug6761", test_bug6761, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_field_flags", test_field_flags, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_field_names", test_field_names, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_func_fields", test_func_fields, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_list_fields", test_list_fields, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug9735", test_bug9735, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, + {"test_bug9992", test_bug9992, TEST_CONNECTION_NEW, CLIENT_MULTI_STATEMENTS, NULL, NULL}, + {"test_multi_statements", test_multi_statements, TEST_CONNECTION_NEW, CLIENT_MULTI_STATEMENTS, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/rpl_api.c b/libmariadb/unittest/libmariadb/rpl_api.c new file mode 100644 index 00000000..f65a2915 --- /dev/null +++ b/libmariadb/unittest/libmariadb/rpl_api.c @@ -0,0 +1,363 @@ +/* +Copyright (c) 2018 MariaDB Corporation AB + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +/** + Some basic tests of the client API. +*/ + +#include "my_test.h" +#include "mariadb_rpl.h" + +static int test_rpl_async(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + MYSQL_RES *result; + MYSQL_ROW row; + MARIADB_RPL_EVENT *event= NULL; + MARIADB_RPL *rpl; + int events= 0, rc; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "SELECT @@log_bin"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + if (!atoi(row[0])) + rc= SKIP; + mysql_free_result(result); + + if (rc == SKIP) + { + diag("binary log disabled -> skip"); + mysql_close(mysql); + return SKIP; + } + + rpl = mariadb_rpl_init(mysql); + + mysql_query(mysql, "SET @mariadb_slave_capability=4"); + mysql_query(mysql, "SET NAMES latin1"); + mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET NAMES utf8"); + mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); + rpl->server_id= 12; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + if (mariadb_rpl_open(rpl)) + return FAIL; + + /* We run rpl_api as very last test, too make sure + binary log contains > 10000 events. + */ + while((event= mariadb_rpl_fetch(rpl, event)) && events < 10000) + { + events++; + } + mariadb_free_rpl_event(event); + mariadb_rpl_close(rpl); + mysql_close(mysql); + return OK; +} + +static int test_rpl_semisync(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + MYSQL_RES *result; + MYSQL_ROW row; + MARIADB_RPL_EVENT *event= NULL; + MARIADB_RPL *rpl; + int events= 0, rc; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "SELECT @@log_bin"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + if (!atoi(row[0])) + rc= SKIP; + mysql_free_result(result); + + if (rc == SKIP) + { + diag("binary log disabled -> skip"); + mysql_close(mysql); + return SKIP; + } + + rpl = mariadb_rpl_init(mysql); + + mariadb_rpl_optionsv(rpl, MARIADB_RPL_HOST, "foo"); + + mysql_query(mysql, "SET @mariadb_slave_capability=4"); + mysql_query(mysql, "SET NAMES latin1"); + mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET NAMES utf8"); + mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); + rpl->server_id= 12; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + mysql_query(mysql, "SET @mariadb_slave_capability=4"); + mysql_query(mysql, "SET NAMES latin1"); + mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET NAMES utf8"); + mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); + rpl->server_id= 12; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + if (mariadb_rpl_open(rpl)) + return FAIL; + + /* We run rpl_api as very last test, too make sure + binary log contains > 10000 events. + */ + while((event= mariadb_rpl_fetch(rpl, event)) && events < 10000) + { + events++; + } + mariadb_free_rpl_event(event); + mariadb_rpl_close(rpl); + mysql_close(mysql); + return OK; +} + +static int test_conc467(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + MYSQL_RES *result; + MYSQL_ROW row; + MARIADB_RPL_EVENT *event= NULL; + MARIADB_RPL *rpl; + int rc; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "SELECT @@log_bin"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + if (!atoi(row[0])) + rc= SKIP; + mysql_free_result(result); + + if (rc == SKIP) + { + diag("binary log disabled -> skip"); + mysql_close(mysql); + return SKIP; + } + + /* Force to create a log rotate event */ + rc= mysql_query(mysql, "FLUSH logs"); + check_mysql_rc(rc, mysql); + + rpl = mariadb_rpl_init(mysql); + + mysql_query(mysql, "SET @mariadb_slave_capability=4"); + mysql_query(mysql, "SET NAMES latin1"); + mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET NAMES utf8"); + mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); + rpl->server_id= 12; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + if (mariadb_rpl_open(rpl)) + return FAIL; + + if (!(event= mariadb_rpl_fetch(rpl, event))) + rc= FAIL; + else + { + if (!rpl->filename) + { + diag("error: filename not set"); + rc= FAIL; + } + else + diag("filename: %.*s", (int)rpl->filename_length, rpl->filename); + } + + mariadb_free_rpl_event(event); + mariadb_rpl_close(rpl); + mysql_close(mysql); + return rc; +} + +static int test_conc592(MYSQL *my __attribute__((unused))) +{ + MARIADB_RPL *rpl; + MYSQL *mysql= mysql_init(NULL); + MYSQL *mysql_check= mysql_init(NULL); + const char *host= "myhost"; + MYSQL_RES *result; + MYSQL_ROW row; + int rc; + int found= 0; + + + mysql_optionsv(mysql, MARIADB_OPT_RPL_REGISTER_REPLICA, host, 123); + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + if (!my_test_connect(mysql_check, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "SELECT @@log_bin"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + if (!atoi(row[0])) + rc= SKIP; + mysql_free_result(result); + + if (rc == SKIP) + { + diag("binary log disabled -> skip"); + mysql_close(mysql); + return SKIP; + } + + rpl = mariadb_rpl_init(mysql); + + mysql_query(mysql, "SET @mariadb_slave_capability=4"); + mysql_query(mysql, "SET NAMES latin1"); + mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET NAMES utf8"); + mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); + mysql_query(mysql, "SET @rpl_semi_sync_slave=1"); + rpl->server_id= 12; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + if (mariadb_rpl_open(rpl)) + return FAIL; + + rc= mysql_query(mysql_check, "SHOW SLAVE HOSTS"); + check_mysql_rc(rc, mysql_check); + + result= mysql_store_result(mysql_check); + + while ((row= mysql_fetch_row(result))) + if (!strcmp(row[1], host)) + found= 1; + + mysql_free_result(result); + mysql_close(mysql); + mysql_close(mysql_check); + + if (!found) + { + diag("Host '%s' not found in replica list", host); + return FAIL; + } + + return OK; +} + +struct my_tests_st my_tests[] = { + {"test_conc592", test_conc592, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_rpl_async", test_rpl_async, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_rpl_semisync", test_rpl_semisync, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc467", test_conc467, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/sp.c b/libmariadb/unittest/libmariadb/sp.c new file mode 100644 index 00000000..6aeb557e --- /dev/null +++ b/libmariadb/unittest/libmariadb/sp.c @@ -0,0 +1,91 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" + +/* Bug#15752 "Lost connection to MySQL server when calling a SP from C API" */ + +static int test_bug15752(MYSQL *mysql) +{ + int rc, i; + const int ITERATION_COUNT= 100; + const char *query= "CALL p1()"; + + + rc= mysql_query(mysql, "drop procedure if exists p1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create procedure p1() select 1"); + check_mysql_rc(rc, mysql); + + rc= mysql_real_query(mysql, SL(query)); + check_mysql_rc(rc, mysql); + mysql_free_result(mysql_store_result(mysql)); + + rc= mysql_real_query(mysql, SL(query)); + FAIL_UNLESS(rc && mysql_errno(mysql) == CR_COMMANDS_OUT_OF_SYNC, "Error expected"); + + rc= mysql_next_result(mysql); + check_mysql_rc(rc, mysql); + + mysql_free_result(mysql_store_result(mysql)); + + rc= mysql_next_result(mysql); + FAIL_IF(rc != -1, "rc != -1"); + + for (i = 0; i < ITERATION_COUNT; i++) + { + rc= mysql_real_query(mysql, SL(query)); + check_mysql_rc(rc, mysql); + mysql_free_result(mysql_store_result(mysql)); + rc= mysql_next_result(mysql); + check_mysql_rc(rc, mysql); + mysql_free_result(mysql_store_result(mysql)); + rc= mysql_next_result(mysql); + FAIL_IF(rc != -1, "rc != -1"); + + } + rc= mysql_query(mysql, "drop procedure p1"); + check_mysql_rc(rc, mysql); + + return OK; +} + + + + +struct my_tests_st my_tests[] = { + {"test_bug15752", test_bug15752, TEST_CONNECTION_NEW, 0, NULL , NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/ssl.c b/libmariadb/unittest/libmariadb/ssl.c new file mode 100644 index 00000000..0cabe7c4 --- /dev/null +++ b/libmariadb/unittest/libmariadb/ssl.c @@ -0,0 +1,1424 @@ +/************************************************************************************ + Copyright (C) 2012 Monty Program AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see <http://www.gnu.org/licenses> + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + *************************************************************************************/ +#if defined(WIN32) && defined(HEAP_CHECK) +#define _CRTDBG_MAP_ALLOC +#include <stdlib.h> +#include <crtdbg.h> +#endif + +#include "my_test.h" +#include <ma_pthread.h> +#ifdef HAVE_OPENSSL +#include <openssl/opensslv.h> +#include <openssl/ssl.h> +#endif + +#define FNLEN 4096 + +static int skip_ssl= 1; +static uchar have_openssl= 1; +static unsigned char have_tls13= 0; + +const char *ssluser= "ssluser"; +const char *sslpw= "sslpw"; +char sslhost[128]; +char sslcert[FNLEN]; +char sslcombined[FNLEN]; +char sslkey[FNLEN]; +char sslkey_enc[FNLEN]; +char sslca[FNLEN]; +char sslcrl[FNLEN]; +char ssl_cert_finger_print[129]= {0}; +char bad_cert_finger_print[]= "00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:01:23:45:67"; + +pthread_mutex_t LOCK_test; + +void read_fingerprint() +{ + FILE *f= fopen(CERT_PATH "/server-cert.sha1", "r"); + if (f) + { + if (!fscanf(f, "%128s", ssl_cert_finger_print)) + ssl_cert_finger_print[0]= 0; + fclose(f); + } +} + +int check_skip_ssl() +{ + const char *ssldir= NULL; +#ifndef HAVE_TLS + diag("client library built without OpenSSL support -> skip"); + return 1; +#endif + if (skip_ssl) + { + diag("server doesn't support SSL -> skip"); + return 1; + } + if (!(ssldir= getenv("SECURE_LOAD_PATH"))) + { + ssldir= CERT_PATH; + if (!strlen(ssldir)) + { + diag("certificate directory not found"); + return 1; + } + } + snprintf(sslcert, FNLEN - 1, "%s/%s", ssldir, "client-cert.pem"); + snprintf(sslcombined, FNLEN - 1, "%s/%s", ssldir, "client-certkey.pem"); + snprintf(sslkey, FNLEN - 1, "%s/%s", ssldir, "client-key.pem"); + snprintf(sslkey_enc, FNLEN - 1, "%s/%s", ssldir, "client-key-enc.pem"); + snprintf(sslca, FNLEN - 1, "%s/%s", ssldir, "cacert.pem"); + return 0; +} + +static int check_cipher(MYSQL *mysql) +{ + char *cipher= (char *)mysql_get_ssl_cipher(mysql); + if (!cipher) + return 1; + diag("cipher: %s", cipher); + + return 0; +} + +static int create_ssl_user(const char *ssluser, my_bool is_X509) +{ + int rc; + char query[1024]; + MYSQL *mysql= mysql_init(NULL); + + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(mysql)); + + sprintf(query, "DROP USER '%s'@'%s'", ssluser, this_host); + rc= mysql_query(mysql, query); + + sprintf(query, "CREATE USER '%s'@'%s' IDENTIFIED BY '%s'", ssluser, this_host, sslpw); + rc= mysql_query(mysql, query); + check_mysql_rc(rc,mysql); + + sprintf(query, "GRANT ALL ON %s.* TO '%s'@'%s' REQUIRE %s", schema, ssluser, this_host, is_X509 ? "X509" : "SSL"); + rc= mysql_query(mysql, query); + check_mysql_rc(rc,mysql); + rc= mysql_query(mysql, "FLUSH PRIVILEGES"); + check_mysql_rc(rc,mysql); + + mysql_close(mysql); + + return rc; +} + +static int test_ssl(MYSQL *mysql) +{ + int rc; + unsigned int iversion; + MYSQL_RES *res; + MYSQL_ROW row; + char *tls_library; + MYSQL *my= mysql_init(NULL); + + mysql_ssl_set(my,0, 0, 0, 0, 0); + + create_ssl_user("ssluser", 0); + + FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0), mysql_error(my)); + + mariadb_get_infov(my, MARIADB_CONNECTION_TLS_VERSION_ID, &iversion); + diag("iversion: %d", iversion); + if (iversion == 4) + have_tls13= 1; + + mysql_close(my); + + rc= mysql_query(mysql, "SELECT @@have_ssl, @@have_openssl"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(!res, mysql_error(mysql)); + + while ((row= mysql_fetch_row(res))) + { + if (!strcmp(row[0], "YES")) + skip_ssl= 0; + if (strcmp(row[1], "YES")) + have_openssl= 0; + diag("SSL: %s", row[0]); + } + mysql_free_result(res); + + /* In MySQL we need to check tls_version */ + if (!mariadb_connection(mysql)) + { + rc= mysql_query(mysql, "select locate('v1.2', @@tls_version) > 0"); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(!res, mysql_error(mysql)); + + if ((row= mysql_fetch_row(res))) + { + if (row[0] && row[0][0] == '0') + have_openssl= 0; + } + mysql_free_result(res); + } + diag("OpenSSL: %d", have_openssl); + + mariadb_get_infov(NULL, MARIADB_TLS_LIBRARY, &tls_library); + diag("SSL library: %s", tls_library); + + sslhost[0]= 0; + + if (!skip_ssl) + { + char *p; + + rc= mysql_query(mysql, "SELECT CURRENT_USER()"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + diag("user: %s", row[0]); + if ((p= strchr(row[0], '@'))) + strcpy(sslhost, p+1); + mysql_free_result(res); + } + + return OK; +} + +static int test_ssl_cipher(MYSQL *unused __attribute__((unused))) +{ + MYSQL *my; + MYSQL_RES *res; + MYSQL_ROW row; + int rc; + + if (check_skip_ssl()) + return SKIP; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_ssl_set(my,0, 0, sslca, 0, 0); + + FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0), mysql_error(my)); + + rc= mysql_query(my, "SHOW session status like 'Ssl_version'"); + check_mysql_rc(rc, my); + res= mysql_store_result(my); + row= mysql_fetch_row(res); + diag("%s: %s", row[0], row[1]); + diag("cipher: %s", mysql_get_ssl_cipher(my)); + mysql_free_result(res); + + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); + mysql_close(my); + return OK; +} + +static int test_conc95(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (check_skip_ssl()) + return SKIP; + + create_ssl_user("ssluser1", 1); + + mysql= mysql_init(NULL); + mysql_ssl_set(mysql, + sslkey, + sslcert, + NULL, + NULL, + NULL); + + if (!mysql_real_connect(mysql, hostname, "ssluser1", sslpw, schema, + ssl_port, socketname, 0)) + { + diag("could not establish x509 connection. Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + mysql_close(mysql); + return OK; +} + +static int test_multi_ssl_connections(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql[50], *my; + int i, rc; + int old_connections= 0, new_connections= 0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (check_skip_ssl()) + return SKIP; + + diag("Test doesn't work with yassl"); + return SKIP; + + create_ssl_user(ssluser, 0); + + my= mysql_init(NULL); + FAIL_IF(!my,"mysql_init() failed"); + FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0), mysql_error(my)); + + rc= mysql_query(my, "SHOW STATUS LIKE 'Ssl_accepts'"); + check_mysql_rc(rc, my); + + res= mysql_store_result(my); + if ((row= mysql_fetch_row(res))) + old_connections= atoi(row[1]); + mysql_free_result(res); + + for (i=0; i < 50; i++) + { + mysql[i]= mysql_init(NULL); + FAIL_IF(!mysql[i],"mysql_init() failed"); + + mysql_ssl_set(mysql[i], 0, 0, sslca, 0, 0); + + mysql_real_connect(mysql[i], hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + if (mysql_errno(mysql[i])) + { + diag("loop: %d error: %d %s", i, mysql_errno(mysql[i]), mysql_error(mysql[i])); + return FAIL; + } + + FAIL_IF(check_cipher(mysql[i]) != 0, "Invalid cipher"); + } + for (i=0; i < 50; i++) + mysql_close(mysql[i]); + + rc= mysql_query(my, "SHOW STATUS LIKE 'Ssl_accepts'"); + check_mysql_rc(rc, my); + + res= mysql_store_result(my); + if ((row= mysql_fetch_row(res))) + new_connections= atoi(row[1]); + mysql_free_result(res); + + mysql_close(my); + + diag("%d SSL connections processed", new_connections - old_connections); + FAIL_IF(new_connections - old_connections < 50, "new_connections should be at least old_connections + 50"); + return OK; +} + +#ifndef WIN32 +static void ssl_thread(void *unused __attribute__((unused))) +#else +DWORD WINAPI ssl_thread(void *dummy) +#endif +{ + MYSQL *mysql= NULL; + + mysql_thread_init(); + + if (!(mysql= mysql_init(NULL))) + { + goto end; + } + mysql_ssl_set(mysql, 0, 0, sslca, 0, 0); + + if(!mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0)) + { + diag(">Error: %s", mysql_error(mysql)); + goto end; + } + + pthread_mutex_lock(&LOCK_test); + mysql_query(mysql, "UPDATE ssltest SET a=a+1"); + pthread_mutex_unlock(&LOCK_test); + +end: + if(mysql) + mysql_close(mysql); + mysql_thread_end(); +#ifdef _WIN32 + return 0; +#endif +} + +static int test_ssl_threads(MYSQL *mysql) +{ + int i, rc; +#ifndef WIN32 + pthread_t threads[50]; +#else + HANDLE hthreads[50]; + DWORD dthreads[50]; +#endif + MYSQL_RES *res; + MYSQL_ROW row; + + if (check_skip_ssl()) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF exists ssltest"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE ssltest (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT into ssltest VALUES (0)"); + check_mysql_rc(rc, mysql); + pthread_mutex_init(&LOCK_test, NULL); + + pthread_mutex_init(&LOCK_test, NULL); + + for (i=0; i < 50; i++) + { +#ifndef WIN32 + pthread_create(&threads[i], NULL, (void *)ssl_thread, NULL); +#else + hthreads[i]= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ssl_thread, NULL, 0, &dthreads[i]); + if (hthreads[i]==NULL) + diag("error while starting thread"); +#endif + } + for (i=0; i < 50; i++) +#ifndef WIN32 + pthread_join(threads[i], NULL); +#else + WaitForSingleObject(hthreads[i], INFINITE); +#endif + + pthread_mutex_destroy(&LOCK_test); + + rc= mysql_query(mysql, "SELECT a FROM ssltest"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + diag("Found: %s", row[0]); + FAIL_IF(strcmp(row[0], "50") != 0, "Expected 50"); + mysql_free_result(res); + rc= mysql_query(mysql, "DROP TABLE IF exists ssltest"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_phpbug51647(MYSQL *unused __attribute__((unused))) +{ + MYSQL* mysql; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, sslkey, + sslcert, + sslca, 0, 0); + + FAIL_IF(!mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + diag("%s", mysql_get_ssl_cipher(mysql)); + mysql_close(mysql); + + return OK; +} + +static int test_password_protected(MYSQL *unused __attribute__((unused))) +{ + MYSQL* mysql; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, sslkey_enc, + sslcert, + sslca, 0, 0); + + mysql_options(mysql, MARIADB_OPT_TLS_PASSPHRASE, "qwerty"); + + FAIL_IF(!mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + diag("%s", mysql_get_ssl_cipher(mysql)); + mysql_close(mysql); + + return OK; +} + + +static int test_conc50(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, NULL, "./non_exisiting_cert.pem", NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + diag("Error: %d %s", mysql_errno(mysql), mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql) != 2026, "Expected errno 2026"); + mysql_close(mysql); + + return OK; +} + +static int test_conc50_1(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (check_skip_ssl()) + return SKIP; + + if (!have_openssl) + { + diag("Server with OpenSSL required"); + return SKIP; + } + + create_ssl_user(ssluser, 0); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, NULL, sslca, NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + if (mysql_errno(mysql)) + diag("Error: %d %s", mysql_errno(mysql), mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql), "No error expected"); + + mysql_close(mysql); + + return OK; +} + +static int test_conc50_2(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, NULL, "./non_exisiting_cert.pem", NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + FAIL_IF(mysql_errno(mysql) != 2026, "Expected errno 2026"); + mysql_close(mysql); + + return OK; +} + +static int test_conc127(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + diag("test disabled - for testing disable other tests or run this test as first test"); + return SKIP; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, NULL, "./non_exisiting.pem", NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + diag("Error: %s", mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql) == 0, "Error expected (invalid certificate)"); + mysql_close(mysql); + + return OK; +} + +static int test_conc50_3(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (check_skip_ssl()) + return SKIP; + + create_ssl_user(ssluser, 0); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + FAIL_IF(!mysql_errno(mysql), "Error expected, SSL connection required!"); + mysql_close(mysql); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, NULL, sslca, NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + diag("Error: %s<", mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql), "No error expected"); + mysql_close(mysql); + + return OK; +} + +static int test_conc50_4(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, sslca, NULL, NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + FAIL_IF(!mysql_errno(mysql) , "Error expected"); + mysql_close(mysql); + + return OK; +} + +static int verify_ssl_server_cert(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + uint verify= 1; + + if (check_skip_ssl()) + return SKIP; + + if (!hostname || !strcmp(hostname, "localhost")) + return SKIP; + + SKIP_TRAVIS(); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, NULL, sslca, NULL, NULL); + mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + + FAIL_IF(!mysql_errno(mysql), "Expected error"); + diag("Error (expected): %s", mysql_error(mysql)); + mysql_close(mysql); + + return OK; +} + +static int test_bug62743(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, "dummykey", NULL, NULL, NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + diag("Error: %s", mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql) != 2026, "Expected errno 2026"); + mysql_close(mysql); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, sslkey, NULL, NULL, NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + diag("Error with key: %s", mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql) != 2026, "Expected errno 2026"); + mysql_close(mysql); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, sslkey, + sslcert, NULL, NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + FAIL_IF(mysql_errno(mysql) != 0, "Expected no error"); + mysql_close(mysql); + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, sslkey, "blablubb", NULL, NULL, NULL); + + mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0); + diag("Error with cert: %s", mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql) == 0, "Expected error"); + mysql_close(mysql); + + return OK; +} + +#ifndef WIN32 +int thread_conc102(void) +#else +DWORD WINAPI thread_conc102(void) +#endif +{ + MYSQL *mysql; + int rc; + MYSQL_RES *res; + mysql_thread_init(); + mysql= mysql_init(NULL); + + mysql_ssl_set(mysql, sslkey, + sslcert, + sslca, + NULL, NULL); + mysql_ssl_set(mysql,0, 0, sslca, 0, 0); + + if(!mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0)) + { + diag(">Error: %s", mysql_error(mysql)); + goto end; + } + if (!mysql_get_ssl_cipher(mysql)) + { + diag("Error: No ssl connection"); + goto end; + } + pthread_mutex_lock(&LOCK_test); + rc= mysql_query(mysql, "UPDATE t_conc102 SET a=a+1"); + check_mysql_rc(rc, mysql); + pthread_mutex_unlock(&LOCK_test); + check_mysql_rc(rc, mysql); + if ((res= mysql_store_result(mysql))) + mysql_free_result(res); +end: + mysql_close(mysql); + mysql_thread_end(); + return 0; +} + +static int test_conc_102(MYSQL *mysql) +{ + + int rc; + int i; + MYSQL_ROW row; + MYSQL_RES *res; +#ifndef WIN32 + pthread_t threads[50]; +#else + HANDLE hthreads[50]; + DWORD threads[50]; +#endif + + if (check_skip_ssl()) + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t_conc102"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t_conc102 ( a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t_conc102 VALUES (0)"); + check_mysql_rc(rc, mysql); + pthread_mutex_init(&LOCK_test, NULL); + + for (i=0; i < 50; i++) + { +#ifndef WIN32 + pthread_create(&threads[i], NULL, (void *)thread_conc102, NULL); +#else + hthreads[i]= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_conc102, NULL, 0, &threads[i]); + if (hthreads[i]==NULL) + diag("error while starting thread"); +#endif + } + for (i=0; i < 50; i++) + { +#ifndef WIN32 + pthread_join(threads[i], NULL); +#else + WaitForSingleObject(hthreads[i], INFINITE); +#endif + } + pthread_mutex_destroy(&LOCK_test); + rc= mysql_query(mysql, "SELECT a FROM t_conc102"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + diag("Found: %s", row[0]); + FAIL_IF(strcmp(row[0], "50") != 0, "Expected 50"); + mysql_free_result(res); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t_conc102"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_ssl_fp(MYSQL *unused __attribute__((unused))) +{ + MYSQL *my; + MYSQL_RES *res; + MYSQL_ROW row; + int rc; + + if (check_skip_ssl()) + return SKIP; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_ssl_set(my,0, 0, sslca, 0, 0); + + mysql_options(my, MARIADB_OPT_SSL_FP, bad_cert_finger_print); + + FAIL_IF(mysql_real_connect(my, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(my)); + + mysql_options(my, MARIADB_OPT_SSL_FP, ssl_cert_finger_print); + + FAIL_IF(!mysql_real_connect(my, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(my)); + + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); + + rc= mysql_query(my, "SET @a:=1"); + check_mysql_rc(rc, my); + + rc= mysql_query(my, "SELECT @a"); + check_mysql_rc(rc, my); + + if ((res= mysql_store_result(my))) + { + row= mysql_fetch_row(res); + diag("@a:=%s", row[0]); + mysql_free_result(res); + } + + mysql_close(my); + return OK; +} + +static int test_ssl_fp_list(MYSQL *unused __attribute__((unused))) +{ + MYSQL *my; + + if (check_skip_ssl()) + return SKIP; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_ssl_set(my,0, 0, sslca, 0, 0); + + mysql_options(my, MARIADB_OPT_SSL_FP_LIST, CERT_PATH "/server-cert.sha1"); + + if(!mysql_real_connect(my, hostname, username, password, schema, + ssl_port, socketname, 0)) + { + diag("Error: %s", mysql_error(my)); + mysql_close(my); + return FAIL; + } + + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); + mysql_close(my); + return OK; +} + +static int test_ssl_version(MYSQL *unused __attribute__((unused))) +{ + unsigned int iversion; + char *version, *library; + MYSQL *my; + + if (check_skip_ssl()) + return SKIP; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_ssl_set(my,0, 0, sslca, 0, 0); + FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0), mysql_error(my)); + + diag("cipher: %s", mysql_get_ssl_cipher(my)); + mariadb_get_infov(my, MARIADB_CONNECTION_TLS_VERSION_ID, &iversion); + diag("protocol: %d", iversion); + mariadb_get_infov(my, MARIADB_CONNECTION_TLS_VERSION, &version); + diag("protocol: %s", version); + + mariadb_get_infov(my, MARIADB_TLS_LIBRARY, &library); + diag("library: %s", library); + + mysql_close(my); + + return OK; +} + +#ifdef HAVE_SCHANNEL +static int test_schannel_cipher(MYSQL *unused __attribute__((unused))) +{ + MYSQL *my; + unsigned int cipher_strength= 256; + + if (check_skip_ssl()) + return SKIP; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_ssl_set(my,0, 0, sslca, 0, 0); + mysql_options(my, MARIADB_OPT_TLS_CIPHER_STRENGTH, &cipher_strength); + FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, + ssl_port, socketname, 0), mysql_error(my)); + + diag("cipher: %s", mysql_get_ssl_cipher(my)); + + mysql_close(my); + + return OK; +} + +#endif + +#if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) + +static int test_cipher_mapping(MYSQL *unused __attribute__((unused))) +{ + unsigned int i=0; + const char *ciphers[]= { "DHE-RSA-AES256-GCM-SHA384", "DHE-RSA-AES256-SHA256", "DHE-RSA-AES256-SHA", +#ifdef TEST_CAMELLIA_CIPHER + "DHE-RSA-CAMELLIA256-SHA", "CAMELLIA256-SHA", + "DHE-RSA-CAMELLIA128-SHA", "CAMELLIA128-SHA", +#endif +#ifdef TEST_DES_CIPHER + "EDH-RSA-DES-CBC3-SHA", + "DES-CBC3-SHA", +#endif + "AES256-GCM-SHA384", "AES256-SHA256", "AES256-SHA", + "DHE-RSA-AES128-GCM-SHA256", "DHE-RSA-AES128-SHA256", "DHE-RSA-AES128-SHA", + "AES128-GCM-SHA256", "AES128-SHA256", "AES128-SHA", + "DHE-RSA-AES256-SHA", "AES256-SHA", + NULL }; + + diag("This test depends on OpenSSL version - since several ciphers might not be available"); + return SKIP; + + if (check_skip_ssl()) + return SKIP; + + if (!have_openssl) + { + diag("test requires Server with OpenSSL"); + return SKIP; + } + + while (ciphers[i] != NULL) + { + MYSQL *mysql= mysql_init(NULL); + MYSQL_ROW row; + MYSQL_RES *res; + char c[100]; + int rc; + const char *cipher; + + mysql_options(mysql, MYSQL_OPT_TLS_VERSION, "TLSv1.0,TLSv1.1,TLSv1.2"); + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, ciphers[i]); + diag("%s", ciphers[i]); + + mysql->options.use_ssl= 1; + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + if (!(cipher= mysql_get_ssl_cipher(mysql)) || + strcmp(ciphers[i], cipher) != 0) + { + diag("cipher %s differs: (%s)", ciphers[i], cipher); + mysql_close(mysql); + goto cont; + } + else + { + rc= mysql_query(mysql, "SHOW STATUS LIKE 'ssl_cipher'"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + row= mysql_fetch_row(res); + strcpy(c, row[1]); + mysql_free_result(res); + mysql_close(mysql); + if (strcmp(ciphers[i], c) != 0) + { + diag("expected: %s instead of %s", ciphers[i], c); + /* depending if server supports ECC, ciphers may differ, + so we don't return failure here */ + } + } +cont: + i++; + } + return OK; +} +#endif + +static int test_openssl_1(MYSQL *mysql) +{ + int rc; + MYSQL *my; + uchar val= 1; + char query[1024]; + int i; + + if (check_skip_ssl()) + return SKIP; + + if (have_tls13) + return SKIP; + + if (!mariadb_connection(mysql)) + return SKIP; + + for (i=1; i < 6; i++) + { + sprintf(query, "DROP USER 'ssluser%d'@'%s'", i, this_host); + rc= mysql_query(mysql, query); + sprintf(query, "CREATE USER 'ssluser%d'@'%s'", i, this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + } + rc= mysql_query(mysql, "FLUSH PRIVILEGES"); + check_mysql_rc(rc, mysql); + diag("sslusers created"); + + diag("ssluser1"); + sprintf(query, "grant select on %s.* to 'ssluser1'@'%s' require ssl", schema, this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES128-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser1", NULL, schema, + ssl_port, socketname, 0), mysql_error(my)); + FAIL_IF(!mysql_get_ssl_cipher(my), "No TLS connection"); + mysql_close(my); + + my= mysql_init(NULL); + mysql_options(my, MYSQL_OPT_SSL_ENFORCE, &val); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser1", NULL, schema, + ssl_port, socketname, 0), mysql_error(my)); + FAIL_IF(!mysql_get_ssl_cipher(my), "No TLS connection"); + mysql_close(my); + + diag("ssluser2"); + sprintf(query, "grant select on %s.* to 'ssluser2'@'%s' require cipher 'AES256-SHA'", schema, this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + +#ifdef TEST_RANDOM_RESULT +/* ssl_user2: connect with enforce should work */ + my= mysql_init(NULL); + mysql_options(my, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_real_connect(my, hostname, "ssluser2", NULL, schema, + ssl_port, socketname, 0); + if (!mysql_error(my) && + strcmp(mysql_get_ssl_cipher(my), "AES256-SHA")) + { + diag("Expected error or correct cipher"); + return FAIL; + } + mysql_close(my); +#endif + /* ssl_user2: connect with correct cipher */ + diag("ssluser2"); + if (mysql_get_server_version(mysql) >= 100100) + { + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser2", NULL, schema, + ssl_port, socketname, 0), mysql_error(my)); + FAIL_IF(strcmp("AES256-SHA", mysql_get_ssl_cipher(my)) != 0, "expected cipher AES256-SHA"); + mysql_close(my); + } + + /* ssl_user2: connect with wrong cipher should not work */ + diag("ssluser2"); + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES128-SHA"); + FAIL_IF(mysql_real_connect(my, hostname, "ssluser2", NULL, schema, + ssl_port, socketname, 0), "Error expected"); + mysql_close(my); + + + if (!travis_test) + { + sprintf(query, "grant select on %s.* to 'ssluser3'@'%s' require cipher 'AES256-SHA' AND " + " SUBJECT '/C=FI/ST=Helsinki/L=Helsinki/O=MariaDB/CN=client'", schema, this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + /* ssluser3: connect with cipher only */ + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA"); + FAIL_IF(mysql_real_connect(my, hostname, "ssluser3", NULL, schema, + ssl_port, socketname, 0), "Error expected"); + mysql_close(my); + + /* ssluser3 connect with cipher and certs */ + my= mysql_init(NULL); + mysql_ssl_set(my, sslkey, + sslcert, + sslca, + NULL, + "AES256-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser3", NULL, schema, + ssl_port, socketname, 0), mysql_error(my)); + + mysql_close(my); + + sprintf(query, "grant select on %s.* to 'ssluser4'@'%s' require cipher 'AES256-SHA' AND " + " ISSUER '/CN=cacert/C=FI/ST=Helsinki/L=Helsinki/O=MariaDB'", schema, this_host); + rc= mysql_query(mysql, query); + check_mysql_rc(rc, mysql); + + /* ssluser4: connect with cipher only */ + my= mysql_init(NULL); + mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA"); + FAIL_IF(mysql_real_connect(my, hostname, "ssluser4", NULL, schema, + ssl_port, socketname, 0), "Error expected"); + mysql_close(my); + + /* ssluser4 connect with cipher and certs */ + my= mysql_init(NULL); + mysql_ssl_set(my, sslkey, + sslcert, + sslca, + NULL, + "AES256-SHA"); + FAIL_IF(!mysql_real_connect(my, hostname, "ssluser4", NULL, schema, + ssl_port, socketname, 0), mysql_error(my)); + mysql_close(my); + } + diag("drop users"); + for (i=1; i < 6; i++) + { + sprintf(query, "DROP USER 'ssluser%d'@'%s'", i, this_host); + rc= mysql_query(mysql, query); + } + + return OK; +} + +static int test_ssl_timeout(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + my_bool enforce= 1; + int read_timeout= 1; + int rc; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &enforce); + mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, &read_timeout); + mysql->options.use_ssl= 1; + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + diag("cipher: %s\n", mysql_get_ssl_cipher(mysql)); + rc= mysql_query(mysql, "SELECT SLEEP(600)"); + if (!rc) + { + diag("error expected (timeout)"); + return FAIL; + } + + mysql_close(mysql); + return OK; +} + +static int drop_ssl_user(MYSQL *mysql) +{ + int rc; + + rc= mysql_query(mysql, "DELETE FROM mysql.user where user like 'ssl%'"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DELETE FROM mysql.db where user like 'ssl%'"); + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_conc286(MYSQL *unused __attribute__((unused))) +{ + MYSQL *my; + + if (check_skip_ssl()) + return SKIP; + + my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + mysql_options(my, MARIADB_OPT_SSL_FP, ssl_cert_finger_print); + + FAIL_IF(!mysql_real_connect(my, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(my)); + + FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); + + mysql_close(my); + return OK; +} + +static int test_mdev14027(MYSQL *mysql __attribute__((unused))) +{ + char *tls_library; + const char *check_library= +#if defined(HAVE_OPENSSL) +#if defined(HAVE_LIBRESSL) + "LibreSSL"; +#else + "OpenSSL"; +#endif +#elif defined(HAVE_GNUTLS) + "GnuTLS"; +#elif defined(HAVE_SCHANNEL) + "Schannel"; +#else + "Off"; +#endif + mariadb_get_infov(NULL, MARIADB_TLS_LIBRARY, &tls_library); + diag("TLS/SSL library in use: %s\n", tls_library); + if (!strstr(tls_library, check_library)) + { + diag("expected %s, got %s", check_library, tls_library); + return FAIL; + } + return OK; +} + +static int test_mdev14101(MYSQL *my __attribute__((unused))) +{ + struct { + bool do_yassl; + const char *opt_tls_version; + const char *expected; + } combinations[]= { + {1, "TLSv1.1", "TLSv1.1"}, + {1, "TLSv1,TLSv1.1", "TLSv1.1"}, + {0, "TLSv1.2", "TLSv1.2"}, + {0, "TLSv1.1,TLSv1.2", "TLSv1.2"}, + {1, NULL, NULL} + }; + + int i; +#ifdef HAVE_SCHANNEL + bool skip_tlsv12= 1; +#else + bool skip_tlsv12= !have_openssl; +#endif + +#if defined(HAVE_OPENSSL) && defined(TLS1_3_VERSION) + diag("Test fails with TLS v1.3"); + return(SKIP); +#endif + + for (i=0; combinations[i].expected; i++) + { + MYSQL *mysql; + bool val=1; + char *tls_version; + + if (!combinations[i].do_yassl && skip_tlsv12) + break; + + diag("combination %d: %s", i, combinations[i].opt_tls_version); + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(mysql, MARIADB_OPT_TLS_VERSION, combinations[i].opt_tls_version); + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + mariadb_get_infov(mysql, MARIADB_CONNECTION_TLS_VERSION, &tls_version); + diag("options: %s", combinations[i].opt_tls_version); + diag("protocol: %s expected: %s", tls_version, combinations[i].expected); + FAIL_IF(strcmp(combinations[i].expected, tls_version), "Wrong tls_version"); + mysql_close(mysql); + } + return OK; +} + +static int test_conc386(MYSQL *mysql) +{ + mysql= mysql_init(NULL); + mysql_ssl_set(mysql, + sslcombined, + NULL, + NULL, + NULL, + NULL); + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + FAIL_IF(check_cipher(mysql) != 0, "Invalid cipher"); + mysql_close(mysql); + return OK; +} + +#ifndef HAVE_SCHANNEL +static int test_ssl_verify(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql; + my_bool verify= 1, enforce= 1; + + if (check_skip_ssl()) + return SKIP; + + /* verify, using system ca should fail with self signed certificate */ + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &enforce); + mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + FAIL_IF(mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0), "Error expected"); + diag("error expected: %s\n", mysql_error(mysql)); + mysql_close(mysql); + + /* verify, using system ca should pass */ + + /* Disable this for now, since for some unknown reason it fails on travis + setenv("SSL_CERT_DIR", CERT_PATH, 1); + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &enforce); + mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(mysql)); + mysql_close(mysql); + unsetenv("SSL_CERT_DIR"); + */ + + /* verify against local ca, this should pass */ + mysql= mysql_init(NULL); + mysql_ssl_set(mysql,0, 0, sslca, 0, 0); + mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + mysql_close(mysql); + + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &enforce); + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + ssl_port, socketname, 0), mysql_error(mysql)); + + diag("cipher: %s", mysql_get_ssl_cipher(mysql)); + mysql_close(mysql); + return OK; +} +#endif + +struct my_tests_st my_tests[] = { + {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#ifndef HAVE_SCHANNEL + {"test_ssl_verify", test_ssl_verify, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#endif + {"test_mdev14101", test_mdev14101, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_mdev14027", test_mdev14027, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc286", test_conc286, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_ssl_timeout", test_ssl_timeout, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_openssl_1", test_openssl_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#ifndef HAVE_SCHANNEL + {"test_cipher_mapping", test_cipher_mapping, TEST_CONNECTION_NONE, 0, NULL, NULL}, +#endif + {"test_conc127", test_conc127, TEST_CONNECTION_NEW, 0, NULL, NULL}, +/* Both tests work with GNU tls, however we can't create fingerprints with + gnutls-cli in CMakeLists.txt */ +#ifndef HAVE_SCHANNEL + {"test_ssl_fp", test_ssl_fp, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_ssl_fp_list", test_ssl_fp_list, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#endif + {"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc50_1", test_conc50_1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc50_2", test_conc50_2, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc50_3", test_conc50_3, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc50_4", test_conc50_4, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_conc95", test_conc95, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"verify_ssl_server_cert", verify_ssl_server_cert, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_bug62743", test_bug62743, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_phpbug51647", test_phpbug51647, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_ssl_cipher", test_ssl_cipher, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_multi_ssl_connections", test_multi_ssl_connections, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc_102", test_conc_102, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_ssl_version", test_ssl_version, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_ssl_threads", test_ssl_threads, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#ifndef HAVE_SCHANNEL + {"test_password_protected", test_password_protected, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#else + {"test_schannel_cipher", test_schannel_cipher, TEST_CONNECTION_NEW, 0, NULL, NULL}, +#endif + {"test_conc386", test_conc386, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"drop_ssl_user", drop_ssl_user, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ +#if defined(WIN32) && defined(HEAP_CHECK) + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT ); +#endif + + get_envvars(); + read_fingerprint(); + + if (argc > 1) + get_options(argc, argv); + run_tests(my_tests); + + mysql_server_end(); +#if defined(WIN32) && defined(HEAP_CHECK) + _CrtDumpMemoryLeaks(); +#endif + return(exit_status()); +} + diff --git a/libmariadb/unittest/libmariadb/t_conc173.c b/libmariadb/unittest/libmariadb/t_conc173.c new file mode 100644 index 00000000..5b9369b1 --- /dev/null +++ b/libmariadb/unittest/libmariadb/t_conc173.c @@ -0,0 +1,72 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +/** + Some basic tests of the client API. +*/ + +#include "my_test.h" + +static int test_conc_173(MYSQL *unused __attribute__((unused))) +{ + MYSQL mysql; + int arg; + int i; + + for (i = 0; i < 1000; i++) + { + mysql_init(&mysql); + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "client"); + mysql_options(&mysql, MYSQL_OPT_COMPRESS, 0); + + mysql_options(&mysql, MYSQL_OPT_NAMED_PIPE, 0); + + arg = MYSQL_PROTOCOL_SOCKET; + + mysql_options(&mysql, MYSQL_OPT_PROTOCOL, &arg); + + if(!mysql_real_connect(&mysql, hostname, username, password, schema, port, 0, 0)) { + fprintf(stderr, "Failed to connect to database after %d iterations: Error: %s\n", i, mysql_error(&mysql)); + return 1; + } + mysql_close(&mysql); + } + return OK; +} + +struct my_tests_st my_tests[] = { + {"test_conc_173", test_conc_173, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, +}; + + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/thread.c b/libmariadb/unittest/libmariadb/thread.c new file mode 100644 index 00000000..6fae38a8 --- /dev/null +++ b/libmariadb/unittest/libmariadb/thread.c @@ -0,0 +1,180 @@ +/* +*/ + +#include "my_test.h" +#include <ma_pthread.h> + +static int basic_connect(MYSQL *unused __attribute__((unused))) +{ + MYSQL_ROW row; + MYSQL_RES *res; + MYSQL_FIELD *field; + int rc; + + MYSQL *my= mysql_init(NULL); + FAIL_IF(!my, "mysql_init() failed"); + + FAIL_IF(!my_test_connect(my, hostname, username, password, schema, + port, socketname, 0), mysql_error(my)); + + rc= mysql_query(my, "SELECT @@version"); + check_mysql_rc(rc, my); + + res= mysql_store_result(my); + FAIL_IF(!res, mysql_error(my)); + field= mysql_fetch_fields(res); + FAIL_IF(!field, "Couldn't fetch fields"); + + while ((row= mysql_fetch_row(res)) != NULL) + { + FAIL_IF(mysql_num_fields(res) != 1, "Got the wrong number of fields"); + } + FAIL_IF(mysql_errno(my), mysql_error(my)); + + mysql_free_result(res); + mysql_close(my); + + + return OK; +} + +pthread_mutex_t LOCK_test; + +#ifndef _WIN32 +int thread_conc27(void); +#else +DWORD WINAPI thread_conc27(void); +#endif + +#define THREAD_NUM 100 + +/* run this test as root and increase the number of handles (ulimit -n) */ +static int test_conc_27(MYSQL *mysql) +{ + + int rc; + int i; + MYSQL_ROW row; + MYSQL_RES *res; +#ifndef _WIN32 + pthread_t threads[THREAD_NUM]; +#else + HANDLE hthreads[THREAD_NUM]; + DWORD threads[THREAD_NUM]; +#endif + + diag("please run this test manually as root"); + return SKIP; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t_conc27"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t_conc27(a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t_conc27 VALUES(0)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET @a:=@@max_connections"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SET GLOBAL max_connections=100000"); + check_mysql_rc(rc, mysql); + + pthread_mutex_init(&LOCK_test, NULL); + for (i=0; i < THREAD_NUM; i++) + { +#ifndef _WIN32 + pthread_create(&threads[i], NULL, (void *)thread_conc27, NULL); +#else + hthreads[i]= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_conc27, NULL, 0, &threads[i]); + if (hthreads[i]==NULL) + diag("error while starting thread"); +#endif + } + for (i=0; i < THREAD_NUM; i++) + { +#ifndef _WIN32 + pthread_join(threads[i], NULL); +#else + WaitForSingleObject(hthreads[i], INFINITE); +#endif + } + + pthread_mutex_destroy(&LOCK_test); + + rc= mysql_query(mysql, "SET GLOBAL max_connections=@a"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT a FROM t_conc27"); + check_mysql_rc(rc,mysql); + + res= mysql_store_result(mysql); + FAIL_IF(!res, "invalid result"); + + row= mysql_fetch_row(res); + FAIL_IF(!row, "can't fetch row"); + + diag("row=%s", row[0]); + FAIL_IF(atoi(row[0]) != THREAD_NUM, "expected value THREAD_NUM"); + mysql_free_result(res); + rc= mysql_query(mysql, "DROP TABLE t_conc27"); + check_mysql_rc(rc,mysql); + + return OK; +} + +#ifndef _WIN32 +int thread_conc27(void) +#else +DWORD WINAPI thread_conc27(void) +#endif +{ + MYSQL *mysql; + int rc; + MYSQL_RES *res; + mysql_thread_init(); + mysql= mysql_init(NULL); + if(!my_test_connect(mysql, hostname, username, password, schema, + port, socketname, 0)) + { + diag(">Error: %s", mysql_error(mysql)); + mysql_close(mysql); + mysql_thread_end(); + goto end; + } + pthread_mutex_lock(&LOCK_test); + rc= mysql_query(mysql, "UPDATE t_conc27 SET a=a+1"); + check_mysql_rc(rc, mysql); + pthread_mutex_unlock(&LOCK_test); + check_mysql_rc(rc, mysql); + if ((res= mysql_store_result(mysql))) + mysql_free_result(res); + mysql_close(mysql); +end: + mysql_thread_end(); + return 0; +} + +struct my_tests_st my_tests[] = { + {"basic_connect", basic_connect, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc_27", test_conc_27, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + + +int main(int argc, char **argv) +{ + + mysql_library_init(0,0,NULL); + + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + mysql_server_end(); + return(exit_status()); +} diff --git a/libmariadb/unittest/libmariadb/view.c b/libmariadb/unittest/libmariadb/view.c new file mode 100644 index 00000000..ac2c1e56 --- /dev/null +++ b/libmariadb/unittest/libmariadb/view.c @@ -0,0 +1,723 @@ +/* +Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + +The MySQL Connector/C is licensed under the terms of the GPLv2 +<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most +MySQL Connectors. There are special exceptions to the terms and +conditions of the GPLv2 as it is applied to this software, see the +FLOSS License Exception +<http://www.mysql.com/about/legal/licensing/foss-exception.html>. + +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-1301 USA +*/ +#include "my_test.h" + +static int test_view(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + MYSQL_BIND my_bind[1]; + char str_data[50]; + ulong length = 0L; + my_bool is_null = 0; + const char *query= + "SELECT COUNT(*) FROM v1 WHERE SERVERNAME=?"; + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,t2,t3,v1"); + check_mysql_rc(rc, mysql); + + rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1,t2,t3"); + check_mysql_rc(rc, mysql); + 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"); + check_mysql_rc(rc, mysql); + 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"); + check_mysql_rc(rc, mysql); + 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"); + check_mysql_rc(rc, mysql); + 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' '))"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + strcpy(str_data, "TEST"); + memset(my_bind, '\0', sizeof(MYSQL_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= &is_null; + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + int rowcount= 0; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_IF(rowcount != 1, "Expected 1 row"); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1,t2,t3"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int test_view_where(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= + "select v1.c,v2.c from v1, v2"; + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1,v2"); + check_mysql_rc(rc, mysql); + + rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,v2,t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"CREATE TABLE t1 (a int, b int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"create view v1 (c) as select b from t1 where a<3"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"create view v2 (c) as select b from t1 where a>=3"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + int rowcount= 0; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_UNLESS(4 == rowcount, "Expected 4 rows"); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW v1, v2"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int test_view_2where(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + MYSQL_BIND my_bind[8]; + char params[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 = ?"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS LTDX"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS V_LTDX"); + check_mysql_rc(rc, mysql); + 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"); + check_mysql_rc(rc, mysql); + 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)"); + check_mysql_rc(rc, mysql); + memset(my_bind, '\0', 8 * sizeof(MYSQL_BIND)); + for (i=0; i < 8; i++) { + strcpy(params[i], "1"); + my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; + my_bind[i].buffer = (char *)¶ms[i]; + my_bind[i].buffer_length = 1; + my_bind[i].is_null = 0; + length[i] = 1; + my_bind[i].length = &length[i]; + } + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(MYSQL_NO_DATA == rc, "Expected 0 rows"); + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW V_LTDX"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE LTDX"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int test_view_star(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + MYSQL_BIND my_bind[8]; + char params[8][100]; + ulong length[8]; + const char *query= "SELECT * FROM vt1 WHERE a IN (?,?)"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, vt1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, vt1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + memset(my_bind, '\0', 8 * sizeof(MYSQL_BIND)); + for (i= 0; i < 2; i++) { + sprintf((char *)¶ms[i], "%d", i); + my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING; + my_bind[i].buffer = (char *)¶ms[i]; + my_bind[i].buffer_length = 100; + my_bind[i].is_null = 0; + my_bind[i].length = &length[i]; + length[i] = 1; + } + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + FAIL_UNLESS(MYSQL_NO_DATA == rc, "Expected 0 rows"); + } + + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW vt1"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int test_view_insert(MYSQL *mysql) +{ + MYSQL_STMT *insert_stmt, *select_stmt; + int rc, i; + MYSQL_BIND my_bind[1]; + int my_val = 0; + ulong my_length = 0L; + my_bool my_null = 0; + const char *query= + "insert into v1 values (?)"; + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1"); + check_mysql_rc(rc, mysql); + rc = mysql_query(mysql, "DROP VIEW IF EXISTS t1,v1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql,"create table t1 (a int, primary key (a))"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create view v1 as select a from t1 where a>=1"); + check_mysql_rc(rc, mysql); + + insert_stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(insert_stmt, SL(query)); + check_stmt_rc(rc, insert_stmt); + query= "select * from t1"; + select_stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(select_stmt, SL(query)); + check_stmt_rc(rc, select_stmt); + + memset(my_bind, '\0', sizeof(MYSQL_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 = &my_null; + rc= mysql_stmt_bind_param(insert_stmt, my_bind); + check_stmt_rc(rc, select_stmt); + + for (i= 0; i < 3; i++) + { + int rowcount= 0; + my_val= i; + + rc= mysql_stmt_execute(insert_stmt); + check_stmt_rc(rc, insert_stmt);; + + rc= mysql_stmt_execute(select_stmt); + check_stmt_rc(rc, select_stmt);; + while (mysql_stmt_fetch(select_stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_UNLESS((i+1) == rowcount, "rowcount != i+1"); + } + mysql_stmt_close(insert_stmt); + mysql_stmt_close(select_stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int test_left_join_view(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc, i; + const char *query= + "select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x);"; + + rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1"); + check_mysql_rc(rc, mysql); + + rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"insert into t1 values (1), (2), (3)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql,"create view v1 (x) as select a from t1 where a > 1"); + check_mysql_rc(rc, mysql); + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + + for (i= 0; i < 3; i++) + { + int rowcount= 0; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_UNLESS(3 == rowcount, "Expected 3 rows"); + } + mysql_stmt_close(stmt); + + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +static int test_view_insert_fields(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + char parm[11][1000]; + ulong l[11]; + int rc, i; + int rowcount= 0; + MYSQL_BIND my_bind[11]; + const char *query= "INSERT INTO `v1` ( `K1C4` ,`K2C4` ,`K3C4` ,`K4N4` ,`F1C4` ,`F2I4` ,`F3N5` ,`F7F8` ,`F6N4` ,`F5C8` ,`F9D8` ) VALUES( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )"; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, v1"); + check_mysql_rc(rc, mysql); + 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))"); + check_mysql_rc(rc, mysql); + 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"); + + memset(my_bind, '\0', sizeof(my_bind)); + for (i= 0; i < 11; i++) + { + l[i]= 2; + my_bind[i].buffer_type= MYSQL_TYPE_STRING; + my_bind[i].is_null= 0; + my_bind[i].buffer= (char *)&parm[i]; + + strcpy(parm[i], "1"); + my_bind[i].buffer_length= 2; + my_bind[i].length= &l[i]; + } + + // https://jira.mariadb.org/browse/XPT-266 + if (IS_XPAND()) { + rc= mysql_query(mysql, "SET NAMES UTF8"); + check_mysql_rc(rc, mysql); + } + + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_bind_param(stmt, my_bind); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + mysql_stmt_close(stmt); + + query= "select * from t1"; + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + rowcount++; + FAIL_UNLESS(1 == rowcount, "Expected 1 row"); + + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE t1"); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_view_sp_list_fields(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res; + MYSQL_ROW row; + int skip; + + /* skip this test if bin_log is on */ + rc= mysql_query(mysql, "SHOW VARIABLES LIKE 'log_bin'"); + check_mysql_rc(rc, mysql); + res= mysql_store_result(mysql); + FAIL_IF(!res, "empty/invalid resultset"); + row = mysql_fetch_row(res); + skip= (strcmp((char *)row[1], "ON") == 0); + mysql_free_result(res); + + if (skip) { + diag("bin_log is ON -> skip"); + return SKIP; + } + + rc= mysql_query(mysql, "DROP FUNCTION IF EXISTS f1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE IF EXISTS v1, t1, t2"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS v1, t1, t2"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create function f1 () returns int return 5"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (s1 char,s2 char)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t2 (s1 int);"); + check_mysql_rc(rc, mysql); + 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);"); + check_mysql_rc(rc, mysql); + res= mysql_list_fields(mysql, "v1", NullS); + FAIL_UNLESS(res != 0 && mysql_num_fields(res) != 0, "0 Fields"); + rc= mysql_query(mysql, "DROP FUNCTION f1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP TABLE t1, t2"); + mysql_free_result(res); + check_mysql_rc(rc, mysql); + + return OK; +} + +static int test_bug19671(MYSQL *mysql) +{ + MYSQL_RES *result; + MYSQL_FIELD *field; + int rc, retcode= OK; + + + rc= mysql_query(mysql, "set sql_mode=''"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "drop table if exists t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "drop view if exists v1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create table t1(f1 int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "create view v1 as select va.* from t1 va"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT * FROM v1"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + FAIL_IF(!result, "Invalid result set"); + + field= mysql_fetch_field(result); + FAIL_IF(!field, "Can't fetch field"); + + if (strcmp(field->table, "v1") != 0) { + diag("Wrong value '%s' for field_table. Expected 'v1'. (%s: %d)", field->table, __FILE__, __LINE__); + retcode= FAIL; + } + + mysql_free_result(result); + + rc= mysql_query(mysql, "drop view v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "drop table t1"); + check_mysql_rc(rc, mysql); + + return retcode; +} + +/* + Bug#11111: fetch from view returns wrong data +*/ + +static int test_bug11111(MYSQL *mysql) +{ + 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"; + + rc= mysql_query(mysql, "drop table if exists t1, t2, v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "drop view if exists t1, t2, v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t1 (f1 int, f2 int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create table t2 (ff1 int, ff2 int)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "create view v1 as select * from t1, t2 where f1=ff1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t1 values (1,1), (2,2), (3,3)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "insert into t2 values (1,1), (2,2), (3,3)"); + check_mysql_rc(rc, mysql); + + stmt= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt, SL(query)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + memset(my_bind, '\0', 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_stmt_rc(rc, stmt); + + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + FAIL_UNLESS(!strcmp(buf[1],"1"), "buf[1] != '1'"); + mysql_stmt_close(stmt); + rc= mysql_query(mysql, "drop view v1"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "drop table t1, t2"); + check_mysql_rc(rc, mysql); + + return OK; +} + +/** + Bug#29306 Truncated data in MS Access with decimal (3,1) columns in a VIEW +*/ + +static int test_bug29306(MYSQL *mysql) +{ + MYSQL_FIELD *field; + int rc; + MYSQL_RES *res; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS tab17557"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW IF EXISTS view17557"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE TABLE tab17557 (dd decimal (3,1))"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "CREATE VIEW view17557 as SELECT dd FROM tab17557"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO tab17557 VALUES (7.6)"); + check_mysql_rc(rc, mysql); + + /* Checking the view */ + res= mysql_list_fields(mysql, "view17557", NULL); + while ((field= mysql_fetch_field(res))) + { + FAIL_UNLESS(field->decimals == 1, "field->decimals != 1"); + } + mysql_free_result(res); + + rc= mysql_query(mysql, "DROP TABLE tab17557"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "DROP VIEW view17557"); + check_mysql_rc(rc, mysql); + + return OK; +} + + +struct my_tests_st my_tests[] = { + {"test_view", test_view, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_view_where", test_view_where, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_view_2where", test_view_2where, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_view_star", test_view_star, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_view_insert", test_view_insert, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_left_join_view", test_left_join_view, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_view_insert_fields", test_view_insert_fields, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_view_sp_list_fields", test_view_sp_list_fields,TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug19671", test_bug19671, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug29306", test_bug29306, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {"test_bug11111", test_bug11111, TEST_CONNECTION_DEFAULT, 0, NULL , NULL}, + {NULL, NULL, 0, 0, NULL, NULL} +}; + +int main(int argc, char **argv) +{ + if (argc > 1) + get_options(argc, argv); + + get_envvars(); + + run_tests(my_tests); + + return(exit_status()); +} diff --git a/libmariadb/unittest/mytap/CMakeLists.txt b/libmariadb/unittest/mytap/CMakeLists.txt new file mode 100644 index 00000000..1a03d7ac --- /dev/null +++ b/libmariadb/unittest/mytap/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2007 MySQL AB +# +# 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-1301 USA + + +INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include ${CC_SOURCE_DIR}/zlib + ${CC_BINARY_DIR}/include) +ADD_LIBRARY(cctap tap.c) diff --git a/libmariadb/unittest/mytap/Doxyfile b/libmariadb/unittest/mytap/Doxyfile new file mode 100644 index 00000000..1b1c82b4 --- /dev/null +++ b/libmariadb/unittest/mytap/Doxyfile @@ -0,0 +1,1156 @@ +# Doxyfile 1.3.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of source +# files, where putting all generated files in the same directory would otherwise +# cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is used +# as the annotated text. Otherwise, the brief description is used as-is. If left +# blank, the following values are used ("$name" is automatically replaced with the +# name of the entity): "The $name class" "The $name widget" "The $name file" +# "is" "provides" "specifies" "contains" "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories +# that contain documented source files. You may enter file names like +# "myfile.cpp" or directories like "/usr/src/myproject". Separate the +# files or directories with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like +# *.cpp and *.h) to filter out the source-files in the directories. If +# left blank the following patterns are tested: *.c *.cc *.cxx *.cpp +# *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp *.h++ +# *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not +# subdirectories should be searched for input files as well. Possible +# values are YES and NO. If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that +# should excluded from the INPUT source files. This way you can easily +# exclude a subdirectory from a directory tree whose root is specified +# with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are +# excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to +# exclude certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = e + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = *.c + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more +# wildcard patterns (like *.h and *.hpp) to filter out the +# header-files in the directories. If left blank, the patterns +# specified with FILE_PATTERNS will be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names +# that are defined before the preprocessor is started (similar to the +# -D option of gcc). The argument of the tag is a list of macros of +# the form: name or name=definition (no spaces). If the definition and +# the = are omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES +# then this tag can be used to specify a list of macro names that +# should be expanded. The macro definition that is found in the +# sources will be used. Use the PREDEFINED tag if you want to use a +# different macro definition. + +EXPAND_AS_DEFINED = __attribute__ + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are +# alone on a line, have an all uppercase name, and do not end with a +# semicolon. Such function macros are typically used for boiler-plate +# code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superseded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/libmariadb/unittest/mytap/t/basic-t.c b/libmariadb/unittest/mytap/t/basic-t.c new file mode 100644 index 00000000..c0ceb5bf --- /dev/null +++ b/libmariadb/unittest/mytap/t/basic-t.c @@ -0,0 +1,33 @@ +/* Copyright (C) 2006 MySQL AB + + 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-1301 USA */ + +#include "my_config.h" + +#include <stdlib.h> +#include "../tap.h" + +int main() { + plan(5); + ok(1 == 1, "testing basic functions"); + ok(2 == 2, " "); + ok(3 == 3, NULL); + if (1 == 1) + skip(2, "Sensa fragoli"); + else { + ok(1 == 2, "Should not be run at all"); + ok(1, "This one neither"); + } + return exit_status(); +} diff --git a/libmariadb/unittest/mytap/tap.c b/libmariadb/unittest/mytap/tap.c new file mode 100644 index 00000000..0290389f --- /dev/null +++ b/libmariadb/unittest/mytap/tap.c @@ -0,0 +1,600 @@ +/* Copyright (C) 2006 MySQL AB + + 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 02111-1301, USA + + Library for providing TAP support for testing C and C++ was written + by Mats Kindahl <mats@mysql.com>. +*/ + +#include "tap.h" + +#include "ma_global.h" + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> + +/* + Visual Studio 2003 does not know vsnprintf but knows _vsnprintf. + We don't put this #define in config-win.h because we prefer + ma_vsnprintf everywhere instead, except when linking with libmysys + is not desirable - the case here. +*/ +#if defined(_MSC_VER) && ( _MSC_VER == 1310 ) +#define vsnprintf _vsnprintf +#endif + +/** + @defgroup MyTAP_Internal MyTAP Internals + + Internal functions and data structures for the MyTAP implementation. +*/ + +/** + Test data structure. + + Data structure containing all information about the test suite. + + @ingroup MyTAP_Internal + */ +static TEST_DATA g_test = { 0, 0, 0, "" }; + +/** + Output stream for test report message. + + The macro is just a temporary solution. + + @ingroup MyTAP_Internal + */ +#define tapout stdout + +/** + Emit the beginning of a test line, that is: "(not) ok", test number, + and description. + + To emit the directive, use the emit_dir() function + + @ingroup MyTAP_Internal + + @see emit_dir + + @param pass 'true' if test passed, 'false' otherwise + @param fmt Description of test in printf() format. + @param ap Vararg list for the description string above. + */ +static void +vemit_tap(int pass, char const *fmt, va_list ap) +{ + fprintf(tapout, "%sok %d%s", + pass ? "" : "not ", + ++g_test.last, + (fmt && *fmt) ? " - " : ""); + if (fmt && *fmt) + vfprintf(tapout, fmt, ap); +} + + +/** + Emit a TAP directive. + + TAP directives are comments after that have the form: + + @code + ok 1 # skip reason for skipping + not ok 2 # todo some text explaining what remains + @endcode + + @ingroup MyTAP_Internal + + @param dir Directive as a string + @param why Explanation string + */ +static void +emit_dir(const char *dir, const char *why) +{ + fprintf(tapout, " # %s %s", dir, why); +} + + +/** + Emit a newline to the TAP output stream. + + @ingroup MyTAP_Internal + */ +static void +emit_endl() +{ + fprintf(tapout, "\n"); +} + +static void +handle_core_signal(int signo) +{ + BAIL_OUT("Signal %d thrown", signo); +} + +void +BAIL_OUT(char const *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(tapout, "Bail out! "); + vfprintf(tapout, fmt, ap); + emit_endl(); + va_end(ap); + exit(255); +} + + +void +diag(char const *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(tapout, "# "); + vfprintf(tapout, fmt, ap); + emit_endl(); + va_end(ap); +} + +typedef struct signal_entry { + int signo; + void (*handler)(int); +} signal_entry; + +static signal_entry install_signal[]= { +#ifdef SIGQUIT + { SIGQUIT, handle_core_signal }, +#endif + { SIGILL, handle_core_signal }, + { SIGABRT, handle_core_signal }, + { SIGFPE, handle_core_signal }, + { SIGSEGV, handle_core_signal } +#ifdef SIGBUS + , { SIGBUS, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGXCPU, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGXFSZ, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGSYS, handle_core_signal } +#endif +#ifdef SIGXCPU + , { SIGTRAP, handle_core_signal } +#endif +}; + +int skip_big_tests= 1; + +void +plan(int const count) +{ + char *config= getenv("MYTAP_CONFIG"); + size_t i; + + if (config) + skip_big_tests= strcmp(config, "big"); + + setvbuf(tapout, 0, _IONBF, 0); /* provide output at once */ + /* + Install signal handler + */ + + for (i= 0; i < sizeof(install_signal)/sizeof(*install_signal); ++i) + signal(install_signal[i].signo, install_signal[i].handler); + + g_test.plan= count; + switch (count) + { + case NO_PLAN: + break; + default: + if (count > 0) + fprintf(tapout, "1..%d\n", count); + break; + } +} + + +void +skip_all(char const *reason, ...) +{ + va_list ap; + va_start(ap, reason); + fprintf(tapout, "1..0 # skip "); + vfprintf(tapout, reason, ap); + va_end(ap); + exit(0); +} + +void +ok(int const pass, char const *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + if (!pass && *g_test.todo == '\0') + ++g_test.failed; + + vemit_tap(pass, fmt, ap); + va_end(ap); + if (*g_test.todo != '\0') + emit_dir("todo", g_test.todo); + emit_endl(); +} + + +void +skip(int how_many, char const *const fmt, ...) +{ + char reason[80]; + if (fmt && *fmt) + { + va_list ap; + va_start(ap, fmt); + vsnprintf(reason, sizeof(reason), fmt, ap); + va_end(ap); + } + else + reason[0] = '\0'; + + while (how_many-- > 0) + { + va_list ap; + memset((char*) &ap, 0, sizeof(ap)); /* Keep compiler happy */ + vemit_tap(1, NULL, ap); + emit_dir("skip", reason); + emit_endl(); + } +} + +void +todo_start(char const *message, ...) +{ + va_list ap; + va_start(ap, message); + vsnprintf(g_test.todo, sizeof(g_test.todo), message, ap); + va_end(ap); +} + +void +todo_end() +{ + *g_test.todo = '\0'; +} + +int exit_status() { + /* + If there were no plan, we write one last instead. + */ + if (g_test.plan == NO_PLAN) + plan(g_test.last); + + if (g_test.plan != g_test.last) + { + diag("%d tests planned but%s %d executed", + g_test.plan, (g_test.plan > g_test.last ? " only" : ""), g_test.last); + return EXIT_FAILURE; + } + + if (g_test.failed > 0) + { + diag("Failed %d tests!", g_test.failed); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/** + @mainpage Testing C and C++ using MyTAP + + @section IntroSec Introduction + + Unit tests are used to test individual components of a system. In + contrast, functional tests usually test the entire system. The + rationale is that each component should be correct if the system is + to be correct. Unit tests are usually small pieces of code that + tests an individual function, class, a module, or other unit of the + code. + + Observe that a correctly functioning system can be built from + "faulty" components. The problem with this approach is that as the + system evolves, the bugs surface in unexpected ways, making + maintenance harder. + + The advantages of using unit tests to test components of the system + are several: + + - The unit tests can make a more thorough testing than the + functional tests by testing correctness even for pathological use + (which shouldn't be present in the system). This increases the + overall robustness of the system and makes maintenance easier. + + - It is easier and faster to find problems with a malfunctioning + component than to find problems in a malfunctioning system. This + shortens the compile-run-edit cycle and therefore improves the + overall performance of development. + + - The component has to support at least two uses: in the system and + in a unit test. This leads to more generic and stable interfaces + and in addition promotes the development of reusable components. + + For example, the following are typical functional tests: + - Does transactions work according to specifications? + - Can we connect a client to the server and execute statements? + + In contrast, the following are typical unit tests: + + - Can the 'String' class handle a specified list of character sets? + - Does all operations for 'my_bitmap' produce the correct result? + - Does all the NIST test vectors for the AES implementation encrypt + correctly? + + + @section UnitTest Writing unit tests + + The purpose of writing unit tests is to use them to drive component + development towards a solution that passes the tests. This means that the + unit tests has to be as complete as possible, testing at least: + + - Normal input + - Borderline cases + - Faulty input + - Error handling + - Bad environment + + @subsection NormalSubSec Normal input + + This is to test that the component have the expected behaviour. + This is just plain simple: test that it works. For example, test + that you can unpack what you packed, adding gives the sum, pinching + the duck makes it quack. + + This is what everybody does when they write tests. + + + @subsection BorderlineTests Borderline cases + + If you have a size anywhere for your component, does it work for + size 1? Size 0? Sizes close to <code>UINT_MAX</code>? + + It might not be sensible to have a size 0, so in this case it is + not a borderline case, but rather a faulty input (see @ref + FaultyInputTests). + + + @subsection FaultyInputTests Faulty input + + Does your bitmap handle 0 bits size? Well, it might not be designed + for it, but is should <em>not</em> crash the application, but + rather produce an error. This is called defensive programming. + + Unfortunately, adding checks for values that should just not be + entered at all is not always practical: the checks cost cycles and + might cost more than it's worth. For example, some functions are + designed so that you may not give it a null pointer. In those + cases it's not sensible to pass it <code>NULL</code> just to see it + crash. + + Since every experienced programmer add an <code>assert()</code> to + ensure that you get a proper failure for the debug builds when a + null pointer passed (you add asserts too, right?), you will in this + case instead have a controlled (early) crash in the debug build. + + + @subsection ErrorHandlingTests Error handling + + This is testing that the errors your component is designed to give + actually are produced. For example, testing that trying to open a + non-existing file produces a sensible error code. + + + @subsection BadEnvironmentTests Environment + + Sometimes, modules has to behave well even when the environment + fails to work correctly. Typical examples are when the computer is + out of dynamic memory or when the disk is full. You can emulate + this by replacing, e.g., <code>malloc()</code> with your own + version that will work for a while, but then fail. Some things are + worth to keep in mind here: + + - Make sure to make the function fail deterministically, so that + you really can repeat the test. + + - Make sure that it doesn't just fail immediately. The unit might + have checks for the first case, but might actually fail some time + in the near future. + + + @section UnitTest How to structure a unit test + + In this section we will give some advice on how to structure the + unit tests to make the development run smoothly. The basic + structure of a test is: + + - Plan + - Test + - Report + + + @subsection TestPlanning Plan the test + + Planning the test means telling how many tests there are. In the + event that one of the tests causes a crash, it is then possible to + see that there are fewer tests than expected, and print a proper + error message. + + To plan a test, use the @c plan() function in the following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + . + . + . + } + @endcode + + If you don't call the @c plan() function, the number of tests + executed will be printed at the end. This is intended to be used + while developing the unit and you are constantly adding tests. It + is not indented to be used after the unit has been released. + + + @subsection TestRunning Execute the test + + To report the status of a test, the @c ok() function is used in the + following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + ok(ducks == paddling_ducks, + "%d ducks did not paddle", ducks - paddling_ducks); + . + . + . + } + @endcode + + This will print a test result line on the standard output in TAP + format, which allows TAP handling frameworks (like Test::Harness) + to parse the status of the test. + + @subsection TestReport Report the result of the test + + At the end, a complete test report should be written, with some + statistics. If the test returns EXIT_SUCCESS, all tests were + successful, otherwise at least one test failed. + + To get a TAP compliant output and exit status, report the exit + status in the following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + ok(ducks == paddling_ducks, + "%d ducks did not paddle", ducks - paddling_ducks); + . + . + . + return exit_status(); + } + @endcode + + @section DontDoThis Ways to not do unit testing + + In this section, we'll go through some quite common ways to write + tests that are <em>not</em> a good idea. + + @subsection BreadthFirstTests Doing breadth-first testing + + If you're writing a library with several functions, don't test all + functions using size 1, then all functions using size 2, etc. If a + test for size 42 fails, you have no easy way of tracking down why + it failed. + + It is better to concentrate on getting one function to work at a + time, which means that you test each function for all sizes that + you think is reasonable. Then you continue with the next function, + doing the same. This is usually also the way that a library is + developed (one function at a time) so stick to testing that is + appropriate for now the unit is developed. + + @subsection JustToBeSafeTest Writing unnecessarily large tests + + Don't write tests that use parameters in the range 1-1024 unless + you have a very good reason to believe that the component will + succeed for 562 but fail for 564 (the numbers picked are just + examples). + + It is very common to write extensive tests "just to be safe." + Having a test suite with a lot of values might give you a warm + fuzzy feeling, but it doesn't really help you find the bugs. Good + tests fail; seriously, if you write a test that you expect to + succeed, you don't need to write it. If you think that it + <em>might</em> fail, <em>then</em> you should write it. + + Don't take this as an excuse to avoid writing any tests at all + "since I make no mistakes" (when it comes to this, there are two + kinds of people: those who admit they make mistakes, and those who + don't); rather, this means that there is no reason to test that + using a buffer with size 100 works when you have a test for buffer + size 96. + + The drawback is that the test suite takes longer to run, for little + or no benefit. It is acceptable to do a exhaustive test if it + doesn't take too long to run and it is quite common to do an + exhaustive test of a function for a small set of values. + Use your judgment to decide what is excessive: your milage may + vary. +*/ + +/** + @example simple.t.c + + This is an simple example of how to write a test using the + library. The output of this program is: + + @code + 1..1 + # Testing basic functions + ok 1 - Testing gcs() + @endcode + + The basic structure is: plan the number of test points using the + plan() function, perform the test and write out the result of each + test point using the ok() function, print out a diagnostics message + using diag(), and report the result of the test by calling the + exit_status() function. Observe that this test does excessive + testing (see @ref JustToBeSafeTest), but the test point doesn't + take very long time. +*/ + +/** + @example todo.t.c + + This example demonstrates how to use the <code>todo_start()</code> + and <code>todo_end()</code> function to mark a sequence of tests to + be done. Observe that the tests are assumed to fail: if any test + succeeds, it is considered a "bonus". +*/ + +/** + @example skip.t.c + + This is an example of how the <code>SKIP_BLOCK_IF</code> can be + used to skip a predetermined number of tests. Observe that the + macro actually skips the following statement, but it's not sensible + to use anything than a block. +*/ + +/** + @example skip_all.t.c + + Sometimes, you skip an entire test because it's testing a feature + that doesn't exist on the system that you're testing. To skip an + entire test, use the <code>skip_all()</code> function according to + this example. + */ diff --git a/libmariadb/unittest/mytap/tap.h b/libmariadb/unittest/mytap/tap.h new file mode 100644 index 00000000..15b1842b --- /dev/null +++ b/libmariadb/unittest/mytap/tap.h @@ -0,0 +1,305 @@ +/* Copyright (C) 2006 MySQL AB + + 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 02111-1301, USA + + Library for providing TAP support for testing C and C++ was written + by Mats Kindahl <mats@mysql.com>. +*/ + +#ifndef TAP_H +#define TAP_H + +#include "ma_global.h" + +/* + @defgroup MyTAP MySQL support for performing unit tests according to + the Test Anything Protocol (TAP). +*/ + +#define NO_PLAN (0) + +/** + Data about test plan. + + @ingroup MyTAP_Internal + + @internal We are using the "typedef struct X { ... } X" idiom to + create class/struct X both in C and C++. + */ + +typedef struct TEST_DATA { + /** + Number of tests that is planned to execute. + + Can be zero (<code>NO_PLAN</code>) meaning that the plan string + will be printed at the end of test instead. + */ + int plan; + + /** Number of last test that was done or skipped. */ + int last; + + /** Number of tests that failed. */ + int failed; + + /** Todo reason. */ + char todo[128]; +} TEST_DATA; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Defines whether "big" tests should be skipped. + + This variable is set by plan() function unless MYTAP_CONFIG environment + variable is set to the string "big". It is supposed to be used as + + @code + if (skip_big_tests) { + skip(1, "Big test skipped"); + } else { + ok(life_universe_and_everything() == 42, "The answer is CORRECT"); + } + @endcode + + @see SKIP_BIG_TESTS +*/ +extern int skip_big_tests; + +/** + @defgroup MyTAP_API MyTAP API + + MySQL support for performing unit tests according to TAP. + + @{ +*/ + +/** + Set number of tests that is planned to execute. + + The function also accepts the predefined constant + <code>NO_PLAN</code>. If the function is not called, it is as if + it was called with <code>NO_PLAN</code>, i.e., the test plan will + be printed after all the test lines. + + The plan() function will install signal handlers for all signals + that generate a core, so if you want to override these signals, do + it <em>after</em> you have called the plan() function. + + It will also set skip_big_tests variable if MYTAP_CONFIG environment + variable is defined. + + @see skip_big_tests + + @param count The planned number of tests to run. +*/ + +void plan(int const count); + + +/** + Report test result as a TAP line. + + Function used to write status of an individual test. Call this + function in the following manner: + + @code + ok(ducks == paddling, + "%d ducks did not paddle", ducks - paddling); + @endcode + + @param pass Zero if the test failed, non-zero if it passed. + @param fmt Format string in printf() format. NULL is allowed, in + which case nothing is printed. +*/ + +void ok(int const pass, char const *fmt, ...) + __attribute__((format(printf,2,3))); + + +/** + Skip a determined number of tests. + + Function to print that <em>how_many</em> tests have been skipped. + The reason is printed for each skipped test. Observe that this + function does not do the actual skipping for you, it just prints + information that tests have been skipped. This function is not + usually used, but rather the macro @c SKIP_BLOCK_IF, which does the + skipping for you. + + It shall be used in the following manner: + + @code + if (ducks == 0) { + skip(2, "No ducks in the pond"); + } else { + int i; + for (i = 0 ; i < 2 ; ++i) + ok(duck[i] == paddling, "is duck %d paddling?", i); + } + @endcode + + @see SKIP_BLOCK_IF + + @param how_many Number of tests that are to be skipped. + @param reason A reason for skipping the tests + */ + +void skip(int how_many, char const *const reason, ...) + __attribute__((format(printf,2,3))); + + +/** + Helper macro to skip a block of code. The macro can be used to + simplify conditionally skipping a block of code. It is used in the + following manner: + + @code + SKIP_BLOCK_IF(ducks == 0, 2, "No ducks in the pond") + { + int i; + for (i = 0 ; i < 2 ; ++i) + ok(duck[i] == paddling, "is duck %d paddling?", i); + } + @endcode + + @see skip + */ + +#define SKIP_BLOCK_IF(SKIP_IF_TRUE, COUNT, REASON) \ + if (SKIP_IF_TRUE) skip((COUNT),(REASON)); else + + +/** + Helper macro to skip a group of "big" tests. It is used in the following + manner: + + @code + SKIP_BIG_TESTS(1) + { + ok(life_universe_and_everything() == 42, "The answer is CORRECT"); + } + @endcode + + @see skip_big_tests + */ + +#define SKIP_BIG_TESTS(COUNT) \ + if (skip_big_tests) skip((COUNT), "big test"); else + + +/** + Print a diagnostics message. + + @param fmt Diagnostics message in printf() format. + */ + +void diag(char const *fmt, ...) + __attribute__((format(printf,1,2))); + + +/** + Print a bail out message. + + A bail out message can be issued when no further testing can be + done, e.g., when there are missing dependencies. + + The test will exit with status 255. This function does not return. + + @code + BAIL_OUT("Lost connection to server %s", server_name); + @endcode + + @note A bail out message is printed if a signal that generates a + core is raised. + + @param fmt Bail out message in printf() format. +*/ + +void BAIL_OUT(char const *fmt, ...) + __attribute__((noreturn, format(printf,1,2))); + + +/** + Print summary report and return exit status. + + This function will print a summary report of how many tests passed, + how many were skipped, and how many remains to do. The function + should be called after all tests are executed in the following + manner: + + @code + return exit_status(); + @endcode + + @returns @c EXIT_SUCCESS if all tests passed, @c EXIT_FAILURE if + one or more tests failed. + */ + +int exit_status(void); + + +/** + Skip entire test suite. + + To skip the entire test suite, use this function. It will + automatically call exit(), so there is no need to have checks + around it. + */ + +void skip_all(char const *reason, ...) + __attribute__((noreturn, format(printf, 1, 2))); + + +/** + Start section of tests that are not yet ready. + + To start a section of tests that are not ready and are expected to + fail, use this function and todo_end() in the following manner: + + @code + todo_start("Not ready yet"); + ok(is_rocketeering(duck), "Rocket-propelled ducks"); + ok(is_kamikaze(duck), "Kamikaze ducks"); + todo_end(); + @endcode + + @see todo_end + + @note + It is not possible to nest todo sections. + + @param message Message that will be printed before the todo tests. +*/ + +void todo_start(char const *message, ...) + __attribute__((format(printf, 1, 2))); + + +/** + End a section of tests that are not yet ready. +*/ + +void todo_end(); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* TAP_H */ |