You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-07 02:42:49 +03:00
Added openssl layer support
Imported libmysql unittests Added simple ssl tests minor cleanup
This commit is contained in:
@@ -72,13 +72,11 @@ INCLUDE(LibmysqlTypes.txt)
|
||||
|
||||
|
||||
|
||||
#Check for OpenSSL
|
||||
#INCLUDE(FindOpenSSL)
|
||||
#IF(OPENSSL_FOUND)
|
||||
# ADD_DEFINITIONS(-DHAVE_OPENSSL)
|
||||
# FIND_LIBRARY(SSL_LIBRARIES NAMES libeay32 crypto)
|
||||
#ENDIF(OPENSSL_FOUND)
|
||||
|
||||
# Check for OpenSSL
|
||||
INCLUDE(FindOpenSSL)
|
||||
IF(OPENSSL_FOUND)
|
||||
ADD_DEFINITIONS(-DHAVE_OPENSSL)
|
||||
ENDIF(OPENSSL_FOUND)
|
||||
|
||||
MESSAGE(STATUS "writing configuration files")
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/include/mysql_version.h.in
|
||||
|
@@ -58,6 +58,7 @@ extern const char *client_errors[]; /* Error messages */
|
||||
#define CR_NAMEDPIPESETSTATE_ERROR 2018
|
||||
#define CR_CANT_READ_CHARSET 2019
|
||||
#define CR_NET_PACKET_TOO_LARGE 2020
|
||||
#define CR_SSL_CONNECTION_ERROR 2026
|
||||
#define CR_MALFORMED_PACKET 2027
|
||||
#define CR_NO_PREPARE_STMT 2030
|
||||
#define CR_PARAMS_NOT_BOUND 2031
|
||||
|
43
include/my_secure.h
Normal file
43
include/my_secure.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/************************************************************************************
|
||||
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
|
||||
|
||||
Part of this code includes code from the PHP project which
|
||||
is freely available from http://www.php.net
|
||||
*************************************************************************************/
|
||||
#ifndef _my_secure_h_
|
||||
#define _my_secure_h_
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <mysql.h>
|
||||
#include <openssl/ssl.h> /* SSL and SSL_CTX */
|
||||
#include <openssl/err.h> /* error reporting */
|
||||
#include <openssl/conf.h>
|
||||
|
||||
struct MYSQL;
|
||||
|
||||
size_t my_ssl_read(Vio *vio, uchar* buf, size_t size);
|
||||
int my_ssl_close(Vio *vio);
|
||||
size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size);
|
||||
SSL *my_ssl_init(MYSQL *mysql);
|
||||
int my_ssl_connect(SSL *ssl);
|
||||
|
||||
int my_ssl_start(MYSQL *mysql);
|
||||
void my_ssl_end();
|
||||
|
||||
#endif /* HAVE_OPENSSL */
|
||||
#endif /* _my_secure_h_ */
|
@@ -234,7 +234,7 @@ struct st_mysql_options {
|
||||
|
||||
typedef struct st_mysql {
|
||||
NET net; /* Communication parameters */
|
||||
unsigned char *connector_fd; /* ConnectorFd for SSL */
|
||||
unsigned char *unused;
|
||||
char *host,*user,*passwd,*unix_socket,*server_version,*host_info;
|
||||
char *info,*db;
|
||||
const struct charset_info_st *charset; /* character set */
|
||||
@@ -368,7 +368,7 @@ MYSQL * STDCALL mysql_init(MYSQL *mysql);
|
||||
int STDCALL mysql_ssl_set(MYSQL *mysql, const char *key,
|
||||
const char *cert, const char *ca,
|
||||
const char *capath);
|
||||
char * STDCALL mysql_ssl_cipher(MYSQL *mysql);
|
||||
const char * STDCALL mysql_get_ssl_cipher(MYSQL *mysql);
|
||||
int STDCALL mysql_ssl_clear(MYSQL *mysql);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
MYSQL * STDCALL mysql_connect(MYSQL *mysql, const char *host,
|
||||
|
@@ -144,6 +144,7 @@ enum enum_server_command
|
||||
#define CLIENT_PS_MULTI_RESULTS (1UL << 18)
|
||||
#define CLIENT_PLUGIN_AUTH (1UL << 19)
|
||||
#define CLIENT_PROGRESS (1UL << 29) /* client supports progress indicator */
|
||||
#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
|
||||
|
||||
#define CLIENT_SUPPORTED_FLAGS (CLIENT_LONG_PASSWORD | \
|
||||
CLIENT_LONG_PASSWORD |\
|
||||
@@ -164,7 +165,8 @@ enum enum_server_command
|
||||
CLIENT_SECURE_CONNECTION |\
|
||||
CLIENT_MULTI_STATEMENTS |\
|
||||
CLIENT_MULTI_RESULTS |\
|
||||
CLIENT_PROGRESS)
|
||||
CLIENT_PROGRESS |\
|
||||
CLIENT_SSL_VERIFY_SERVER_CERT)
|
||||
|
||||
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD |\
|
||||
CLIENT_LONG_FLAG |\
|
||||
|
@@ -31,6 +31,10 @@
|
||||
#include <Vio.h> /* Full VIO interface */
|
||||
#else
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
/* Simple vio interface in C; The functions are implemented in violite.c */
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -43,6 +47,10 @@ struct st_vio; /* Only C */
|
||||
typedef struct st_vio Vio;
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#define HANDLE void *
|
||||
#endif
|
||||
|
||||
enum enum_vio_type { VIO_CLOSED, VIO_TYPE_TCPIP, VIO_TYPE_SOCKET,
|
||||
VIO_TYPE_NAMEDPIPE, VIO_TYPE_SSL};
|
||||
|
||||
@@ -53,6 +61,9 @@ Vio* vio_new(my_socket sd,
|
||||
Vio* vio_new_win32pipe(HANDLE hPipe);
|
||||
#endif
|
||||
void vio_delete(Vio* vio);
|
||||
void vio_reset(Vio* vio, enum enum_vio_type type,
|
||||
my_socket sd, HANDLE hPipe,
|
||||
my_bool localhost);
|
||||
|
||||
/*
|
||||
* vio_read and vio_write should have the same semantics
|
||||
@@ -117,10 +128,6 @@ void vio_in_addr(Vio *vio, struct in_addr *in);
|
||||
my_bool vio_poll_read(Vio *vio,uint timeout);
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
#define HANDLE void *
|
||||
#endif
|
||||
|
||||
struct st_vio
|
||||
{
|
||||
my_socket sd; /* my_socket - real or imaginary */
|
||||
@@ -131,6 +138,9 @@ struct st_vio
|
||||
struct sockaddr_in remote; /* Remote internet address */
|
||||
enum enum_vio_type type; /* Type of connection */
|
||||
char desc[30]; /* String description */
|
||||
#ifdef HAVE_OPENSSL
|
||||
SSL *ssl;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -1,6 +1,7 @@
|
||||
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/zlib
|
||||
${CMAKE_SOURCE_DIR}/libmysql)
|
||||
${CMAKE_SOURCE_DIR}/libmysql
|
||||
${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
SET(LIBMYSQL_VERSION "16.0.0")
|
||||
|
||||
@@ -26,29 +27,22 @@ SET(LIBMYSQL_SOURCES array.c bchange.c bmove.c bmove_upp.c my_charset.c
|
||||
strend.c strfill.c string.c strinstr.c strmake.c
|
||||
strmov.c strnmov.c strtoll.c strtoull.c
|
||||
strxmov.c strxnmov.c thr_mutex.c typelib.c sha1.c my_stmt.c
|
||||
my_loaddata.c my_stmt_codec.c client_plugin.c my_auth.c)
|
||||
|
||||
my_loaddata.c my_stmt_codec.c client_plugin.c my_auth.c my_secure.c)
|
||||
|
||||
ADD_LIBRARY(mysqlclient STATIC ${LIBMYSQL_SOURCES})
|
||||
TARGET_LINK_LIBRARIES(mysqlclient ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} zlib)
|
||||
|
||||
TARGET_LINK_LIBRARIES(mysqlclient ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} ${OPENSSL_LIBRARIES} zlib)
|
||||
IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(mysqlclient m)
|
||||
ENDIF(UNIX)
|
||||
|
||||
ADD_LIBRARY(libmysql SHARED ${LIBMYSQL_SOURCES})
|
||||
TARGET_LINK_LIBRARIES(libmysql ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} zlib)
|
||||
TARGET_LINK_LIBRARIES(libmysql ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} ${OPENSSL_LIBRARIES} zlib)
|
||||
|
||||
IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(libmysql ws2_32)
|
||||
TARGET_LINK_LIBRARIES(mysqlclient ws2_32)
|
||||
ENDIF(WIN32)
|
||||
|
||||
IF(OPENSSL_LIBRARIES)
|
||||
TARGET_LINK_LIBRARIES(mysqlclient ${SSL_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(libmysql ${SSL_LIBRARIES})
|
||||
ENDIF(OPENSSL_LIBRARIES)
|
||||
|
||||
IF(NOT WIN32)
|
||||
# we don't want a liblibmysql
|
||||
SET_TARGET_PROPERTIES(libmysql PROPERTIES PREFIX "")
|
||||
|
@@ -106,7 +106,7 @@ const char *client_errors[]=
|
||||
/* 2023 */ "",
|
||||
/* 2024 */ "",
|
||||
/* 2025 */ "",
|
||||
/* 2026 */ "",
|
||||
/* 2026 */ "SSL connection error",
|
||||
/* 2027 */ "received malformed packet",
|
||||
/* 2028 */ "",
|
||||
/* 2029 */ "",
|
||||
|
@@ -60,6 +60,9 @@
|
||||
#endif
|
||||
#include <sha1.h>
|
||||
#include <violite.h>
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <my_secure.h>
|
||||
#endif
|
||||
|
||||
static my_bool mysql_client_init=0;
|
||||
extern my_bool my_init_done;
|
||||
@@ -1321,14 +1324,18 @@ mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
|
||||
/**************************************************************************
|
||||
**************************************************************************/
|
||||
|
||||
char * STDCALL
|
||||
mysql_ssl_cipher(MYSQL *mysql)
|
||||
const char * STDCALL
|
||||
mysql_get_ssl_cipher(MYSQL *mysql)
|
||||
{
|
||||
// return (char *)mysql->net.vio->cipher_description();
|
||||
return NULL;
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (mysql->net.vio && mysql->net.vio->ssl)
|
||||
{
|
||||
return SSL_get_cipher_name(mysql->net.vio->ssl);
|
||||
}
|
||||
#endif
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
** Free strings in the SSL structure and clear 'use_ssl' flag.
|
||||
** NB! Errors are not reported until you do mysql_real_connect.
|
||||
@@ -1914,13 +1921,37 @@ mysql_select_db(MYSQL *mysql, const char *db)
|
||||
** If handle is alloced by mysql connect free it.
|
||||
*************************************************************************/
|
||||
|
||||
static void mysql_close_options(MYSQL *mysql)
|
||||
{
|
||||
/* todo
|
||||
my_free(mysql->options.init_command,MYF(MY_ALLOW_ZERO_PTR));
|
||||
*/
|
||||
my_free(mysql->options.user,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.host,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.password,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.unix_socket,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.db,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR));
|
||||
#ifdef HAVE_OPENSSL
|
||||
my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR));
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
static void mysql_close_memory(MYSQL *mysql)
|
||||
{
|
||||
my_free(mysql->host_info, MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->server_version,MYF(MY_ALLOW_ZERO_PTR));
|
||||
mysql->server_version=mysql->user=mysql->passwd=mysql->db=0;
|
||||
mysql_close_options(mysql);
|
||||
}
|
||||
|
||||
|
||||
@@ -1970,32 +2001,11 @@ mysql_close(MYSQL *mysql)
|
||||
SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
|
||||
}
|
||||
mysql_close_memory(mysql);
|
||||
//my_free(mysql->host_info,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->server_version,MYF(MY_ALLOW_ZERO_PTR));
|
||||
mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
|
||||
|
||||
/* todo
|
||||
my_free(mysql->options.init_command,MYF(MY_ALLOW_ZERO_PTR));
|
||||
*/
|
||||
my_free(mysql->options.user,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.host,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.password,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.unix_socket,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.db,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR));
|
||||
/* Clear pointers for better safety */
|
||||
bzero((char*) &mysql->options,sizeof(mysql->options));
|
||||
mysql->net.vio= 0;
|
||||
#ifdef HAVE_OPENSSL
|
||||
((VioConnectorFd*)(mysql->connector_fd))->delete();
|
||||
mysql->connector_fd = 0;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
if (mysql->free_me)
|
||||
my_free((gptr) mysql,MYF(0));
|
||||
}
|
||||
@@ -2916,6 +2926,9 @@ void STDCALL mysql_server_end()
|
||||
{
|
||||
if (!mysql_client_init)
|
||||
return;
|
||||
#ifdef HAVE_OPENSSL
|
||||
my_ssl_end();
|
||||
#endif
|
||||
|
||||
mysql_client_plugin_deinit();
|
||||
|
||||
|
@@ -5,6 +5,9 @@
|
||||
#include <mysql.h>
|
||||
#include <mysql/client_plugin.h>
|
||||
#include <violite.h>
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <my_secure.h>
|
||||
#endif
|
||||
|
||||
typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
|
||||
static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t);
|
||||
@@ -316,11 +319,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (mysql->client_flag & CLIENT_SSL)
|
||||
{
|
||||
/* Do the SSL layering. */
|
||||
struct st_mysql_options *options= &mysql->options;
|
||||
struct st_VioSSLFd *ssl_fd;
|
||||
char error_string[1024];
|
||||
|
||||
SSL *ssl;
|
||||
/*
|
||||
Send mysql->client_flag, max_packet_size - unencrypted otherwise
|
||||
the server does not know we want to do SSL
|
||||
@@ -334,39 +333,17 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Create the VioSSLConnectorFd - init SSL and load certs */
|
||||
if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key,
|
||||
options->ssl_cert,
|
||||
options->ssl_ca,
|
||||
options->ssl_capath,
|
||||
options->ssl_cipher)))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0);
|
||||
goto error;
|
||||
}
|
||||
mysql->connector_fd= (void*)ssl_fd;
|
||||
/* Create SSL */
|
||||
if (!(ssl= my_ssl_init(mysql)))
|
||||
goto error;
|
||||
|
||||
/* Connect to the server */
|
||||
DBUG_PRINT("info", ("IO layer change in progress..."));
|
||||
if (sslconnect(ssl_fd, net->vio,
|
||||
(long) (mysql->options.connect_timeout),
|
||||
error_string))
|
||||
if (my_ssl_connect(ssl))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR,
|
||||
SQLSTATE_UNKNOWN, "SSL error: %s",
|
||||
error_string[0] ? error_string :
|
||||
ER(CR_SSL_CONNECTION_ERROR));
|
||||
goto error;
|
||||
}
|
||||
DBUG_PRINT("info", ("IO layer change done!"));
|
||||
|
||||
/* Verify server cert */
|
||||
if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
|
||||
ssl_verify_server_cert(net->vio, mysql->host))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0);
|
||||
SSL_free(ssl);
|
||||
goto error;
|
||||
}
|
||||
/* todo: server certification verification */
|
||||
}
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
|
425
libmysql/my_secure.c
Normal file
425
libmysql/my_secure.c
Normal file
@@ -0,0 +1,425 @@
|
||||
/************************************************************************************
|
||||
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
|
||||
|
||||
*************************************************************************************/
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_secure.h>
|
||||
#include <errmsg.h>
|
||||
#include <violite.h>
|
||||
|
||||
static my_bool my_ssl_initialized= FALSE;
|
||||
static SSL_CTX *SSL_context= NULL;
|
||||
|
||||
#define MAX_SSL_ERR_LEN 100
|
||||
|
||||
#ifdef THREAD
|
||||
extern pthread_mutex_t LOCK_ssl_config;
|
||||
static pthread_mutex_t *LOCK_crypto;
|
||||
#endif
|
||||
|
||||
/*
|
||||
SSL error handling
|
||||
*/
|
||||
static void my_SSL_error(MYSQL *mysql)
|
||||
{
|
||||
ulong ssl_errno= ERR_get_error();
|
||||
char ssl_error[MAX_SSL_ERR_LEN];
|
||||
const char *ssl_error_reason;
|
||||
|
||||
DBUG_ENTER("my_SSL_error");
|
||||
|
||||
if (!ssl_errno)
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "No SSL error");
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
#ifdef THREAD
|
||||
/*
|
||||
thread safe callbacks for OpenSSL
|
||||
Crypto call back functions will be
|
||||
set during ssl_initialization
|
||||
*/
|
||||
static unsigned long my_cb_threadid(void)
|
||||
{
|
||||
/* chast pthread_t to unsigned long */
|
||||
return (unsigned long) pthread_self();
|
||||
}
|
||||
|
||||
static void
|
||||
my_cb_locking(int mode, int n, const char *file, int line)
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
pthread_mutex_lock(&LOCK_crypto[n]);
|
||||
else
|
||||
pthread_mutex_unlock(&LOCK_crypto[n]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Initializes SSL and allocate global
|
||||
context SSL_context
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_start
|
||||
mysql connection handle
|
||||
|
||||
RETURN VALUES
|
||||
0 success
|
||||
1 error
|
||||
*/
|
||||
int my_ssl_start(MYSQL *mysql)
|
||||
{
|
||||
int rc= 0;
|
||||
DBUG_ENTER("my_ssl_start");
|
||||
#ifdef THREAD
|
||||
/* lock mutex to prevent multiple initialization */
|
||||
pthread_mutex_lock(&LOCK_ssl_config);
|
||||
#endif
|
||||
|
||||
if (!my_ssl_initialized)
|
||||
{
|
||||
#ifdef THREAD
|
||||
if (!(LOCK_crypto=
|
||||
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) *
|
||||
CRYPTO_num_locks(), MYF(0))))
|
||||
{
|
||||
rc= 1;
|
||||
goto end;
|
||||
} else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < CRYPTO_num_locks(); i++)
|
||||
pthread_mutex_init(&LOCK_crypto[i], NULL);
|
||||
CRYPTO_set_id_callback(my_cb_threadid);
|
||||
CRYPTO_set_locking_callback(my_cb_locking);
|
||||
}
|
||||
#endif
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
|
||||
OPENSSL_config(NULL);
|
||||
#endif
|
||||
|
||||
/* always returns 1, so we can discard return code */
|
||||
SSL_library_init();
|
||||
/* load errors */
|
||||
SSL_load_error_strings();
|
||||
/* digests and ciphers */
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
if (!(SSL_context= SSL_CTX_new(TLSv1_client_method())))
|
||||
{
|
||||
my_SSL_error(mysql);
|
||||
rc= 1;
|
||||
goto end;
|
||||
}
|
||||
my_ssl_initialized= TRUE;
|
||||
}
|
||||
end:
|
||||
#ifdef THREAD
|
||||
pthread_mutex_unlock(&LOCK_ssl_config);
|
||||
#endif
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
Release SSL and free resources
|
||||
Will be automatically executed by
|
||||
mysql_server_end() function
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_end()
|
||||
void
|
||||
|
||||
RETURN VALUES
|
||||
void
|
||||
*/
|
||||
void my_ssl_end()
|
||||
{
|
||||
DBUG_ENTER("my_ssl_end");
|
||||
#ifdef THREAD
|
||||
pthread_mutex_lock(&LOCK_ssl_config);
|
||||
#endif
|
||||
if (my_ssl_initialized)
|
||||
{
|
||||
#ifdef THREAD
|
||||
int i;
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
CRYPTO_set_id_callback(NULL);
|
||||
|
||||
for (i=0; i < CRYPTO_num_locks(); i++)
|
||||
pthread_mutex_destroy(&LOCK_crypto[i]);
|
||||
|
||||
my_free((gptr)LOCK_crypto, MYF(0));
|
||||
#endif
|
||||
if (SSL_context)
|
||||
{
|
||||
SSL_CTX_free(SSL_context);
|
||||
SSL_context= FALSE;
|
||||
}
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CONF_modules_unload(1);
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
my_ssl_initialized= FALSE;
|
||||
}
|
||||
#ifdef THREAD
|
||||
pthread_mutex_unlock(&LOCK_ssl_config);
|
||||
#endif
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
#ifdef THREAD
|
||||
#endif
|
||||
|
||||
/*
|
||||
Set certification stuff.
|
||||
*/
|
||||
static int my_ssl_set_certs(SSL *ssl)
|
||||
{
|
||||
int have_cert= 0;
|
||||
MYSQL *mysql;
|
||||
|
||||
DBUG_ENTER("my_ssl_connect");
|
||||
|
||||
/* Make sure that ssl was allocated and
|
||||
ssl_system was initialized */
|
||||
DBUG_ASSERT(ssl != NULL);
|
||||
DBUG_ASSERT(my_ssl_initialized == TRUE);
|
||||
|
||||
/* get connection for current ssl */
|
||||
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||
|
||||
/* add cipher */
|
||||
if ((mysql->options.ssl_cipher &&
|
||||
mysql->options.ssl_cipher[0] != 0) &&
|
||||
SSL_set_cipher_list(ssl, mysql->options.ssl_cipher) == 0)
|
||||
goto error;
|
||||
|
||||
/* set cert */
|
||||
if (mysql->options.ssl_cert && mysql->options.ssl_cert[0] != 0)
|
||||
{
|
||||
if ((SSL_CTX_use_certificate_chain_file(SSL_context, mysql->options.ssl_cert) != 1) &&
|
||||
(SSL_use_certificate_file(ssl, mysql->options.ssl_cert, SSL_FILETYPE_PEM) != 1))
|
||||
goto error;
|
||||
have_cert= 1;
|
||||
}
|
||||
|
||||
/* set key */
|
||||
if (mysql->options.ssl_key && mysql->options.ssl_key[0])
|
||||
{
|
||||
if (SSL_use_PrivateKey_file(ssl, mysql->options.ssl_key, SSL_FILETYPE_PEM) != 1)
|
||||
goto error;
|
||||
|
||||
/* verify key */
|
||||
if (have_cert && SSL_check_private_key(ssl) != 1)
|
||||
goto error;
|
||||
}
|
||||
/* ca_file and ca_path */
|
||||
if (SSL_CTX_load_verify_locations(SSL_context,
|
||||
mysql->options.ssl_ca,
|
||||
mysql->options.ssl_capath) == 0)
|
||||
{
|
||||
if (SSL_CTX_set_default_verify_paths(SSL_context) == 0)
|
||||
goto error;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
|
||||
error:
|
||||
my_SSL_error(mysql);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
static int my_verify_callback(int ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
/* since we don't have access to the mysql structure, we just return */
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
allocates a new ssl object
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_init
|
||||
mysql connection object
|
||||
|
||||
RETURN VALUES
|
||||
NULL on error
|
||||
SSL new SSL object
|
||||
*/
|
||||
SSL *my_ssl_init(MYSQL *mysql)
|
||||
{
|
||||
int verify;
|
||||
SSL *ssl= NULL;
|
||||
|
||||
DBUG_ENTER("my_get_ssl");
|
||||
|
||||
DBUG_ASSERT(mysql->net.vio->ssl == NULL);
|
||||
|
||||
if (!my_ssl_initialized)
|
||||
my_ssl_start(mysql);
|
||||
|
||||
if (!(ssl= SSL_new(SSL_context)))
|
||||
goto error;
|
||||
|
||||
if (!SSL_set_app_data(ssl, mysql))
|
||||
goto error;
|
||||
|
||||
if (my_ssl_set_certs(ssl))
|
||||
goto error;
|
||||
|
||||
verify= (!mysql->options.ssl_ca && !mysql->options.ssl_capath) ?
|
||||
SSL_VERIFY_NONE : SSL_VERIFY_PEER;
|
||||
SSL_set_verify(ssl, verify, my_verify_callback);
|
||||
|
||||
DBUG_RETURN(ssl);
|
||||
error:
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
establish SSL connection between client
|
||||
and server
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_connect
|
||||
ssl ssl object
|
||||
|
||||
RETURN VALUES
|
||||
0 success
|
||||
1 error
|
||||
*/
|
||||
int my_ssl_connect(SSL *ssl)
|
||||
{
|
||||
my_bool blocking;
|
||||
MYSQL *mysql;
|
||||
|
||||
DBUG_ENTER("my_ssl_connect");
|
||||
|
||||
DBUG_ASSERT(ssl != NULL);
|
||||
|
||||
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||
|
||||
/* Set socket to blocking if not already set */
|
||||
if (!(blocking= vio_is_blocking(mysql->net.vio)))
|
||||
vio_blocking(mysql->net.vio, TRUE);
|
||||
|
||||
SSL_clear(ssl);
|
||||
SSL_SESSION_set_timeout(SSL_get_session(ssl),
|
||||
mysql->options.connect_timeout);
|
||||
SSL_set_fd(ssl, mysql->net.vio->sd);
|
||||
|
||||
if (SSL_connect(ssl) != 1)
|
||||
{
|
||||
my_SSL_error(mysql);
|
||||
/* restore blocking mode */
|
||||
if (!blocking)
|
||||
vio_blocking(mysql->net.vio, FALSE);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
vio_reset(mysql->net.vio, VIO_TYPE_SSL, mysql->net.vio->sd, 0, 0);
|
||||
mysql->net.vio->ssl= ssl;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
write to ssl socket
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_write()
|
||||
vio vio
|
||||
buf write buffer
|
||||
size size of buffer
|
||||
|
||||
RETURN VALUES
|
||||
bytes written
|
||||
*/
|
||||
size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size)
|
||||
{
|
||||
size_t written;
|
||||
DBUG_ENTER("my_ssl_write");
|
||||
|
||||
written= SSL_write((SSL*) vio->ssl, buf, size);
|
||||
DBUG_RETURN(written);
|
||||
}
|
||||
|
||||
/*
|
||||
read from ssl socket
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_read()
|
||||
vio vio
|
||||
buf read buffer
|
||||
size_t max number of bytes to read
|
||||
|
||||
RETURN VALUES
|
||||
number of bytes read
|
||||
*/
|
||||
size_t my_ssl_read(Vio *vio, uchar* buf, size_t size)
|
||||
{
|
||||
size_t read;
|
||||
DBUG_ENTER("my_ssl_read");
|
||||
|
||||
read= SSL_read((SSL*) vio->ssl, buf, size);
|
||||
DBUG_RETURN(read);
|
||||
}
|
||||
|
||||
/*
|
||||
close ssl connection and free
|
||||
ssl object
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_close()
|
||||
vio vio
|
||||
|
||||
RETURN VALUES
|
||||
1 ok
|
||||
0 or -1 on error
|
||||
*/
|
||||
int my_ssl_close(Vio *vio)
|
||||
{
|
||||
int i, rc;
|
||||
DBUG_ENTER("my_ssl_close");
|
||||
|
||||
/* 2 x pending + 2 * data = 4 */
|
||||
for (i=0; i < 4; i++)
|
||||
if ((rc= SSL_shutdown(vio->ssl)))
|
||||
break;
|
||||
|
||||
SSL_free(vio->ssl);
|
||||
vio->ssl= NULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* HAVE_OPENSSL */
|
@@ -32,6 +32,9 @@ pthread_key(struct st_my_thread_var, THR_KEY_mysys);
|
||||
pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,THR_LOCK_keycache,
|
||||
THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_myisam,THR_LOCK_heap,
|
||||
THR_LOCK_net, THR_LOCK_charset;
|
||||
#ifdef HAVE_OPENSSL
|
||||
pthread_mutex_t LOCK_ssl_config;
|
||||
#endif
|
||||
#ifndef HAVE_LOCALTIME_R
|
||||
pthread_mutex_t LOCK_localtime_r;
|
||||
#endif
|
||||
@@ -65,7 +68,9 @@ my_bool my_thread_global_init(void)
|
||||
pthread_mutexattr_setkind_np(&my_errchk_mutexattr,
|
||||
PTHREAD_MUTEX_ERRORCHECK_NP);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
pthread_mutex_init(&LOCK_ssl_config,MY_MUTEX_INIT_FAST);
|
||||
#endif
|
||||
pthread_mutex_init(&THR_LOCK_malloc,MY_MUTEX_INIT_FAST);
|
||||
pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST);
|
||||
pthread_mutex_init(&THR_LOCK_keycache,MY_MUTEX_INIT_FAST);
|
||||
@@ -98,6 +103,9 @@ void my_thread_global_end(void)
|
||||
#ifdef PPTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
|
||||
pthread_mutexattr_destroy(&my_errchk_mutexattr);
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL
|
||||
pthread_mutex_destroy(&LOCK_ssl_config);
|
||||
#endif
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
pthread_mutex_destroy(&LOCK_gethostbyname_r);
|
||||
#endif
|
||||
|
@@ -41,6 +41,10 @@
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <my_secure.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define socklen_t int
|
||||
#pragma comment (lib, "ws2_32")
|
||||
@@ -73,9 +77,9 @@ typedef char *vio_cstring;
|
||||
* Helper to fill most of the Vio* with defaults.
|
||||
*/
|
||||
|
||||
static void vio_reset(Vio* vio, enum enum_vio_type type,
|
||||
my_socket sd, HANDLE hPipe,
|
||||
my_bool localhost)
|
||||
void vio_reset(Vio* vio, enum enum_vio_type type,
|
||||
my_socket sd, HANDLE hPipe,
|
||||
my_bool localhost)
|
||||
{
|
||||
bzero((char*) vio, sizeof(*vio));
|
||||
vio->type= type;
|
||||
@@ -187,6 +191,15 @@ int vio_read(Vio * vio, gptr buf, int size)
|
||||
int r;
|
||||
DBUG_ENTER("vio_read");
|
||||
DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size));
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (vio->type == VIO_TYPE_SSL)
|
||||
{
|
||||
r= my_ssl_read(vio, (uchar *)buf, size);
|
||||
DBUG_RETURN(r);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined( _WIN32) || defined(OS2)
|
||||
if (vio->type == VIO_TYPE_NAMEDPIPE)
|
||||
{
|
||||
@@ -221,6 +234,13 @@ int vio_write(Vio * vio, const gptr buf, int size)
|
||||
int r;
|
||||
DBUG_ENTER("vio_write");
|
||||
DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size));
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (vio->type == VIO_TYPE_SSL)
|
||||
{
|
||||
r= my_ssl_write(vio, (uchar *)buf, size);
|
||||
DBUG_RETURN(r);
|
||||
}
|
||||
#endif
|
||||
#if defined( _WIN32) || defined(OS2)
|
||||
if ( vio->type == VIO_TYPE_NAMEDPIPE)
|
||||
{
|
||||
@@ -360,6 +380,10 @@ int vio_close(Vio * vio)
|
||||
{
|
||||
int r;
|
||||
DBUG_ENTER("vio_close");
|
||||
if (vio->type == VIO_TYPE_SSL)
|
||||
{
|
||||
r = my_ssl_close(vio);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (vio->type == VIO_TYPE_NAMEDPIPE)
|
||||
{
|
||||
|
33
unittest/libmysql/CMakeLists.txt
Normal file
33
unittest/libmysql/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# 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
|
||||
|
||||
ENABLE_TESTING()
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/unittest/mytap)
|
||||
|
||||
SET(API_TESTS "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs"
|
||||
"sp" "result" "connection" "misc" "ssl")
|
||||
|
||||
FOREACH(API_TEST ${API_TESTS})
|
||||
ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c)
|
||||
TARGET_LINK_LIBRARIES(${API_TEST} mytap mysqlclient)
|
||||
ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST})
|
||||
SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 120)
|
||||
ENDFOREACH(API_TEST)
|
451
unittest/libmysql/basic-t.c
Normal file
451
unittest/libmysql/basic-t.c
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
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 basic_connect(MYSQL *mysql)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *res;
|
||||
int rc;
|
||||
|
||||
MYSQL *my= mysql_init(NULL);
|
||||
FAIL_IF(!my, "mysql_init() failed");
|
||||
|
||||
FAIL_IF(!mysql_real_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));
|
||||
|
||||
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(strcmp(row[0], "utf8"), "wrong character set");
|
||||
}
|
||||
FAIL_IF(mysql_errno(my), mysql_error(my));
|
||||
|
||||
|
||||
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, "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, query, strlen(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)
|
||||
{
|
||||
my_ulonglong 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);
|
||||
/* 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, "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, "create table t2 (f1 int not null primary key auto_increment, f2 varchar(255))");
|
||||
check_mysql_rc(rc, mysql);
|
||||
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, "drop table t2");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
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, "create table t2 (f1 int not null primary key auto_increment, f2 varchar(255))");
|
||||
check_mysql_rc(rc, mysql);
|
||||
rc= mysql_query(mysql, "insert into t2 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 t2 values (null,'b')");
|
||||
check_mysql_rc(rc, mysql);
|
||||
res= mysql_insert_id(mysql);
|
||||
FAIL_UNLESS(res == 2, "");
|
||||
rc= mysql_query(mysql, "insert into t2 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 t2 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 t2 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 t2 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 t2 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 t2 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 t2 select 12,'a' union select 13,'b'");
|
||||
FAIL_IF(!rc, "Error expected");
|
||||
rc= mysql_query(mysql, "insert ignore into t2 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 t2 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 t2 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 t2 values (null,'e'),(12,'a'),(13,'b')");
|
||||
FAIL_IF(!rc, "Error expected");
|
||||
rc= mysql_query(mysql, "insert into t2 values (null,'e'),(12,'a'),(13,'b'),(25,'g')");
|
||||
FAIL_IF(!rc, "Error expected");
|
||||
rc= mysql_query(mysql, "insert into t2 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().
|
||||
*/
|
||||
FAIL_UNLESS(res == 20, "");
|
||||
/* If first autogenerated number fails and 2nd works: */
|
||||
rc= mysql_query(mysql, "drop table t2");
|
||||
check_mysql_rc(rc, mysql);
|
||||
rc= mysql_query(mysql, "create table t2 (f1 int not null primary key "
|
||||
"auto_increment, f2 varchar(255), unique (f2))");
|
||||
check_mysql_rc(rc, mysql);
|
||||
rc= mysql_query(mysql, "insert into t2 values (null,'e')");
|
||||
res= mysql_insert_id(mysql);
|
||||
FAIL_UNLESS(res == 1, "");
|
||||
rc= mysql_query(mysql, "insert ignore into t2 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 t2 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 t2 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 t2 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 t2 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 t2 values (null,'g')");
|
||||
check_mysql_rc(rc, mysql);
|
||||
res= mysql_insert_id(mysql);
|
||||
FAIL_UNLESS(res == 15, "");
|
||||
rc= mysql_query(mysql, "update t2 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 t2 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");
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"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},
|
||||
{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());
|
||||
}
|
38
unittest/libmysql/ca.pem
Normal file
38
unittest/libmysql/ca.pem
Normal file
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDGDCCAgACAQEwDQYJKoZIhvcNAQEFBQAwUjELMAkGA1UEBhMCREUxCzAJBgNV
|
||||
BAgMAkJXMRMwEQYDVQQHDApIZWlkZWxiZXJnMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTIxMTIyMDcxMDM5WhcNMjIxMDAxMDcxMDM5WjBS
|
||||
MQswCQYDVQQGEwJERTELMAkGA1UECAwCQlcxEzARBgNVBAcMCkhlaWRlbGJlcmcx
|
||||
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAOKiAyhtJXHgjr0cLFx+gYqBZCzg+Bpevh3/+U0t
|
||||
A5trng5kht8dbI6m0Qjz8Mi09sFxaHmmL6WA+wxL8LqwMjOXpn3aAjNW3+QFu5Ei
|
||||
Iy+8KrwdJdZVzRHCCLt4HWpeMQBzn2y/MUgZzc8+RhcQSu2KVDBiKLVpa6Z9k3gl
|
||||
wsezI8ClJ6vWsJGnJX699H9BhMyS85ipVmeL69h5tWsdHQtmbK+XdHPQldi9r/88
|
||||
f2VfIOo7EFSm9ohJG70P8lhEIqByhQ8Hw0akGWLLsLg4cufPVrOdPZocJ/qJjQVG
|
||||
OkfSPkIgwKnpzGbXjFG5IMh5rXJCIRbO3ofTxGpSTzNQ0hcCAwEAATANBgkqhkiG
|
||||
9w0BAQUFAAOCAQEAb7bIszKyzpCvom4FjnNYT3buQCf0qnUGoPgVpXIpjc4Lsyr0
|
||||
nmIfgGNo/+5B1cj3iAtIuSojXOK96r8a84TueCaeX9ZDdG+ZZm9ng6mIiyQraZyR
|
||||
Gl+VsTH40O0QTjMcPB344Yz0ZSHU1E35LzarApHtqZi9TpCBFc0td1EhxX7rdEOD
|
||||
WzBRTKcMzV+Y0Fslqjy73JVYnaxJ/ZShW7TOowrdjE9DZ8VZ7dVSJOtdTLB5WNQE
|
||||
mxFInjbUig5vvHzmf4bEsBDz7RXy0W8fMQd2HEcgGBDwdQYq18kZl9H5plORDCgg
|
||||
S93U+OoInjEU2KEWyDyiBI7OwAZYIQytrxDBOw==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDGDCCAgACAQEwDQYJKoZIhvcNAQEFBQAwUjELMAkGA1UEBhMCREUxCzAJBgNV
|
||||
BAgMAkJXMRMwEQYDVQQHDApIZWlkZWxiZXJnMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTIxMTIyMDcxMTI0WhcNMjIxMDAxMDcxMTI0WjBS
|
||||
MQswCQYDVQQGEwJERTELMAkGA1UECAwCQlcxEzARBgNVBAcMCkhlaWRlbGJlcmcx
|
||||
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBANofs7wzZpUourQz03gALCHcTYXbuTJ16w/rbvql
|
||||
WUa1R/qUgaxN9k7SEj260Vr7PMEREAIdKIu54jCy/yCRzYb/03HorQjJgGXjYvtX
|
||||
nmwwUgLZSz3aLIX2p7jcw3ESiqN1/oZ3fB8+i6HT8igFcmbAOkPEN8TBM9Qenqj7
|
||||
NNx9iYAOp7r8xJXJXTEWBIy0kJ2eXZQacveLGPgFs6Qq0Hvn8FsXT9zQQH98BQhL
|
||||
o35vjxas/A8ThZiKd8cCmUbTtGxIlncR7FmJuqKAJVTSg/ZePFoYqW0s9GAtPJfC
|
||||
DVdaT94uGZIWtOCLPqQgiEyjdHWHdeF+WBdXex3xRI3Ii+UCAwEAATANBgkqhkiG
|
||||
9w0BAQUFAAOCAQEAKSXEbcpfgqZiWIJBpQX86kNMWhjA4m8GKwXoxhgxTtEZPju/
|
||||
VO/ehjsTo8AnRQdW4/sD+KgVqn6F4jw5WVwK6L0TTlat5umn+zKW9c72Cmsf7kiZ
|
||||
pc6bluyKv1uhS5pK1HLjQaL8vY4WExHkh8nGEuS4IIhAtHzBblE3G4/Kdq7V7IO7
|
||||
+YaSwO1nRiYaFbrZkF8u+GOIVJlcQ7C7m2332c0NFYBmYoeJ03rwb8kWe40UHaiP
|
||||
R3Pl/bzrRbcHiSqLawFpfYOG1+Sq9GkBwysv6ADU4wKcu9dYNvjgbRHhHuSLB3am
|
||||
Dnj09lCHMDxHUtk1PhLsxG65lMw4GaUEqjfUmg==
|
||||
-----END CERTIFICATE-----
|
683
unittest/libmysql/charset.c
Normal file
683
unittest/libmysql/charset.c
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
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, "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*) "utf8";
|
||||
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, "utf8") || strcmp(cs.name, "utf8_general_ci"), "Character set != UTF8");
|
||||
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), NULL);
|
||||
|
||||
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];
|
||||
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, stmt_text, strlen(stmt_text));
|
||||
check_mysql_rc(rc, mysql);
|
||||
stmt_text= "CREATE TABLE t1 (a TEXT) DEFAULT CHARSET latin1";
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(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, stmt_text, strlen(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, stmt_text, strlen(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, stmt_text, strlen(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, stmt_text, strlen(stmt_text));
|
||||
check_mysql_rc(rc, mysql);
|
||||
stmt_text= "SET NAMES DEFAULT";
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(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];
|
||||
|
||||
DBUG_ENTER("test_bug27876");
|
||||
|
||||
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, stmt_text, strlen(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, stmt_text, strlen(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, stmt_text, strlen(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= strlen(koi8);
|
||||
|
||||
bind_array[1].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind_array[1].buffer= (void *) koi8;
|
||||
bind_array[1].buffer_length= strlen(koi8);
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
check_stmt_rc(rc, stmt);
|
||||
|
||||
stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)";
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(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, stmt_text, strlen(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, stmt_text, strlen(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, stmt_text, strlen(stmt_text));
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
stmt_text= "INSERT INTO t1 (c1, c2) VALUES (?, ?)";
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(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= strlen(koi8);
|
||||
|
||||
bind_array[1].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind_array[1].buffer= (void *) koi8;
|
||||
bind_array[1].buffer_length= strlen(koi8);
|
||||
|
||||
mysql_stmt_bind_param(stmt, bind_array);
|
||||
|
||||
// mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8));
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_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= strlen(cp1251);
|
||||
|
||||
bind_array[1].buffer_type= MYSQL_TYPE_BLOB;
|
||||
bind_array[1].buffer= (void *) cp1251;
|
||||
bind_array[1].buffer_length= strlen(cp1251);
|
||||
|
||||
mysql_stmt_bind_param(stmt, bind_array);
|
||||
|
||||
// mysql_stmt_send_long_data(stmt, 0, cp1251, strlen(cp1251));
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_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, stmt_text, strlen(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, stmt_text, strlen(stmt_text));
|
||||
check_mysql_rc(rc, mysql);
|
||||
stmt_text= "SET NAMES DEFAULT";
|
||||
rc= mysql_real_query(mysql, stmt_text, strlen(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];
|
||||
|
||||
if (mysql_get_server_version(mysql) < 50100) {
|
||||
diag("Test requires MySQL Server version 5.1 or above");
|
||||
return SKIP;
|
||||
}
|
||||
/* Retrieve character set information. */
|
||||
|
||||
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, "utf8"), "Setting cs to utf8 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, "utf8") == 0, "cs_name != utf8");
|
||||
FAIL_UNLESS(strcmp(character_set_client_2, "utf8") == 0, "cs_client != utf8");
|
||||
FAIL_UNLESS(strcmp(character_set_results_2, "utf8") == 0, "cs_result != ut8");
|
||||
FAIL_UNLESS(strcmp(collation_connnection_2, "utf8_general_ci") == 0, "collation != utf8_general_ci");
|
||||
|
||||
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");
|
||||
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, "utf8");
|
||||
|
||||
/*
|
||||
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, "utf8") == 0, "cs_name != utf8");
|
||||
FAIL_UNLESS(strcmp(character_set_client_4, "utf8") == 0, "cs_client != utf8");
|
||||
FAIL_UNLESS(strcmp(character_set_results_4, "utf8") == 0, "cs_result != utf8");
|
||||
FAIL_UNLESS(strcmp(collation_connnection_4, "utf8_general_ci") == 0, "collation_connection != utf8_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") &&
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"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},
|
||||
{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());
|
||||
}
|
512
unittest/libmysql/connection.c
Normal file
512
unittest/libmysql/connection.c
Normal file
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
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_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;
|
||||
|
||||
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;
|
||||
|
||||
DBUG_ENTER("test_change_user");
|
||||
|
||||
/* 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];
|
||||
|
||||
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 epected");
|
||||
|
||||
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 mysql_real_connect() should fail if
|
||||
given an already connected MYSQL handle.
|
||||
*/
|
||||
|
||||
static int test_bug33831(MYSQL *mysql)
|
||||
{
|
||||
FAIL_IF(mysql_real_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;
|
||||
|
||||
mysql= mysql_init(NULL);
|
||||
FAIL_IF(!mysql, "not enough memory");
|
||||
|
||||
FAIL_UNLESS(mysql->reconnect == 0, "reconnect != 0");
|
||||
|
||||
rc= mysql_options(mysql, MYSQL_OPT_RECONNECT, &my_true);
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
FAIL_UNLESS(mysql->reconnect == 1, "reconnect != 1");
|
||||
|
||||
if (!(mysql_real_connect(mysql, hostname, username,
|
||||
password, schema, port,
|
||||
socketname, 0)))
|
||||
{
|
||||
diag("connection failed");
|
||||
mysql_close(mysql);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
FAIL_UNLESS(mysql->reconnect == 1, "reconnect != 1");
|
||||
|
||||
mysql_close(mysql);
|
||||
|
||||
mysql= mysql_init(NULL);
|
||||
FAIL_IF(!mysql, "not enough memory");
|
||||
|
||||
FAIL_UNLESS(mysql->reconnect == 0, "reconnect != 0");
|
||||
|
||||
if (!(mysql_real_connect(mysql, hostname, username,
|
||||
password, schema, port,
|
||||
socketname, 0)))
|
||||
{
|
||||
diag("connection failed");
|
||||
mysql_close(mysql);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
FAIL_UNLESS(mysql->reconnect == 0, "reconnect != 0");
|
||||
|
||||
mysql_close(mysql);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int test_compress(MYSQL *mysql)
|
||||
{
|
||||
MYSQL_RES *res;
|
||||
MYSQL_ROW row;
|
||||
int rc;
|
||||
|
||||
mysql= mysql_init(NULL);
|
||||
FAIL_IF(!mysql, "not enough memory");
|
||||
|
||||
/* use compressed protocol */
|
||||
rc= mysql_options(mysql, MYSQL_OPT_COMPRESS, NULL);
|
||||
|
||||
|
||||
|
||||
if (!(mysql_real_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;
|
||||
}
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"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},
|
||||
{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());
|
||||
}
|
1842
unittest/libmysql/cursor.c
Normal file
1842
unittest/libmysql/cursor.c
Normal file
File diff suppressed because it is too large
Load Diff
100
unittest/libmysql/data.csv
Normal file
100
unittest/libmysql/data.csv
Normal file
@@ -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
|
|
281
unittest/libmysql/errors.c
Normal file
281
unittest/libmysql/errors.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
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;
|
||||
char *query= "DROP TABLE IF EXISTS 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, query, strlen(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;
|
||||
|
||||
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;
|
||||
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, query, strlen(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;
|
||||
|
||||
mysql_query(mysql, "DROP TABLE if exists test_non_exists");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
rc= mysql_query(mysql, "DROP TABLE test_non_exists");
|
||||
|
||||
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;
|
||||
|
||||
|
||||
mysql_query(mysql, "DROP TABLE if exists t1");
|
||||
mysql_query(mysql, "DROP TABLE if exists t2");
|
||||
|
||||
rc= mysql_query(mysql, "CREATE TABLE t1(c1 tinyint)");
|
||||
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, "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);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int test_parse_error_and_bad_length(MYSQL *mysql)
|
||||
{
|
||||
MYSQL_STMT *stmt;
|
||||
int rc;
|
||||
|
||||
/* 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, "SHOW DATABASES", 100);
|
||||
FAIL_UNLESS(rc, "Error expected");
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
FAIL_IF(!stmt, mysql_error(mysql));
|
||||
rc= mysql_stmt_prepare(stmt, "SHOW DATABAAAA", strlen("SHOW DATABAAAA"));
|
||||
FAIL_IF(!rc, "Error expected");
|
||||
mysql_stmt_close(stmt);
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
FAIL_UNLESS(stmt, "");
|
||||
rc= mysql_stmt_prepare(stmt, "SHOW DATABASES", 100);
|
||||
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());
|
||||
}
|
904
unittest/libmysql/fetch.c
Normal file
904
unittest/libmysql/fetch.c
Normal file
@@ -0,0 +1,904 @@
|
||||
/*
|
||||
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;
|
||||
long 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, query, strlen(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, query, strlen(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];
|
||||
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, query, strlen(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[1];
|
||||
char data[11];
|
||||
ulong length;
|
||||
int rc;
|
||||
my_bool is_null;
|
||||
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))");
|
||||
check_mysql_rc(rc, mysql);
|
||||
rc= mysql_query(mysql, "insert into t1 values('abcdefghij'), (null)");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
FAIL_IF(!stmt, mysql_error(mysql));
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, query, strlen(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;
|
||||
my_bind[0].length= &length;
|
||||
|
||||
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);
|
||||
|
||||
rc= mysql_stmt_fetch(stmt);
|
||||
check_stmt_rc(rc,stmt);
|
||||
|
||||
data[0]= '\0';
|
||||
rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
|
||||
check_stmt_rc(rc,stmt);
|
||||
|
||||
|
||||
FAIL_IF(!(strncmp(data, "abcd", 4) == 0 && length == 10), "Wrong value");
|
||||
|
||||
rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 5);
|
||||
check_stmt_rc(rc,stmt);
|
||||
FAIL_IF(!(strncmp(data, "fg", 2) == 0 && length == 10), "Wrong value");
|
||||
|
||||
rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 9);
|
||||
check_stmt_rc(rc,stmt);
|
||||
FAIL_IF(!(strncmp(data, "j", 1) == 0 && length == 10), "Wrong value");
|
||||
|
||||
rc= mysql_stmt_fetch(stmt);
|
||||
check_stmt_rc(rc,stmt);
|
||||
|
||||
is_null= 0;
|
||||
|
||||
rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
|
||||
check_stmt_rc(rc,stmt);
|
||||
|
||||
FAIL_IF(is_null != 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;
|
||||
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, query, strlen(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;
|
||||
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, query, strlen(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;
|
||||
long nData;
|
||||
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, "Exoected 3 rows");
|
||||
|
||||
stmt = mysql_stmt_init(mysql);
|
||||
FAIL_IF(!stmt, mysql_error(mysql));
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, query, strlen(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, "Exoected !is_null");
|
||||
FAIL_UNLESS(length[i] == 4, "Expected length=4");
|
||||
}
|
||||
FAIL_UNLESS(rc == 3, "Expected 3 rows");
|
||||
mysql_stmt_close(stmt);
|
||||
|
||||
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];
|
||||
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, query, strlen(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(year == 2010, "year != 2010");
|
||||
FAIL_UNLESS(y_length == 4, "y_length != 4");
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
return bind_fetch(mysql, 3);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return bind_fetch(mysql, 4);
|
||||
}
|
||||
|
||||
|
||||
/* 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);
|
||||
return bind_fetch(mysql, 5);
|
||||
}
|
||||
|
||||
|
||||
/* 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);
|
||||
return bind_fetch(mysql, 3);
|
||||
}
|
||||
|
||||
|
||||
/* 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);
|
||||
return bind_fetch(mysql, 2);
|
||||
}
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
return bind_fetch(mysql, 2);
|
||||
}
|
||||
|
||||
|
||||
/* 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);
|
||||
return bind_fetch(mysql, 3);
|
||||
}
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"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());
|
||||
}
|
213
unittest/libmysql/logs.c
Normal file
213
unittest/libmysql/logs.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
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 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;
|
||||
}
|
||||
|
||||
|
||||
/* Test update/binary logs */
|
||||
|
||||
static int test_logs(MYSQL *mysql)
|
||||
{
|
||||
MYSQL_STMT *stmt;
|
||||
MYSQL_BIND my_bind[2];
|
||||
char data[255];
|
||||
ulong length;
|
||||
int rc;
|
||||
short id;
|
||||
|
||||
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, data, strlen(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= &length;
|
||||
|
||||
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, data, strlen(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, data, strlen(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());
|
||||
}
|
839
unittest/libmysql/misc.c
Normal file
839
unittest/libmysql/misc.c
Normal file
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
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#28075 "COM_DEBUG crashes mysqld"
|
||||
*/
|
||||
|
||||
static int test_bug28075(MYSQL *mysql)
|
||||
{
|
||||
int rc;
|
||||
|
||||
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)
|
||||
{
|
||||
my_ulonglong res;
|
||||
int rc;
|
||||
|
||||
rc= mysql_query(mysql, "drop table if exists t1");
|
||||
rc= mysql_query(mysql, "create table t1(f1 int primary key)");
|
||||
rc= mysql_query(mysql, "insert into t1 values(1)");
|
||||
rc= mysql_query(mysql, "insert into t1 values(1) on duplicate key update f1=1");
|
||||
res= mysql_affected_rows(mysql);
|
||||
FAIL_UNLESS(!res, "res != 0");
|
||||
rc= mysql_query(mysql, "drop table t1");
|
||||
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 *mysql)
|
||||
{
|
||||
int i;
|
||||
/* Run test case for BUG#31418 for three different connections. */
|
||||
|
||||
for (i=0; i < 3; i++)
|
||||
if (bug31418_impl())
|
||||
return FAIL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Altough mysql_create_db(), mysql_rm_db() are deprecated since 4.0 they
|
||||
should not crash server and should not hang in case of errors.
|
||||
|
||||
Since those functions can't be seen in modern API (unless client library
|
||||
was compiled with USE_OLD_FUNCTIONS define) we use simple_command() macro.
|
||||
*/
|
||||
static int test_bug6081(MYSQL *mysql)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (mysql_get_server_version(mysql) < 50100) {
|
||||
diag("Test requires MySQL Server version 5.1 or above");
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
rc= simple_command(mysql, MYSQL_COM_DROP_DB, (uchar*) schema,
|
||||
(ulong)strlen(schema), 0U);
|
||||
FAIL_IF(!rc, "Error expected");
|
||||
|
||||
rc= simple_command(mysql, MYSQL_COM_CREATE_DB, (uchar*) schema,
|
||||
(ulong)strlen(schema), 0U);
|
||||
FAIL_IF(!rc, "Error expected");
|
||||
|
||||
rc= mysql_select_db(mysql, schema);
|
||||
check_mysql_rc(rc, mysql);
|
||||
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[FN_REFLEN];
|
||||
int rc;
|
||||
|
||||
|
||||
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, "show variables like 'datadir'", strlen("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");
|
||||
|
||||
sprintf(test_frm, "%s/%s/test_frm_bug.frm", data_dir, schema);
|
||||
|
||||
|
||||
if (!(test_file= my_fopen(test_frm, (int) (O_RDWR | O_CREAT), MYF(MY_WME))))
|
||||
{
|
||||
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 != 0");
|
||||
|
||||
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);
|
||||
|
||||
my_fclose(test_file, MYF(0));
|
||||
mysql_query(mysql, "drop table if exists test_frm_bug");
|
||||
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];
|
||||
static 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, query, strlen(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, "select * from t1", strlen("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_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_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, "insert into t1 (year) values (?)", strlen("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= 10000;
|
||||
tm[0].month= 1; tm[0].day= 1;
|
||||
tm[0].hour= 1; tm[0].minute= 1; tm[0].second= 1;
|
||||
tm[0].second_part= 0; tm[0].neg= 0;
|
||||
|
||||
/* Cause a statement reprepare */
|
||||
rc= mysql_query(mysql, "alter table t1 add column c int");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_stmt_rc(rc, stmt);
|
||||
/*
|
||||
Sic: only one warning, instead of two. The warning
|
||||
about data truncation when assigning a parameter is lost.
|
||||
This is a bug.
|
||||
*/
|
||||
FAIL_IF(mysql_warning_count(mysql) != 1, "warning count != 1");
|
||||
|
||||
if (verify_col_data(mysql, "t1", "year", "0000-00-00 00:00:00")) {
|
||||
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;
|
||||
|
||||
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= 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, stmt_text, strlen(stmt_text));
|
||||
check_stmt_rc(rc, stmt);
|
||||
|
||||
mysql_stmt_bind_param(stmt, bind_array);
|
||||
|
||||
mysql_stmt_send_long_data(stmt, 0, koi8, strlen(koi8));
|
||||
|
||||
/* Cause a reprepare at statement execute */
|
||||
rc= mysql_query(mysql, "alter table t1 add column d int");
|
||||
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, stmt_text, strlen(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;
|
||||
|
||||
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");
|
||||
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;
|
||||
int i;
|
||||
FILE *fp;
|
||||
|
||||
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);
|
||||
return OK;
|
||||
}
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"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_bug6081", test_bug6081, 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},
|
||||
{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());
|
||||
}
|
517
unittest/libmysql/my_test.h
Normal file
517
unittest/libmysql/my_test.h
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
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_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <mysql.h>
|
||||
#include <tap.h>
|
||||
#include <getopt.h>
|
||||
#include <memory.h>
|
||||
#include <errmsg.h>
|
||||
|
||||
#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 MAX_KEY MAX_INDEXES
|
||||
#define MAX_KEY_LENGTH_DECIMAL_WIDTH 4 /* strlen("4096") */
|
||||
|
||||
#define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */
|
||||
|
||||
#define check_mysql_rc(rc, mysql) \
|
||||
if (rc)\
|
||||
{\
|
||||
diag("Error (%d): %s (%d) in %s line %d", rc, mysql_error(mysql), \
|
||||
mysql_errno(mysql), __FILE__, __LINE__);\
|
||||
return(FAIL);\
|
||||
}
|
||||
|
||||
#define check_stmt_rc(rc, stmt) \
|
||||
if (rc)\
|
||||
{\
|
||||
diag("Error: %s (%s: %d)", mysql_stmt_error(stmt), __FILE__, __LINE__);\
|
||||
return(FAIL);\
|
||||
}
|
||||
|
||||
#define FAIL_IF(expr, reason)\
|
||||
if (expr)\
|
||||
{\
|
||||
diag("Error: %s (%s: %d)", (reason) ? reason : "", __FILE__, __LINE__);\
|
||||
return FAIL;\
|
||||
}
|
||||
|
||||
#define FAIL_UNLESS(expr, reason)\
|
||||
if (!(expr))\
|
||||
{\
|
||||
diag("Error: %s (%s: %d)", reason, __FILE__, __LINE__);\
|
||||
return FAIL;\
|
||||
}
|
||||
|
||||
/* 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;
|
||||
char *skipmsg;
|
||||
};
|
||||
|
||||
static char *schema = "test";
|
||||
static char *hostname = 0;
|
||||
static char *password = 0;
|
||||
static unsigned int port = 0;
|
||||
static char *socketname = 0;
|
||||
static char *username = 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,
|
||||
const char *table,
|
||||
const char *org_table, const char *db,
|
||||
unsigned long length, const char *def,
|
||||
const char *file, int line)
|
||||
{
|
||||
MYSQL_FIELD *field;
|
||||
CHARSET_INFO *cs;
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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, 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("-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:P:S:?")) >= 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);
|
||||
break;
|
||||
case 'S':
|
||||
socketname= optarg;
|
||||
break;
|
||||
case '?':
|
||||
usage();
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
printf("Unknown option %c\n", c);
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int check_variable(MYSQL *mysql, char *variable, 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;
|
||||
char query[255];
|
||||
|
||||
if (!(mysql = mysql_init(NULL))) {
|
||||
diag("%s", "mysql_init failed - exiting");
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
mysql_options(mysql, MYSQL_REPORT_DATA_TRUNCATION, "1");
|
||||
|
||||
/* option handling */
|
||||
if (test && test->options) {
|
||||
int i=0;
|
||||
|
||||
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 (!(mysql_real_connect(mysql, hostname, username, password,
|
||||
NULL, 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);
|
||||
}
|
||||
|
||||
/* change database or create if it doesn't exist */
|
||||
if (mysql_select_db(mysql, schema)) {
|
||||
if(mysql_errno(mysql) == 1049) {
|
||||
sprintf(query, "CREATE DATABASE %s", schema);
|
||||
if (mysql_query(mysql, query)) {
|
||||
diag("Can't create database %s", schema);
|
||||
mysql_close(mysql);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
diag("Error (%d): %s", mysql_errno(mysql), mysql_error(mysql));
|
||||
mysql_close(mysql);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return(mysql);
|
||||
}
|
||||
|
||||
static int reset_connection(MYSQL *mysql) {
|
||||
int rc;
|
||||
|
||||
rc= mysql_change_user(mysql, username, password, schema);
|
||||
check_mysql_rc(rc, mysql);
|
||||
if (mysql_get_server_version(mysql) < 50400)
|
||||
rc= mysql_query(mysql, "SET table_type='MyISAM'");
|
||||
else
|
||||
rc= mysql_query(mysql, "SET storage_engine='MyISAM'");
|
||||
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 (!hostname && (envvar= getenv("MYSQL_TEST_HOST")))
|
||||
hostname= envvar;
|
||||
if (!username && (envvar= getenv("MYSQL_TEST_USER")))
|
||||
username= envvar;
|
||||
if (!password && (envvar= getenv("MYSQL_TEST_PASSWD")))
|
||||
password= envvar;
|
||||
if (!schema && (envvar= getenv("MYSQL_TEST_DB")))
|
||||
schema= envvar;
|
||||
if (!port && (envvar= getenv("MYSQL_TEST_PORT")))
|
||||
port= atoi(envvar);
|
||||
if (!socketname && (envvar= getenv("MYSQL_TEST_SOCKET")))
|
||||
socketname= envvar;
|
||||
}
|
||||
|
||||
void run_tests(struct my_tests_st *test) {
|
||||
int i, rc, total=0;
|
||||
MYSQL *mysql, *mysql_default= NULL; /* default connection */
|
||||
|
||||
|
||||
while (test[total].function)
|
||||
total++;
|
||||
plan(total);
|
||||
|
||||
if ((mysql_default= test_connect(NULL)))
|
||||
diag("Testing against MySQL Server %s", mysql_get_server_info(mysql_default));
|
||||
else
|
||||
{
|
||||
diag("Can't connect to a server. Aborting....");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
for (i=0; i < total; i++) {
|
||||
if (!mysql_default && (test[i].connection & TEST_CONNECTION_DEFAULT))
|
||||
{
|
||||
diag("MySQL server not running");
|
||||
skip(1, 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, test[i].name);
|
||||
else
|
||||
ok(rc == OK, 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, test[i].skipmsg);
|
||||
}
|
||||
}
|
||||
if (mysql_default) {
|
||||
mysql_close(mysql_default);
|
||||
}
|
||||
mysql_server_end();
|
||||
}
|
||||
|
4619
unittest/libmysql/ps.c
Normal file
4619
unittest/libmysql/ps.c
Normal file
File diff suppressed because it is too large
Load Diff
3779
unittest/libmysql/ps_bugs.c
Normal file
3779
unittest/libmysql/ps_bugs.c
Normal file
File diff suppressed because it is too large
Load Diff
1064
unittest/libmysql/result.c
Normal file
1064
unittest/libmysql/result.c
Normal file
File diff suppressed because it is too large
Load Diff
91
unittest/libmysql/sp.c
Normal file
91
unittest/libmysql/sp.c
Normal file
@@ -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, query, strlen(query));
|
||||
check_mysql_rc(rc, mysql);
|
||||
mysql_free_result(mysql_store_result(mysql));
|
||||
|
||||
rc= mysql_real_query(mysql, query, strlen(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, query, strlen(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());
|
||||
}
|
239
unittest/libmysql/ssl.c
Normal file
239
unittest/libmysql/ssl.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/************************************************************************************
|
||||
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"
|
||||
#include <my_pthread.h>
|
||||
|
||||
static int skip_ssl= 1;
|
||||
|
||||
#ifdef THREAD
|
||||
pthread_mutex_t LOCK_test;
|
||||
#endif
|
||||
|
||||
int check_skip_ssl()
|
||||
{
|
||||
#ifndef HAVE_OPENSSL
|
||||
diag("client library built without OpenSSL support -> skip");
|
||||
return 1;
|
||||
#endif
|
||||
if (skip_ssl)
|
||||
{
|
||||
diag("server doesn't support SSL -> skip");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_ssl(MYSQL *mysql)
|
||||
{
|
||||
int rc;
|
||||
MYSQL_RES *res;
|
||||
MYSQL_ROW row;
|
||||
|
||||
rc= mysql_query(mysql, "SELECT @@have_ssl");
|
||||
check_mysql_rc(rc, mysql);
|
||||
|
||||
res= mysql_store_result(mysql);
|
||||
FAIL_IF(!res, mysql_error(mysql));
|
||||
|
||||
if ((row= mysql_fetch_row(res)))
|
||||
{
|
||||
if (!strcmp(row[0], "YES"))
|
||||
skip_ssl= 0;
|
||||
diag("SSL: %s", row[0]);
|
||||
}
|
||||
mysql_free_result(res);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int test_ssl_cipher(MYSQL *unused)
|
||||
{
|
||||
MYSQL *my;
|
||||
char *cipher;
|
||||
|
||||
if (check_skip_ssl())
|
||||
return SKIP;
|
||||
|
||||
my= mysql_init(NULL);
|
||||
FAIL_IF(!my, "mysql_init() failed");
|
||||
|
||||
mysql_ssl_set(my,0, 0, "./ca.pem", 0);
|
||||
|
||||
FAIL_IF(!mysql_real_connect(my, hostname, username, password, schema,
|
||||
port, socketname, 0), mysql_error(my));
|
||||
|
||||
cipher= (char *)mysql_get_ssl_cipher(my);
|
||||
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
|
||||
mysql_close(my);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int test_multi_ssl_connections(MYSQL *unused)
|
||||
{
|
||||
MYSQL *mysql[50], *my;
|
||||
char *cipher;
|
||||
int i, rc;
|
||||
int old_connections= 0, new_connections= 0;
|
||||
MYSQL_RES *res;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (check_skip_ssl())
|
||||
return SKIP;
|
||||
|
||||
my= mysql_init(NULL);
|
||||
FAIL_IF(!my,"mysql_init() failed");
|
||||
FAIL_IF(!mysql_real_connect(my, hostname, username, password, schema,
|
||||
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, "./ca.pem", 0);
|
||||
|
||||
FAIL_IF(!mysql_real_connect(mysql[i], hostname, username, password, schema,
|
||||
port, socketname, 0), mysql_error(mysql[i]));
|
||||
|
||||
cipher= (char *)mysql_get_ssl_cipher(mysql[i]);
|
||||
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
|
||||
}
|
||||
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);
|
||||
|
||||
FAIL_IF(new_connections - old_connections < 50, "new_connections should be at least old_connections + 50");
|
||||
diag("%d SSL connections processed", new_connections - old_connections);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
#ifdef THREAD
|
||||
static void ssl_thread(void)
|
||||
{
|
||||
MYSQL *mysql;
|
||||
|
||||
mysql_thread_init();
|
||||
|
||||
if (!(mysql= mysql_init(NULL)))
|
||||
{
|
||||
mysql_thread_end();
|
||||
pthread_exit(-1);
|
||||
}
|
||||
mysql_ssl_set(mysql, 0, 0, "./ca.pem", 0);
|
||||
|
||||
if(!mysql_real_connect(mysql, hostname, username, password, schema,
|
||||
port, socketname, 0))
|
||||
{
|
||||
diag("Error: %s", mysql_error(mysql));
|
||||
mysql_close(mysql);
|
||||
mysql_thread_end();
|
||||
pthread_exit(-1);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&LOCK_test);
|
||||
mysql_query(mysql, "UPDATE ssltest SET a=a+1");
|
||||
pthread_mutex_unlock(&LOCK_test);
|
||||
mysql_close(mysql);
|
||||
mysql_thread_end();
|
||||
pthread_exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int test_ssl_threads(MYSQL *mysql)
|
||||
{
|
||||
#ifdef THREAD
|
||||
int i, rc;
|
||||
pthread_t thread[50];
|
||||
MYSQL_RES *res;
|
||||
MYSQL_ROW row;
|
||||
|
||||
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);
|
||||
|
||||
for (i=0; i < 50; i++)
|
||||
pthread_create(&thread[i], NULL, (void *)&ssl_thread, NULL);
|
||||
for (i=0; i < 50; i++)
|
||||
pthread_join(thread[i], NULL);
|
||||
|
||||
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);
|
||||
return OK;
|
||||
#else
|
||||
diag("no thread support -> skip");
|
||||
return SKIP;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 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},
|
||||
#ifndef WIN32
|
||||
{"test_ssl_threads", test_ssl_threads, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
#endif
|
||||
{NULL, NULL, 0, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
get_envvars();
|
||||
|
||||
if (argc > 1)
|
||||
get_options(argc, argv);
|
||||
|
||||
|
||||
run_tests(my_tests);
|
||||
|
||||
mysql_server_end();
|
||||
return(exit_status());
|
||||
}
|
700
unittest/libmysql/view.c
Normal file
700
unittest/libmysql/view.c
Normal file
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
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;
|
||||
long is_null = 0L;
|
||||
const char *query=
|
||||
"SELECT COUNT(*) FROM v1 WHERE SERVERNAME=?";
|
||||
|
||||
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, query, strlen(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= (char*)&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, query, strlen(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 parms[8][100];
|
||||
ulong length[8];
|
||||
const char *query=
|
||||
"select relid, report, handle, log_group, username, variant, type, "
|
||||
"version, erfdat, erftime, erfname, aedat, aetime, aename, dependvars, "
|
||||
"inactive from V_LTDX where mandt = ? and relid = ? and report = ? and "
|
||||
"handle = ? and log_group = ? and username in ( ? , ? ) and type = ?";
|
||||
|
||||
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', sizeof(MYSQL_BIND));
|
||||
for (i=0; i < 8; i++) {
|
||||
strcpy(parms[i], "1");
|
||||
my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
|
||||
my_bind[i].buffer = (char *)&parms[i];
|
||||
my_bind[i].buffer_length = 100;
|
||||
my_bind[i].is_null = 0;
|
||||
my_bind[i].length = &length[i];
|
||||
length[i] = 1;
|
||||
}
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
rc= mysql_stmt_prepare(stmt, query, strlen(query));
|
||||
check_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 parms[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', sizeof(MYSQL_BIND));
|
||||
for (i= 0; i < 2; i++) {
|
||||
sprintf((char *)&parms[i], "%d", i);
|
||||
my_bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
|
||||
my_bind[i].buffer = (char *)&parms[i];
|
||||
my_bind[i].buffer_length = 100;
|
||||
my_bind[i].is_null = 0;
|
||||
my_bind[i].length = &length[i];
|
||||
length[i] = 1;
|
||||
}
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
rc= mysql_stmt_prepare(stmt, query, strlen(query));
|
||||
check_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;
|
||||
long my_null = 0L;
|
||||
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, query, strlen(query));
|
||||
check_stmt_rc(rc, insert_stmt);
|
||||
query= "select * from t1";
|
||||
select_stmt= mysql_stmt_init(mysql);
|
||||
rc= mysql_stmt_prepare(select_stmt, query, strlen(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 = (char*)&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, query, strlen(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)) "
|
||||
"CHARSET=latin1 COLLATE latin1_bin");
|
||||
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]= 20;
|
||||
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];
|
||||
}
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
rc= mysql_stmt_prepare(stmt, query, strlen(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, query, strlen(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 OK;
|
||||
}
|
||||
|
||||
/*
|
||||
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, query, strlen(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;
|
||||
|
||||
DBUG_ENTER("test_bug29306");
|
||||
|
||||
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());
|
||||
}
|
22
unittest/mytap/CMakeLists.txt
Normal file
22
unittest/mytap/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/zlib
|
||||
${CMAKE_SOURCE_DIR}/sql
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/regex
|
||||
${CMAKE_SOURCE_DIR}/extra/yassl/include)
|
||||
ADD_LIBRARY(mytap tap.c)
|
1156
unittest/mytap/Doxyfile
Normal file
1156
unittest/mytap/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
33
unittest/mytap/t/basic-t.c
Normal file
33
unittest/mytap/t/basic-t.c
Normal file
@@ -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();
|
||||
}
|
597
unittest/mytap/tap.c
Normal file
597
unittest/mytap/tap.c
Normal file
@@ -0,0 +1,597 @@
|
||||
/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Library for providing TAP support for testing C and C++ was written
|
||||
by Mats Kindahl <mats@mysql.com>.
|
||||
*/
|
||||
|
||||
#include "tap.h"
|
||||
|
||||
#include "my_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
|
||||
my_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[]= {
|
||||
{ SIGQUIT, handle_core_signal },
|
||||
{ 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, pincing
|
||||
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
|
||||
successfull, otherwise at least one test failed.
|
||||
|
||||
To get a TAP complient 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 belive 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.
|
||||
*/
|
304
unittest/mytap/tap.h
Normal file
304
unittest/mytap/tap.h
Normal file
@@ -0,0 +1,304 @@
|
||||
/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "my_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 */
|
Reference in New Issue
Block a user