summaryrefslogtreecommitdiffstats
path: root/libmariadb/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'libmariadb/unittest')
-rw-r--r--libmariadb/unittest/libmariadb/CMakeLists.txt67
-rw-r--r--libmariadb/unittest/libmariadb/async.c321
-rw-r--r--libmariadb/unittest/libmariadb/basic-t.c885
-rw-r--r--libmariadb/unittest/libmariadb/bulk1.c1099
-rw-r--r--libmariadb/unittest/libmariadb/charset.c881
-rw-r--r--libmariadb/unittest/libmariadb/conc336.c59
-rw-r--r--libmariadb/unittest/libmariadb/connection.c2365
-rw-r--r--libmariadb/unittest/libmariadb/cursor.c1852
-rw-r--r--libmariadb/unittest/libmariadb/data.csv100
-rw-r--r--libmariadb/unittest/libmariadb/dyncol.c323
-rw-r--r--libmariadb/unittest/libmariadb/errors.c299
-rw-r--r--libmariadb/unittest/libmariadb/features-10_2.c255
-rw-r--r--libmariadb/unittest/libmariadb/fetch.c1003
-rw-r--r--libmariadb/unittest/libmariadb/fingerprint.list.in4
-rw-r--r--libmariadb/unittest/libmariadb/getopt.c742
-rw-r--r--libmariadb/unittest/libmariadb/logs.c223
-rw-r--r--libmariadb/unittest/libmariadb/ma_getopt.c741
-rw-r--r--libmariadb/unittest/libmariadb/ma_getopt.h131
-rw-r--r--libmariadb/unittest/libmariadb/misc.c1689
-rw-r--r--libmariadb/unittest/libmariadb/my_test.h770
-rw-r--r--libmariadb/unittest/libmariadb/performance.c76
-rw-r--r--libmariadb/unittest/libmariadb/ps.c5315
-rw-r--r--libmariadb/unittest/libmariadb/ps_bugs.c5846
-rw-r--r--libmariadb/unittest/libmariadb/ps_new.c526
-rw-r--r--libmariadb/unittest/libmariadb/result.c1105
-rw-r--r--libmariadb/unittest/libmariadb/rpl_api.c363
-rw-r--r--libmariadb/unittest/libmariadb/sp.c91
-rw-r--r--libmariadb/unittest/libmariadb/ssl.c1424
-rw-r--r--libmariadb/unittest/libmariadb/t_conc173.c72
-rw-r--r--libmariadb/unittest/libmariadb/thread.c180
-rw-r--r--libmariadb/unittest/libmariadb/view.c723
-rw-r--r--libmariadb/unittest/mytap/CMakeLists.txt19
-rw-r--r--libmariadb/unittest/mytap/Doxyfile1156
-rw-r--r--libmariadb/unittest/mytap/t/basic-t.c33
-rw-r--r--libmariadb/unittest/mytap/tap.c600
-rw-r--r--libmariadb/unittest/mytap/tap.h305
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, &param_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, &param_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, &param_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, &param_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, &param_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, &param_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 *)&param;
+ 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 *)&num;
+ 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, &paramCount);
+
+ 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, &paramCount);
+ 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, &paramCount);
+ mysql_stmt_bind_param(stmt, bind);
+
+ rc = mariadb_stmt_execute_direct(stmt, "INSERT INTO mdev19838"
+ "(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17)"
+ " VALUES "
+ "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", -1);
+ check_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, &paramCount);
+
+ 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, &paramCount);
+
+ 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, &paramcount);
+ 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 *)&params[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 *)&params[i], "%d", i);
+ my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ my_bind[i].buffer = (char *)&params[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 */