From 72e7f011b29998d8a3e15eb5b381ef962af5fe5b Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 5 Apr 2019 10:30:58 +0300 Subject: Upgrade to 8.0.15 --- mysql/libmysql/assert.c | 28 - mysql/libmysql/authentication_win/common.cpp | 510 -- mysql/libmysql/authentication_win/common.h | 324 -- mysql/libmysql/authentication_win/handshake.cpp | 288 -- mysql/libmysql/authentication_win/handshake.h | 181 - .../authentication_win/handshake_client.cpp | 393 -- mysql/libmysql/authentication_win/log_client.cpp | 65 - .../libmysql/authentication_win/plugin_client.cpp | 67 - mysql/libmysql/client_settings.h | 73 - mysql/libmysql/errmsg.c | 130 - mysql/libmysql/get_password.c | 205 - mysql/libmysql/libmysql.c | 5077 -------------------- mysql/libmysql/libmysql_exports_win32.def | 120 - mysql/libmysql/mysql_trace.c | 215 - mysql/libmysql/mysql_trace.h | 165 - 15 files changed, 7841 deletions(-) delete mode 100644 mysql/libmysql/assert.c delete mode 100644 mysql/libmysql/authentication_win/common.cpp delete mode 100644 mysql/libmysql/authentication_win/common.h delete mode 100644 mysql/libmysql/authentication_win/handshake.cpp delete mode 100644 mysql/libmysql/authentication_win/handshake.h delete mode 100644 mysql/libmysql/authentication_win/handshake_client.cpp delete mode 100644 mysql/libmysql/authentication_win/log_client.cpp delete mode 100644 mysql/libmysql/authentication_win/plugin_client.cpp delete mode 100644 mysql/libmysql/client_settings.h delete mode 100644 mysql/libmysql/errmsg.c delete mode 100644 mysql/libmysql/get_password.c delete mode 100644 mysql/libmysql/libmysql.c delete mode 100644 mysql/libmysql/libmysql_exports_win32.def delete mode 100644 mysql/libmysql/mysql_trace.c delete mode 100644 mysql/libmysql/mysql_trace.h (limited to 'mysql/libmysql') diff --git a/mysql/libmysql/assert.c b/mysql/libmysql/assert.c deleted file mode 100644 index 25bc49d..0000000 --- a/mysql/libmysql/assert.c +++ /dev/null @@ -1,28 +0,0 @@ -/* file : mysql/libmysql/assert.c -*- C -*- - * copyright : Copyright (c) 2016-2019 Code Synthesis Ltd - * license : GPLv2 with FOSS License Exception; see accompanying COPYING file - */ - -#include - -#ifdef _MSC_VER -# include // C_ASSERT() -#endif - -#include -#include - -/* - * Make sure that some assumptions made at the preprocessing stage are true. - */ - -/* - * _Static_assert() is introduced in C11, is supported by GCC and Clang for C99 - * as well, and is not supported by VC at all. - */ -#ifdef _MSC_VER -# define _Static_assert(C, M) C_ASSERT (C) -#endif - -_Static_assert (sizeof (off_t) == SIZEOF_OFF_T, "unexpected off_t size"); -_Static_assert (sizeof (time_t) == SIZEOF_TIME_T, "unexpected time_t size"); diff --git a/mysql/libmysql/authentication_win/common.cpp b/mysql/libmysql/authentication_win/common.cpp deleted file mode 100644 index 48c400d..0000000 --- a/mysql/libmysql/authentication_win/common.cpp +++ /dev/null @@ -1,510 +0,0 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#include "common.h" -#include // for ConvertSidToStringSid() -#include // for GetUserNameEx() - - -template <> void error_log_print(const char *fmt, ...); -template <> void error_log_print(const char *fmt, ...); -template <> void error_log_print(const char *fmt, ...); - -/** - Option indicating desired level of logging. Values: - - 0 - no logging - 1 - log only error messages - 2 - additionally log warnings - 3 - additionally log info notes - 4 - also log debug messages - - Value of this option should be taken into account in the - implementation of error_log_vprint() function (see - log_client.cc). - - Note: No error or debug messages are logged in production code - (see logging macros in common.h). -*/ -int opt_auth_win_log_level= 2; - - -/** Connection class **************************************************/ - -/** - Create connection out of an active MYSQL_PLUGIN_VIO object. - - @param[in] vio pointer to a @c MYSQL_PLUGIN_VIO object used for - connection - it can not be NULL -*/ - -Connection::Connection(MYSQL_PLUGIN_VIO *vio): m_vio(vio), m_error(0) -{ - DBUG_ASSERT(vio); -} - - -/** - Write data to the connection. - - @param[in] blob data to be written - - @return 0 on success, VIO error code on failure. - - @note In case of error, VIO error code is stored in the connection object - and can be obtained with @c error() method. -*/ - -int Connection::write(const Blob &blob) -{ - m_error= m_vio->write_packet(m_vio, blob.ptr(), static_cast(blob.len())); - -#ifndef DBUG_OFF - if (m_error) - DBUG_PRINT("error", ("vio write error %d", m_error)); -#endif - - return m_error; -} - - -/** - Read data from connection. - - @return A Blob containing read packet or null Blob in case of error. - - @note In case of error, VIO error code is stored in the connection object - and can be obtained with @c error() method. -*/ - -Blob Connection::read() -{ - unsigned char *ptr; - int len= m_vio->read_packet(m_vio, &ptr); - - if (len < 0) - { - m_error= true; - return Blob(); - } - - return Blob(ptr, len); -} - - -/** Sid class *****************************************************/ - - -/** - Create Sid object corresponding to a given account name. - - @param[in] account_name name of a Windows account - - The account name can be in any form accepted by @c LookupAccountName() - function. - - @note In case of errors created object is invalid and its @c is_valid() - method returns @c false. -*/ - -Sid::Sid(const wchar_t *account_name): m_data(NULL) -#ifndef DBUG_OFF -, m_as_string(NULL) -#endif -{ - DWORD sid_size= 0, domain_size= 0; - bool success; - - // Determine required buffer sizes - - success= LookupAccountNameW(NULL, account_name, NULL, &sid_size, - NULL, &domain_size, &m_type); - - if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) - { -#ifndef DBUG_OFF - Error_message_buf error_buf; - DBUG_PRINT("error", ("Could not determine SID buffer size, " - "LookupAccountName() failed with error %X (%s)", - GetLastError(), get_last_error_message(error_buf))); -#endif - return; - } - - // Query for SID (domain is ignored) - - wchar_t *domain= new wchar_t[domain_size]; - m_data= (TOKEN_USER*) new BYTE[sid_size + sizeof(TOKEN_USER)]; - m_data->User.Sid= (BYTE*)m_data + sizeof(TOKEN_USER); - - success= LookupAccountNameW(NULL, account_name, - m_data->User.Sid, &sid_size, - domain, &domain_size, - &m_type); - - if (!success || !is_valid()) - { -#ifndef DBUG_OFF - Error_message_buf error_buf; - DBUG_PRINT("error", ("Could not determine SID of '%S', " - "LookupAccountName() failed with error %X (%s)", - account_name, GetLastError(), - get_last_error_message(error_buf))); -#endif - goto fail; - } - - goto end; - -fail: - if (m_data) - delete [] m_data; - m_data= NULL; - -end: - if (domain) - delete [] domain; -} - - -/** - Create Sid object corresponding to a given security token. - - @param[in] token security token of a Windows account - - @note In case of errors created object is invalid and its @c is_valid() - method returns @c false. -*/ - -Sid::Sid(HANDLE token): m_data(NULL) -#ifndef DBUG_OFF -, m_as_string(NULL) -#endif -{ - DWORD req_size= 0; - bool success; - - // Determine required buffer size - - success= GetTokenInformation(token, TokenUser, NULL, 0, &req_size); - if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) - { -#ifndef DBUG_OFF - Error_message_buf error_buf; - DBUG_PRINT("error", ("Could not determine SID buffer size, " - "GetTokenInformation() failed with error %X (%s)", - GetLastError(), get_last_error_message(error_buf))); -#endif - return; - } - - m_data= (TOKEN_USER*) new BYTE[req_size]; - success= GetTokenInformation(token, TokenUser, m_data, req_size, &req_size); - - if (!success || !is_valid()) - { - delete [] m_data; - m_data= NULL; -#ifndef DBUG_OFF - if (!success) - { - Error_message_buf error_buf; - DBUG_PRINT("error", ("Could not read SID from security token, " - "GetTokenInformation() failed with error %X (%s)", - GetLastError(), get_last_error_message(error_buf))); - } -#endif - } -} - - -Sid::~Sid() -{ - if (m_data) - delete [] m_data; -#ifndef DBUG_OFF - if (m_as_string) - LocalFree(m_as_string); -#endif -} - -/// Check if Sid object is valid. -bool Sid::is_valid(void) const -{ - return m_data && m_data->User.Sid && IsValidSid(m_data->User.Sid); -} - - -#ifndef DBUG_OFF - -/** - Produces string representation of the SID. - - @return String representation of the SID or NULL in case of errors. - - @note Memory allocated for the string is automatically freed in Sid's - destructor. -*/ - -const char* Sid::as_string() -{ - if (!m_data) - return NULL; - - if (!m_as_string) - { - bool success= ConvertSidToStringSid(m_data->User.Sid, &m_as_string); - - if (!success) - { -#ifndef DBUG_OFF - Error_message_buf error_buf; - DBUG_PRINT("error", ("Could not get textual representation of a SID, " - "ConvertSidToStringSid() failed with error %X (%s)", - GetLastError(), get_last_error_message(error_buf))); -#endif - m_as_string= NULL; - return NULL; - } - } - - return m_as_string; -} - -#endif - - -bool Sid::operator ==(const Sid &other) -{ - if (!is_valid() || !other.is_valid()) - return false; - - return EqualSid(m_data->User.Sid, other.m_data->User.Sid); -} - - -/** Generating User Principal Name *************************/ - -/** - Call Windows API functions to get UPN of the current user and store it - in internal buffer. -*/ - -UPN::UPN(): m_buf(NULL) -{ - wchar_t buf1[MAX_SERVICE_NAME_LENGTH]; - - // First we try to use GetUserNameEx. - - m_len= sizeof(buf1)/sizeof(wchar_t); - - if (!GetUserNameExW(NameUserPrincipal, buf1, (PULONG)&m_len)) - { - if (GetLastError()) - { -#ifndef DBUG_OFF - Error_message_buf error_buf; - DBUG_PRINT("note", ("When determining UPN" - ", GetUserNameEx() failed with error %X (%s)", - GetLastError(), get_last_error_message(error_buf))); -#endif - if (ERROR_MORE_DATA == GetLastError()) - ERROR_LOG(INFO, ("Buffer overrun when determining UPN:" - " need %ul characters but have %ul", - m_len, sizeof(buf1)/sizeof(WCHAR))); - } - - m_len= 0; // m_len == 0 indicates invalid UPN - return; - } - - /* - UPN is stored in buf1 in wide-char format - convert it to utf8 - for sending over network. - */ - - m_buf= wchar_to_utf8(buf1, &m_len); - - if(!m_buf) - ERROR_LOG(ERROR, ("Failed to convert UPN to utf8")); - - // Note: possible error would be indicated by the fact that m_buf is NULL. - return; -} - - -UPN::~UPN() -{ - if (m_buf) - free(m_buf); -} - - -/** - Convert a wide-char string to utf8 representation. - - @param[in] string null-terminated wide-char string to be converted - @param[in,out] len length of the string to be converted or 0; on - return length (in bytes, excluding terminating - null character) of the converted string - - If len is 0 then the length of the string will be computed by this function. - - @return Pointer to a buffer containing utf8 representation or NULL in - case of error. - - @note The returned buffer must be freed with @c free() call. -*/ - -char* wchar_to_utf8(const wchar_t *string, size_t *len) -{ - char *buf= NULL; - size_t str_len= len && *len ? *len : wcslen(string); - - /* - A conversion from utf8 to wchar_t will never take more than 3 bytes per - character, so a buffer of length 3 * str_len schould be sufficient. - We check that assumption with an assertion later. - */ - - size_t buf_len= 3 * str_len; - - buf= (char*)malloc(buf_len + 1); - if (!buf) - { - DBUG_PRINT("error",("Out of memory when converting string '%S' to utf8", - string)); - return NULL; - } - - int res= WideCharToMultiByte(CP_UTF8, // convert to UTF-8 - 0, // conversion flags - string, // input buffer - str_len, // its length - buf, buf_len, // output buffer and its size - NULL, NULL); // default character (not used) - - if (res) - { - buf[res]= '\0'; - if (len) - *len= res; - return buf; - } - - // res is 0 which indicates error - -#ifndef DBUG_OFF - Error_message_buf error_buf; - DBUG_PRINT("error", ("Could not convert string '%S' to utf8" - ", WideCharToMultiByte() failed with error %X (%s)", - string, GetLastError(), - get_last_error_message(error_buf))); -#endif - - // Let's check our assumption about sufficient buffer size - DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); - - return NULL; -} - - -/** - Convert an utf8 string to a wide-char string. - - @param[in] string null-terminated utf8 string to be converted - @param[in,out] len length of the string to be converted or 0; on - return length (in chars) of the converted string - - If len is 0 then the length of the string will be computed by this function. - - @return Pointer to a buffer containing wide-char representation or NULL in - case of error. - - @note The returned buffer must be freed with @c free() call. -*/ - -wchar_t* utf8_to_wchar(const char *string, size_t *len) -{ - size_t buf_len; - - /* - Note: length (in bytes) of an utf8 string is always bigger than the - number of characters in this string. Hence a buffer of size len will - be sufficient. We add 1 for the terminating null character. - */ - - buf_len= len && *len ? *len : strlen(string); - wchar_t *buf= (wchar_t*)malloc((buf_len+1)*sizeof(wchar_t)); - - if (!buf) - { - DBUG_PRINT("error",("Out of memory when converting utf8 string '%s'" - " to wide-char representation", string)); - return NULL; - } - - size_t res; - res= MultiByteToWideChar(CP_UTF8, // convert from UTF-8 - 0, // conversion flags - string, // input buffer - buf_len, // its size - buf, buf_len); // output buffer and its size - if (res) - { - buf[res]= '\0'; - if (len) - *len= res; - return buf; - } - - // error in MultiByteToWideChar() - -#ifndef DBUG_OFF - Error_message_buf error_buf; - DBUG_PRINT("error", ("Could not convert UPN from UTF-8" - ", MultiByteToWideChar() failed with error %X (%s)", - GetLastError(), get_last_error_message(error_buf))); -#endif - - // Let's check our assumption about sufficient buffer size - DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); - - return NULL; -} - - -/** Error handling ****************************************************/ - - -/** - Returns error message corresponding to the last Windows error given - by GetLastError(). - - @note Error message is overwritten by next call to - @c get_last_error_message(). -*/ - -const char* get_last_error_message(Error_message_buf buf) -{ - int error= GetLastError(); - - buf[0]= '\0'; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)buf, sizeof(buf), NULL ); - - return buf; -} diff --git a/mysql/libmysql/authentication_win/common.h b/mysql/libmysql/authentication_win/common.h deleted file mode 100644 index b7525fb..0000000 --- a/mysql/libmysql/authentication_win/common.h +++ /dev/null @@ -1,324 +0,0 @@ -/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#ifndef COMMON_H -#define COMMON_H - -#include -#include -#include // for CtxtHandle -#include // for MYSQL_PLUGIN_VIO - -/// Maximum length of the target service name. -#define MAX_SERVICE_NAME_LENGTH 1024 - - -/** Debugging and error reporting infrastructure ***************************/ - -/* - Note: We use plugin local logging and error reporting mechanisms until - WL#2940 (plugin service: error reporting) is available. -*/ - -#undef INFO -#undef WARNING -#undef ERROR - -struct error_log_level -{ - typedef enum {INFO, WARNING, ERROR} type; -}; - -extern "C" int opt_auth_win_log_level; -unsigned int get_log_level(void); -void set_log_level(unsigned int); - - -/* - If DEBUG_ERROR_LOG is defined then error logging happens only - in debug-copiled code. Otherwise ERROR_LOG() expands to - error_log_print() even in production code. - - Note: Macro ERROR_LOG() can use printf-like format string like this: - - ERROR_LOG(Level, ("format string", args)); - - The implementation should handle it correctly. Currently it is passed - to fprintf() (see error_log_vprint() function). -*/ - -#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF) -#define ERROR_LOG(Level, Msg) do {} while (0) -#else -#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg -#endif - - -void error_log_vprint(error_log_level::type level, - const char *fmt, va_list args); - -template -void error_log_print(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - error_log_vprint(Level, fmt, args); - va_end(args); -} - -typedef char Error_message_buf[1024]; -const char* get_last_error_message(Error_message_buf); - - -/* - Internal implementation of debug message printing which does not use - dbug library. This is invoked via macro: - - DBUG_PRINT_DO(Keyword, ("format string", args)); - - This is supposed to be used as an implementation of DBUG_PRINT() macro, - unless the dbug library implementation is used or debug messages are disabled. -*/ - -#ifndef DBUG_OFF - -#define DBUG_PRINT_DO(Keyword, Msg) \ - do { \ - if (4 > get_log_level()) break; \ - fprintf(stderr, "winauth: %s: ", Keyword); \ - debug_msg Msg; \ - } while (0) - -inline -void debug_msg(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - fputc('\n', stderr); - fflush(stderr); - va_end(args); -} - -#else -#define DBUG_PRINT_DO(K, M) do {} while (0) -#endif - - -#ifndef WINAUTH_USE_DBUG_LIB - -#undef DBUG_PRINT -#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg) - -/* - Redefine few more debug macros to make sure that no symbols from - dbug library are used. -*/ - -#undef DBUG_ENTER -#define DBUG_ENTER(X) do {} while (0) - -#undef DBUG_RETURN -#define DBUG_RETURN(X) return (X) - -#undef DBUG_ASSERT -#ifndef DBUG_OFF -#define DBUG_ASSERT(X) assert (X) -#else -#define DBUG_ASSERT(X) do {} while (0) -#endif - -#undef DBUG_DUMP -#define DBUG_DUMP(A,B,C) do {} while (0) - -#endif - - -/** Blob class *************************************************************/ - -typedef unsigned char byte; - -/** - Class representing a region of memory (e.g., a string or binary buffer). - - @note This class does not allocate memory. It merely describes a region - of memory which must be allocated externally (if it is dynamic memory). -*/ - -class Blob -{ - byte *m_ptr; ///< Pointer to the first byte of the memory region. - size_t m_len; ///< Length of the memory region. - -public: - - Blob(): m_ptr(NULL), m_len(0) - {} - - Blob(const byte *ptr, const size_t len) - : m_ptr(const_cast(ptr)), m_len(len) - {} - - Blob(const char *str): m_ptr((byte*)str) - { - m_len= strlen(str); - } - - byte* ptr() const - { - return m_ptr; - } - - size_t len() const - { - return m_len; - } - - byte& operator[](unsigned pos) const - { - static byte out_of_range= 0; // alas, no exceptions... - return pos < len() ? m_ptr[pos] : out_of_range; - } - - bool is_null() const - { - return m_ptr == NULL; - } - - void trim(size_t l) - { - m_len= l; - } -}; - - -/** Connection class *******************************************************/ - -/** - Convenience wrapper around MYSQL_PLUGIN_VIO object providing basic - read/write operations. -*/ - -class Connection -{ - MYSQL_PLUGIN_VIO *m_vio; ///< Pointer to @c MYSQL_PLUGIN_VIO structure. - - /** - If non-zero, indicates that connection is broken. If this has happened - because of failed operation, stores non-zero error code from that failure. - */ - int m_error; - -public: - - Connection(MYSQL_PLUGIN_VIO *vio); - int write(const Blob&); - Blob read(); - - int error() const - { - return m_error; - } -}; - - -/** Sid class **************************************************************/ - -/** - Class for storing and manipulating Windows security identifiers (SIDs). -*/ - -class Sid -{ - TOKEN_USER *m_data; ///< Pointer to structure holding identifier's data. - SID_NAME_USE m_type; ///< Type of identified entity. - -public: - - Sid(const wchar_t*); - Sid(HANDLE sec_token); - ~Sid(); - - bool is_valid(void) const; - - bool is_group(void) const - { - return m_type == SidTypeGroup - || m_type == SidTypeWellKnownGroup - || m_type == SidTypeAlias; - } - - bool is_user(void) const - { - return m_type == SidTypeUser; - } - - bool operator==(const Sid&); - - operator PSID() const - { - return (PSID)m_data->User.Sid; - } - -#ifndef DBUG_OFF - -private: - char *m_as_string; ///< Cached string representation of the SID. -public: - const char* as_string(); - -#endif -}; - - -/** UPN class **************************************************************/ - -/** - An object of this class obtains and stores User Principal Name of the - account under which current process is running. -*/ - -class UPN -{ - char *m_buf; ///< Pointer to UPN in utf8 representation. - size_t m_len; ///< Length of the name. - -public: - - UPN(); - ~UPN(); - - bool is_valid() const - { - return m_len > 0; - } - - const Blob as_blob() const - { - return m_len ? Blob((byte*)m_buf, m_len) : Blob(); - } - - const char* as_string() const - { - return (const char*)m_buf; - } - -}; - - -char* wchar_to_utf8(const wchar_t*, size_t*); -wchar_t* utf8_to_wchar(const char*, size_t*); - -#endif diff --git a/mysql/libmysql/authentication_win/handshake.cpp b/mysql/libmysql/authentication_win/handshake.cpp deleted file mode 100644 index 7801bb0..0000000 --- a/mysql/libmysql/authentication_win/handshake.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#include "handshake.h" - - -/** Handshake class implementation **********************************/ - -/** - Create common part of handshake context. - - @param[in] ssp name of the SSP (Security Service Provider) to - be used for authentication - @param[in] side is this handshake object used for server- or - client-side handshake - - Prepare for handshake using the @c ssp security module. We use - "Negotiate" which picks best available module. Parameter @c side - tells if this is preparing for server or client side authentication - and is used to prepare appropriate credentials. -*/ - -Handshake::Handshake(const char *ssp, side_t side) -: m_atts(0L), m_error(0), m_complete(FALSE), - m_have_credentials(false), m_have_sec_context(false) -#ifndef DBUG_OFF - , m_ssp_info(NULL) -#endif -{ - SECURITY_STATUS ret; - - // Obtain credentials for the authentication handshake. - - ret= AcquireCredentialsHandle(NULL, (SEC_CHAR*)ssp, - side == SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, - NULL, NULL, NULL, NULL, &m_cred, &m_expire); - - if (ret != SEC_E_OK) - { - DBUG_PRINT("error", ("AcqireCredentialsHandle() failed" - " with error %X", ret)); - ERROR_LOG(ERROR, ("Could not obtain local credentials" - " required for authentication")); - m_error= ret; - } - - m_have_credentials= true; -} - - -Handshake::~Handshake() -{ - if (m_have_credentials) - FreeCredentialsHandle(&m_cred); - if (m_have_sec_context) - DeleteSecurityContext(&m_sctx); - m_output.mem_free(); - -#ifndef DBUG_OFF - if (m_ssp_info) - FreeContextBuffer(m_ssp_info); -#endif -} - - -/** - Read and process data packets from the other end of a connection. - - @param[IN] con a connection to read packets from - - Packets are read and processed until authentication handshake is - complete. It is assumed that the peer will send at least one packet. - Packets are processed with @c process_data() method. If new data is - generated during packet processing, this data is sent to the peer and - another round of packet exchange starts. - - @return 0 on success. - - @note In case of error, appropriate error message is logged. -*/ -int Handshake::packet_processing_loop() -{ - m_round= 0; - - do { - ++m_round; - // Read packet send by the peer - - DBUG_PRINT("info", ("Waiting for packet")); - Blob packet= read_packet(); - if (error()) - { - ERROR_LOG(ERROR, ("Error reading packet in round %d", m_round)); - return 1; - } - DBUG_PRINT("info", ("Got packet of length %d", packet.len())); - - /* - Process received data, possibly generating new data to be sent. - */ - - Blob new_data= process_data(packet); - - if (error()) - { - ERROR_LOG(ERROR, ("Error processing packet in round %d", m_round)); - return 1; - } - - /* - If new data has been generated, send it to the peer. Otherwise - handshake must be completed. - */ - - if (!new_data.is_null()) - { - DBUG_PRINT("info", ("Round %d started", m_round)); - - DBUG_PRINT("info", ("Sending packet of length %d", new_data.len())); - int ret= write_packet(new_data); - if (ret) - { - ERROR_LOG(ERROR, ("Error writing packet in round %d", m_round)); - return 1; - } - DBUG_PRINT("info", ("Data sent")); - } - else if (!is_complete()) - { - ERROR_LOG(ERROR, ("No data to send in round %d" - " but handshake is not complete", m_round)); - return 1; - } - - /* - To protect against malicious clients, break handshake exchange if - too many rounds. - */ - - if (m_round > MAX_HANDSHAKE_ROUNDS) - { - ERROR_LOG(ERROR, ("Authentication handshake could not be completed" - " after %d rounds", m_round)); - return 1; - } - - } while(!is_complete()); - - ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round)); - return 0; -} - - -#ifndef DBUG_OFF - -/** - Get name of the security package which was used in authentication. - - This method should be called only after handshake was completed. It is - available only in debug builds. - - @return Name of security package or NULL if it can not be obtained. -*/ - -const char* Handshake::ssp_name() -{ - if (!m_ssp_info && m_complete) - { - SecPkgContext_PackageInfo pinfo; - - int ret= QueryContextAttributes(&m_sctx, SECPKG_ATTR_PACKAGE_INFO, &pinfo); - - if (SEC_E_OK == ret) - { - m_ssp_info= pinfo.PackageInfo; - } - else - DBUG_PRINT("error", - ("Could not obtain SSP info from authentication context" - ", QueryContextAttributes() failed with error %X", ret)); - } - - return m_ssp_info ? m_ssp_info->Name : NULL; -} - -#endif - - -/** - Process result of @c {Initialize,Accept}SecurityContext() function. - - @param[in] ret return code from @c {Initialize,Accept}SecurityContext() - function - - This function analyses return value of Windows - @c {Initialize,Accept}SecurityContext() function. A call to - @c CompleteAuthToken() is done if requested. If authentication is complete, - this fact is marked in the internal state of the Handshake object. - If errors are detected the object is moved to error state. - - @return True if error has been detected. -*/ - -bool Handshake::process_result(int ret) -{ - /* - First check for errors and set the m_complete flag if the result - indicates that handshake is complete. - */ - - switch (ret) - { - case SEC_E_OK: - case SEC_I_COMPLETE_NEEDED: - // Handshake completed - m_complete= true; - break; - - case SEC_I_CONTINUE_NEEDED: - case SEC_I_COMPLETE_AND_CONTINUE: - break; - - default: - m_error= ret; - return true; - } - - m_have_sec_context= true; - - /* - If the result indicates a need for this, complete the authentication - token. - */ - - switch (ret) - { - case SEC_I_COMPLETE_NEEDED: - case SEC_I_COMPLETE_AND_CONTINUE: - ret= CompleteAuthToken(&m_sctx, &m_output); - if (ret != 0) - { - DBUG_PRINT("error", ("CompleteAuthToken() failed with error %X", ret)); - m_error= ret; - return true; - } - default: - break; - } - - return false; -} - - -/** Security_buffer class implementation **********************************/ - - -Security_buffer::Security_buffer(const Blob &blob): m_allocated(false) -{ - init(blob.ptr(), blob.len()); -} - - -Security_buffer::Security_buffer(): m_allocated(true) -{ - init(NULL, 0); -} - - -void Security_buffer::mem_free(void) -{ - if (m_allocated && NULL != ptr()) - { - FreeContextBuffer(ptr()); - init(NULL, 0); - } -} diff --git a/mysql/libmysql/authentication_win/handshake.h b/mysql/libmysql/authentication_win/handshake.h deleted file mode 100644 index 14b1386..0000000 --- a/mysql/libmysql/authentication_win/handshake.h +++ /dev/null @@ -1,181 +0,0 @@ -/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#ifndef HANDSHAKE_H -#define HANDSHAKE_H - -#include "common.h" - -/** - Name of the SSP (Security Support Provider) to be used for authentication. - - We use "Negotiate" which will find the most secure SSP which can be used - and redirect to that SSP. -*/ -#define SSP_NAME "Negotiate" - -/** - Maximal number of rounds in authentication handshake. - - Server will interrupt authentication handshake with error if client's - identity can not be determined within this many rounds. -*/ -#define MAX_HANDSHAKE_ROUNDS 50 - - -/// Convenience wrapper around @c SecBufferDesc. - -class Security_buffer: public SecBufferDesc -{ - SecBuffer m_buf; ///< A @c SecBuffer instance. - - void init(byte *ptr, size_t len) - { - ulVersion= 0; - cBuffers= 1; - pBuffers= &m_buf; - - m_buf.BufferType= SECBUFFER_TOKEN; - m_buf.pvBuffer= ptr; - m_buf.cbBuffer= (ulong)len; - } - - /// If @c false, no deallocation will be done in the destructor. - const bool m_allocated; - - // Copying/assignment is not supported and can lead to memory leaks - // So declaring copy constructor and assignment operator as private - Security_buffer( const Security_buffer& ); - const Security_buffer& operator=( const Security_buffer& ); - - public: - - Security_buffer(const Blob&); - Security_buffer(); - - ~Security_buffer() - { - mem_free(); - } - - byte* ptr() const - { - return (byte*)m_buf.pvBuffer; - } - - size_t len() const - { - return m_buf.cbBuffer; - } - - const Blob as_blob() const - { - return Blob(ptr(), len()); - } - - void mem_free(void); -}; - - -/// Common base for Handshake_{server,client}. - -class Handshake -{ -public: - - typedef enum {CLIENT, SERVER} side_t; - - Handshake(const char *ssp, side_t side); - virtual ~Handshake(); - - int Handshake::packet_processing_loop(); - - bool virtual is_complete() const - { - return m_complete; - } - - int error() const - { - return m_error; - } - -protected: - - /// Security context object created during the handshake. - CtxtHandle m_sctx; - - /// Credentials of the principal performing this handshake. - CredHandle m_cred; - - /// Stores expiry date of the created security context. - TimeStamp m_expire; - - /// Stores attributes of the created security context. - ULONG m_atts; - - /** - Round of the handshake (starting from round 1). One round - consist of reading packet from the other side, processing it and - optionally sending a reply (see @c packet_processing_loop()). - */ - unsigned int m_round; - - /// If non-zero, stores error code of the last failed operation. - int m_error; - - /// @c true when handshake is complete. - bool m_complete; - - /// @c true when the principal credentials has been determined. - bool m_have_credentials; - - /// @c true when the security context has been created. - bool m_have_sec_context; - - /// Buffer for data to be send to the other side. - Security_buffer m_output; - - bool process_result(int); - - /** - This method is used inside @c packet_processing_loop to process - data packets received from the other end. - - @param[IN] data data to be processed - - @return A blob with data to be sent to the other end or null blob if - no more data needs to be exchanged. - */ - virtual Blob process_data(const Blob &data) =0; - - /// Read packet from the other end. - virtual Blob read_packet() =0; - - /// Write packet to the other end. - virtual int write_packet(Blob &data) =0; - -#ifndef DBUG_OFF - -private: - SecPkgInfo *m_ssp_info; -public: - const char* ssp_name(); - -#endif -}; - - -#endif diff --git a/mysql/libmysql/authentication_win/handshake_client.cpp b/mysql/libmysql/authentication_win/handshake_client.cpp deleted file mode 100644 index 4cf6219..0000000 --- a/mysql/libmysql/authentication_win/handshake_client.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#include "handshake.h" - -#include // for MYSQL structure - - -/// Client-side context for authentication handshake - -class Handshake_client: public Handshake -{ - /** - Name of the server's service for which we authenticate. - - The service name is sent by server in the initial packet. If no - service name is used, this member is @c NULL. - */ - SEC_WCHAR *m_service_name; - - /// Buffer for storing service name obtained from server. - SEC_WCHAR m_service_name_buf[MAX_SERVICE_NAME_LENGTH]; - - Connection &m_con; - -public: - - Handshake_client(Connection &con, const char *target, size_t len); - ~Handshake_client(); - - Blob first_packet(); - Blob process_data(const Blob&); - - Blob read_packet(); - int write_packet(Blob &data); -}; - - -/** - Create authentication handshake context for client. - - @param con connection for communication with the peer - @param target name of the target service with which we will authenticate - (can be NULL if not used) - - Some security packages (like Kerberos) require providing explicit name - of the service with which a client wants to authenticate. The server-side - authentication plugin sends this name in the greeting packet - (see @c win_auth_handshake_{server,client}() functions). -*/ - -Handshake_client::Handshake_client(Connection &con, - const char *target, size_t len) -: Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con) -{ - if (!target || 0 == len) - return; - - // Convert received UPN to internal WCHAR representation. - - m_service_name= utf8_to_wchar(target, &len); - - if (m_service_name) - DBUG_PRINT("info", ("Using target service: %S\n", m_service_name)); - else - { - /* - Note: we ignore errors here - m_target will be NULL, the target name - will not be used and system will fall-back to NTLM authentication. But - we leave trace in error log. - */ - ERROR_LOG(WARNING, ("Could not decode UPN sent by the server" - "; target service name will not be used" - " and Kerberos authentication will not work")); - } -} - - -Handshake_client::~Handshake_client() -{ - if (m_service_name) - free(m_service_name); -} - - -Blob Handshake_client::read_packet() -{ - /* - We do a fake read in the first round because first - packet from the server containing UPN must be read - before the handshake context is created and the packet - processing loop starts. We return an empty blob here - and process_data() function will ignore it. - */ - if (m_round == 1) - return Blob(); - - // Otherwise we read packet from the connection. - - Blob packet= m_con.read(); - m_error= m_con.error(); - if (!m_error && packet.is_null()) - m_error= true; // (no specific error code assigned) - - if (m_error) - return Blob(); - - DBUG_PRINT("dump", ("Got the following bytes")); - DBUG_DUMP("dump", packet.ptr(), packet.len()); - return packet; -} - - - -int Handshake_client::write_packet(Blob &data) -{ - /* - Length of the first data payload send by client authentication plugin is - limited to 255 bytes (because it is wrapped inside client authentication - packet and is length-encoded with 1 byte for the length). - - If the data payload is longer than 254 bytes, then it is sent in two parts: - first part of length 255 will be embedded in the authentication packet, - second part will be sent in the following packet. Byte 255 of the first - part contains information about the total length of the payload. It is a - number of blocks of size 512 bytes which is sufficient to store the - combined packets. - - Server's logic for reading first client's payload is as follows - (see Handshake_server::read_packet()): - 1. Read data from the authentication packet, if it is shorter than 255 bytes - then that is all data sent by client. - 2. If there is 255 bytes of data in the authentication packet, read another - packet and append it to the data, skipping byte 255 of the first packet - which can be used to allocate buffer of appropriate size. - */ - - size_t len2= 0; // length of the second part of first data payload - byte saved_byte; // for saving byte 255 in which data length is stored - - if (m_round == 1 && data.len() > 254) - { - len2= data.len() - 254; - DBUG_PRINT("info", ("Splitting first packet of length %lu" - ", %lu bytes will be sent in a second part", - data.len(), len2)); - /* - Store in byte 255 the number of 512b blocks that are needed to - keep all the data. - */ - unsigned block_count= data.len()/512 + ((data.len() % 512) ? 1 : 0); - -#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB) - - /* - For testing purposes, use wrong block count to see how server - handles this. - */ - DBUG_EXECUTE_IF("winauth_first_packet_test",{ - block_count= data.len() == 601 ? 0 : - data.len() == 602 ? 1 : - block_count; - }); - -#endif - - DBUG_ASSERT(block_count < (unsigned)0x100); - saved_byte= data[254]; - data[254] = block_count; - - data.trim(255); - } - - DBUG_PRINT("dump", ("Sending the following data")); - DBUG_DUMP("dump", data.ptr(), data.len()); - int ret= m_con.write(data); - - if (ret) - return ret; - - // Write second part if it is present. - if (len2) - { - data[254]= saved_byte; - Blob data2(data.ptr() + 254, len2); - DBUG_PRINT("info", ("Sending second part of data")); - DBUG_DUMP("info", data2.ptr(), data2.len()); - ret= m_con.write(data2); - } - - return ret; -} - - -/** - Process data sent by server. - - @param[in] data blob with data from server - - This method analyses data sent by server during authentication handshake. - If client should continue packet exchange, this method returns data to - be sent to the server next. If no more data needs to be exchanged, an - empty blob is returned and @c is_complete() is @c true. In case of error - an empty blob is returned and @c error() gives non-zero error code. - - When invoked for the first time (in the first round of the handshake) - there is no data from the server (data blob is null) and the intial - packet is generated without an input. - - @return Data to be sent to the server next or null blob if no more data - needs to be exchanged or in case of error. -*/ - -Blob Handshake_client::process_data(const Blob &data) -{ -#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB) - /* - Code for testing the logic for sending the first client payload. - - A fake data of length given by environment variable TEST_PACKET_LENGTH - (or default 255 bytes) is sent to the server. First 2 bytes of the - payload contain its total length (LSB first). The length of test data - is limited to 2048 bytes. - - Upon receiving test data, server will check that data is correct and - refuse connection. If server detects data errors it will crash on - assertion. - - This code is executed if debug flag "winauth_first_packet_test" is - set, e.g. using client option: - - --debug="d,winauth_first_packet_test" - - The same debug flag must be enabled in the server, e.g. using - statement: - - SET GLOBAL debug= '+d,winauth_first_packet_test'; - */ - - static byte test_buf[2048]; - - if (m_round == 1 - && DBUG_EVALUATE_IF("winauth_first_packet_test", true, false)) - { - const char *env= getenv("TEST_PACKET_LENGTH"); - size_t len= env ? atoi(env) : 0; - if (!len) - len= 255; - if (len > sizeof(test_buf)) - len= sizeof(test_buf); - - // Store data length in first 2 bytes. - byte *ptr= test_buf; - *ptr++= len & 0xFF; - *ptr++= len >> 8; - - // Fill remaining bytes with known values. - for (byte b= 0; ptr < test_buf + len; ++ptr, ++b) - *ptr= b; - - return Blob(test_buf, len); - }; - -#endif - - Security_buffer input(data); - SECURITY_STATUS ret; - - m_output.mem_free(); - - ret= InitializeSecurityContextW( - &m_cred, - m_round == 1 ? NULL : &m_sctx, // partial context - m_service_name, // service name - ASC_REQ_ALLOCATE_MEMORY, // requested attributes - 0, // reserved - SECURITY_NETWORK_DREP, // data representation - m_round == 1 ? NULL : &input, // input data - 0, // reserved - &m_sctx, // context - &m_output, // output data - &m_atts, // attributes - &m_expire); // expire date - - if (process_result(ret)) - { - DBUG_PRINT("error", - ("InitializeSecurityContext() failed with error %X", ret)); - return Blob(); - } - - return m_output.as_blob(); -} - - -/**********************************************************************/ - - -/** - Perform authentication handshake from client side. - - @param[in] vio pointer to @c MYSQL_PLUGIN_VIO instance to be used - for communication with the server - @param[in] mysql pointer to a MySQL connection for which we authenticate - - After reading the initial packet from server, containing its UPN to be - used as service name, client starts packet exchange by sending the first - packet in this exchange. While handshake is not yet completed, client - reads packets sent by the server and process them, possibly generating new - data to be sent to the server. - - This function reports errors. - - @return 0 on success. -*/ - -int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) -{ - DBUG_ENTER("win_auth_handshake_client"); - - /* - Check if we should enable logging. - */ - { - const char *opt= getenv("AUTHENTICATION_WIN_LOG"); - int opt_val= opt ? atoi(opt) : 0; - if (opt && !opt_val) - { - if (!_strnicmp("on", opt, 2)) opt_val= 2; - if (!_strnicmp("yes", opt, 3)) opt_val= 2; - if (!_strnicmp("true", opt, 4)) opt_val= 2; - if (!_strnicmp("debug", opt, 5)) opt_val= 4; - if (!_strnicmp("dbug", opt, 4)) opt_val= 4; - } - set_log_level(opt_val); - } - - ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user)); - - // Create connection object. - - Connection con(vio); - DBUG_ASSERT(!con.error()); - - // Read initial packet from server containing service name. - - Blob service_name= con.read(); - - if (con.error() || service_name.is_null()) - { - ERROR_LOG(ERROR, ("Error reading initial packet")); - DBUG_RETURN(CR_ERROR); - } - DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len())); - - // Create authentication handshake context using the given service name. - - Handshake_client hndshk(con, - service_name[0] ? (char *)service_name.ptr() : NULL, - service_name.len()); - if (hndshk.error()) - { - ERROR_LOG(ERROR, ("Could not create authentication handshake context")); - DBUG_RETURN(CR_ERROR); - } - - DBUG_ASSERT(!hndshk.error()); - - /* - Read and process packets from server until handshake is complete. - Note that the first read from server is dummy - (see Handshake_client::read_packet()) as we already have read the - first packet to establish service name. - */ - if (hndshk.packet_processing_loop()) - DBUG_RETURN(CR_ERROR); - - DBUG_ASSERT(!hndshk.error() && hndshk.is_complete()); - - DBUG_RETURN(CR_OK); -} diff --git a/mysql/libmysql/authentication_win/log_client.cpp b/mysql/libmysql/authentication_win/log_client.cpp deleted file mode 100644 index 6973c95..0000000 --- a/mysql/libmysql/authentication_win/log_client.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#include -#include "common.h" - - -// Client-side logging function - -void error_log_vprint(error_log_level::type level, - const char *fmt, va_list args) -{ - const char *level_string= ""; - int log_level= get_log_level(); - - switch (level) - { - case error_log_level::INFO: - if (3 > log_level) - return; - level_string= "Note"; - break; - case error_log_level::WARNING: - if (2 > log_level) - return; - level_string= "Warning"; - break; - case error_log_level::ERROR: - if (1 > log_level) - return; - level_string= "ERROR"; - break; - } - - fprintf(stderr, "Windows Authentication Plugin %s: ", level_string); - vfprintf(stderr, fmt, args); - fputc('\n', stderr); - fflush(stderr); -} - - -// Trivial implementation of log-level setting storage. - -void set_log_level(unsigned int level) -{ - opt_auth_win_log_level= level; -} - - -unsigned int get_log_level(void) -{ - return opt_auth_win_log_level; -} diff --git a/mysql/libmysql/authentication_win/plugin_client.cpp b/mysql/libmysql/authentication_win/plugin_client.cpp deleted file mode 100644 index d25aae8..0000000 --- a/mysql/libmysql/authentication_win/plugin_client.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -#include -#include -#include - -#include "common.h" - -/* - The following MS C++ specific pragma embeds a comment in the resulting - object file. A "lib" comment tells the linker to use the specified - library, thus the dependency is handled automagically. -*/ - -#ifdef _MSC_VER -#pragma comment(lib, "Secur32") -#endif - -static int win_auth_client_plugin_init(char*, size_t, int, va_list) -{ - return 0; -} - - -static int win_auth_client_plugin_deinit() -{ - return 0; -} - - -int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); - - -/* - Client plugin declaration. This is added to mysql_client_builtins[] - in sql-common/client.c -*/ - -extern "C" -st_mysql_client_plugin_AUTHENTICATION win_auth_client_plugin= -{ - MYSQL_CLIENT_AUTHENTICATION_PLUGIN, - MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, - "authentication_windows_client", - "Rafal Somla", - "Windows Authentication Plugin - client side", - {0,1,0}, - "GPL", - NULL, - win_auth_client_plugin_init, - win_auth_client_plugin_deinit, - NULL, // option handling - win_auth_handshake_client -}; diff --git a/mysql/libmysql/client_settings.h b/mysql/libmysql/client_settings.h deleted file mode 100644 index 3d44a81..0000000 --- a/mysql/libmysql/client_settings.h +++ /dev/null @@ -1,73 +0,0 @@ -/* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#ifndef CLIENT_SETTINGS_INCLUDED -#define CLIENT_SETTINGS_INCLUDED -#else -#error You have already included an client_settings.h and it should not be included twice -#endif /* CLIENT_SETTINGS_INCLUDED */ - -extern uint mysql_port; -extern char * mysql_unix_port; - -/* - Note: CLIENT_CAPABILITIES is also defined in sql/client_settings.h. - When adding capabilities here, consider if they should be also added to - the server's version. -*/ -#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \ - CLIENT_LONG_FLAG | \ - CLIENT_TRANSACTIONS | \ - CLIENT_PROTOCOL_41 | \ - CLIENT_RESERVED2 | \ - CLIENT_MULTI_RESULTS | \ - CLIENT_PS_MULTI_RESULTS | \ - CLIENT_PLUGIN_AUTH | \ - CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \ - CLIENT_CONNECT_ATTRS | \ - CLIENT_SESSION_TRACK | \ - CLIENT_DEPRECATE_EOF) - -void read_user_name(char *name); -my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); - -void mysql_read_default_options(struct st_mysql_options *options, - const char *filename,const char *group); -void mysql_detach_stmt_list(LIST **stmt_list, const char *func_name); -MYSQL * STDCALL -cli_mysql_real_connect(MYSQL *mysql,const char *host, const char *user, - const char *passwd, const char *db, - uint port, const char *unix_socket,ulong client_flag); - -void cli_mysql_close(MYSQL *mysql); - -MYSQL_FIELD * cli_list_fields(MYSQL *mysql); -my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt); -MYSQL_DATA * cli_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, - uint fields); -int cli_stmt_execute(MYSQL_STMT *stmt); -int cli_read_binary_rows(MYSQL_STMT *stmt); -int cli_unbuffered_fetch(MYSQL *mysql, char **row); -const char * cli_read_statistics(MYSQL *mysql); -int cli_read_change_user_result(MYSQL *mysql); - -#ifdef EMBEDDED_LIBRARY -int init_embedded_server(int argc, char **argv, char **groups); -void end_embedded_server(); -#endif /*EMBEDDED_LIBRARY*/ - -C_MODE_START -extern int mysql_init_character_set(MYSQL *mysql); -C_MODE_END diff --git a/mysql/libmysql/errmsg.c b/mysql/libmysql/errmsg.c deleted file mode 100644 index 5534b6d..0000000 --- a/mysql/libmysql/errmsg.c +++ /dev/null @@ -1,130 +0,0 @@ -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation. - - There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. - - 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 */ - -/* Error messages for MySQL clients */ -/* (Error messages for the daemon are in share/language/errmsg.sys) */ - -#include -#include -#include "errmsg.h" - -const char *client_errors[]= -{ - "Unknown MySQL error", - "Can't create UNIX socket (%d)", - "Can't connect to local MySQL server through socket '%-.100s' (%d)", - "Can't connect to MySQL server on '%-.100s' (%d)", - "Can't create TCP/IP socket (%d)", - "Unknown MySQL server host '%-.100s' (%d)", - "MySQL server has gone away", - "Protocol mismatch; server version = %d, client version = %d", - "MySQL client ran out of memory", - "Wrong host info", - "Localhost via UNIX socket", - "%-.100s via TCP/IP", - "Error in server handshake", - "Lost connection to MySQL server during query", - "Commands out of sync; you can't run this command now", - "Named pipe: %-.32s", - "Can't wait for named pipe to host: %-.64s pipe: %-.32s (%lu)", - "Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)", - "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)", - "Can't initialize character set %-.32s (path: %-.100s)", - "Got packet bigger than 'max_allowed_packet' bytes", - "Embedded server", - "Error on SHOW SLAVE STATUS:", - "Error on SHOW SLAVE HOSTS:", - "Error connecting to slave:", - "Error connecting to master:", - "SSL connection error: %-.100s", - "Malformed packet", - "This client library is licensed only for use with MySQL servers having '%s' license", - "Invalid use of null pointer", - "Statement not prepared", - "No data supplied for parameters in prepared statement", - "Data truncated", - "No parameters exist in the statement", - "Invalid parameter number", - "Can't send long data for non-string/non-binary data types (parameter: %d)", - "Using unsupported buffer type: %d (parameter: %d)", - "Shared memory: %-.100s", - "Can't open shared memory; client could not create request event (%lu)", - "Can't open shared memory; no answer event received from server (%lu)", - "Can't open shared memory; server could not allocate file mapping (%lu)", - "Can't open shared memory; server could not get pointer to file mapping (%lu)", - "Can't open shared memory; client could not allocate file mapping (%lu)", - "Can't open shared memory; client could not get pointer to file mapping (%lu)", - "Can't open shared memory; client could not create %s event (%lu)", - "Can't open shared memory; no answer from server (%lu)", - "Can't open shared memory; cannot send request event to server (%lu)", - "Wrong or unknown protocol", - "Invalid connection handle", - "Connection using old (pre-4.1.1) authentication protocol refused (client option 'secure_auth' enabled)", - "Row retrieval was canceled by mysql_stmt_close() call", - "Attempt to read column without prior row fetch", - "Prepared statement contains no metadata", - "Attempt to read a row while there is no result set associated with the statement", - "This feature is not implemented yet", - "Lost connection to MySQL server at '%s', system error: %d", - "Statement closed indirectly because of a preceding %s() call", - "The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again", - "This handle is already connected. Use a separate handle for each connection.", - "Authentication plugin '%s' cannot be loaded: %s", - "There is an attribute with the same name already", - "Authentication plugin '%s' reported error: %s", - "Insecure API function call: '%s' Use instead: '%s'", - "" -}; - -const char* get_client_errmsg(int nr) -{ - return client_errors[nr - CR_ERROR_FIRST]; -} - -/* - Register client error messages for use with my_error(). - - SYNOPSIS - init_client_errs() - - RETURN - void -*/ - -void init_client_errs(void) -{ - compile_time_assert(array_elements(client_errors) == - (CR_ERROR_LAST - CR_ERROR_FIRST + 2)); - (void) my_error_register(get_client_errmsg, CR_ERROR_FIRST, CR_ERROR_LAST); -} - - -/* - Unregister client error messages. - - SYNOPSIS - finish_client_errs() - - RETURN - void -*/ - -void finish_client_errs(void) -{ - (void) my_error_unregister(CR_ERROR_FIRST, CR_ERROR_LAST); -} diff --git a/mysql/libmysql/get_password.c b/mysql/libmysql/get_password.c deleted file mode 100644 index d904fbe..0000000 --- a/mysql/libmysql/get_password.c +++ /dev/null @@ -1,205 +0,0 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation. - - There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. - - 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 */ - -/* -** Ask for a password from tty -** This is an own file to avoid conflicts with curses -*/ -#include -#include -#include "mysql.h" -#include -#include - -#ifdef HAVE_GETPASS -#ifdef HAVE_PWD_H -#include -#endif /* HAVE_PWD_H */ -#else /* ! HAVE_GETPASS */ -#if !defined(_WIN32) -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_TERMIOS_H /* For tty-password */ -#include -#define TERMIO struct termios -#else -#ifdef HAVE_TERMIO_H /* For tty-password */ -#include -#define TERMIO struct termio -#else -#include -#define TERMIO struct sgttyb -#endif -#endif -#else -#include -#endif /* _WIN32 */ -#endif /* HAVE_GETPASS */ - -#ifdef HAVE_GETPASSPHRASE /* For Solaris */ -#define getpass(A) getpassphrase(A) -#endif - -#if defined(_WIN32) -/* were just going to fake it here and get input from the keyboard */ -char *get_tty_password(const char *opt_message) -{ - char to[80]; - char *pos=to,*end=to+sizeof(to)-1; - int i=0; - DBUG_ENTER("get_tty_password"); - _cputs(opt_message ? opt_message : "Enter password: "); - for (;;) - { - char tmp; - tmp=_getch(); - if (tmp == '\b' || (int) tmp == 127) - { - if (pos != to) - { - _cputs("\b \b"); - pos--; - continue; - } - } - if (tmp == '\n' || tmp == '\r' || tmp == 3) - break; - if (iscntrl(tmp) || pos == end) - continue; - _cputs("*"); - *(pos++) = tmp; - } - while (pos != to && isspace(pos[-1]) == ' ') - pos--; /* Allow dummy space at end */ - *pos=0; - _cputs("\n"); - DBUG_RETURN(my_strdup(PSI_NOT_INSTRUMENTED, - to,MYF(MY_FAE))); -} - -#else - -#ifndef HAVE_GETPASS -/* - Can't use fgets, because readline will get confused - length is max number of chars in to, not counting \0 - to will not include the eol characters. -*/ - -static void get_password(char *to,uint length,int fd, my_bool echo) -{ - char *pos=to,*end=to+length; - - for (;;) - { - char tmp; - if (my_read(fd,&tmp,1,MYF(0)) != 1) - break; - if (tmp == '\b' || (int) tmp == 127) - { - if (pos != to) - { - if (echo) - { - fputs("\b \b",stdout); - fflush(stdout); - } - pos--; - continue; - } - } - if (tmp == '\n' || tmp == '\r' || tmp == 3) - break; - if (iscntrl(tmp) || pos == end) - continue; - if (echo) - { - fputc('*',stdout); - fflush(stdout); - } - *(pos++) = tmp; - } - while (pos != to && isspace(pos[-1]) == ' ') - pos--; /* Allow dummy space at end */ - *pos=0; - return; -} -#endif /* ! HAVE_GETPASS */ - - -char *get_tty_password(const char *opt_message) -{ -#ifdef HAVE_GETPASS - char *passbuff; -#else /* ! HAVE_GETPASS */ - TERMIO org,tmp; -#endif /* HAVE_GETPASS */ - char buff[80]; - - DBUG_ENTER("get_tty_password"); - -#ifdef HAVE_GETPASS - passbuff = getpass(opt_message ? opt_message : "Enter password: "); - - /* copy the password to buff and clear original (static) buffer */ - my_stpnmov(buff, passbuff, sizeof(buff) - 1); -#ifdef _PASSWORD_LEN - memset(passbuff, 0, _PASSWORD_LEN); -#endif -#else - if (isatty(fileno(stdout))) - { - fputs(opt_message ? opt_message : "Enter password: ",stdout); - fflush(stdout); - } -#if defined(HAVE_TERMIOS_H) - tcgetattr(fileno(stdin), &org); - tmp = org; - tmp.c_lflag &= ~(ECHO | ISIG | ICANON); - tmp.c_cc[VMIN] = 1; - tmp.c_cc[VTIME] = 0; - tcsetattr(fileno(stdin), TCSADRAIN, &tmp); - get_password(buff, sizeof(buff)-1, fileno(stdin), isatty(fileno(stdout))); - tcsetattr(fileno(stdin), TCSADRAIN, &org); -#elif defined(HAVE_TERMIO_H) - ioctl(fileno(stdin), (int) TCGETA, &org); - tmp=org; - tmp.c_lflag &= ~(ECHO | ISIG | ICANON); - tmp.c_cc[VMIN] = 1; - tmp.c_cc[VTIME]= 0; - ioctl(fileno(stdin),(int) TCSETA, &tmp); - get_password(buff,sizeof(buff)-1,fileno(stdin),isatty(fileno(stdout))); - ioctl(fileno(stdin),(int) TCSETA, &org); -#else - gtty(fileno(stdin), &org); - tmp=org; - tmp.sg_flags &= ~ECHO; - tmp.sg_flags |= RAW; - stty(fileno(stdin), &tmp); - get_password(buff,sizeof(buff)-1,fileno(stdin),isatty(fileno(stdout))); - stty(fileno(stdin), &org); -#endif - if (isatty(fileno(stdout))) - fputc('\n',stdout); -#endif /* HAVE_GETPASS */ - - DBUG_RETURN(my_strdup(PSI_NOT_INSTRUMENTED, - buff,MYF(MY_FAE))); -} -#endif /* _WIN32 */ diff --git a/mysql/libmysql/libmysql.c b/mysql/libmysql/libmysql.c deleted file mode 100644 index 51aab62..0000000 --- a/mysql/libmysql/libmysql.c +++ /dev/null @@ -1,5077 +0,0 @@ -/* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include -#include -#include -#include -#include -#include -#include "mysql.h" -#include "mysql_version.h" -#include "mysqld_error.h" -#include "errmsg.h" -#include -#include -#include -#include -#include "my_thread_local.h" -#ifdef HAVE_PWD_H -#include -#endif -#ifdef HAVE_SYS_SELECT_H -#include -#endif -#ifdef HAVE_POLL -#include -#endif -#ifdef HAVE_SYS_UN_H -#include -#endif -#if !defined(_WIN32) -#include /* because of signal() */ -#endif -#ifndef INADDR_NONE -#define INADDR_NONE -1 -#endif - -#include -#include "client_settings.h" -#include "mysql_trace.h" - - - -#ifdef EMBEDDED_LIBRARY -#undef net_flush -my_bool net_flush(NET *net); -#endif - -#if defined(_WIN32) -/* socket_errno is defined in my_global.h for all platforms */ -#define perror(A) -#else -#include -#define SOCKET_ERROR -1 -#endif /* _WIN32 */ - -/* - If allowed through some configuration, then this needs to - be changed -*/ -#define MAX_LONG_DATA_LENGTH 8192 -#define unsigned_field(A) ((A)->flags & UNSIGNED_FLAG) - -static void append_wild(char *to,char *end,const char *wild); - -static my_bool mysql_client_init= 0; -static my_bool org_my_init_done= 0; - -typedef struct st_mysql_stmt_extension -{ - MEM_ROOT fields_mem_root; -} MYSQL_STMT_EXT; - - -/* - Initialize the MySQL client library - - SYNOPSIS - mysql_server_init() - - NOTES - Should be called before doing any other calls to the MySQL - client library to initialize thread specific variables etc. - It's called by mysql_init() to ensure that things will work for - old not threaded applications that doesn't call mysql_server_init() - directly. - - RETURN - 0 ok - 1 could not initialize environment (out of memory or thread keys) -*/ - -int STDCALL mysql_server_init(int argc MY_ATTRIBUTE((unused)), - char **argv MY_ATTRIBUTE((unused)), - char **groups MY_ATTRIBUTE((unused))) -{ - int result= 0; - if (!mysql_client_init) - { - mysql_client_init=1; - org_my_init_done=my_init_done; - if (my_init()) /* Will init threads */ - return 1; - init_client_errs(); - if (mysql_client_plugin_init()) - return 1; -#if defined (HAVE_OPENSSL) && !defined(HAVE_YASSL) - ssl_start(); -#endif - - if (!mysql_port) - { - char *env; - struct servent *serv_ptr MY_ATTRIBUTE((unused)); - - mysql_port = MYSQL_PORT; - - /* - if builder specifically requested a default port, use that - (even if it coincides with our factory default). - only if they didn't do we check /etc/services (and, failing - on that, fall back to the factory default of 3306). - either default can be overridden by the environment variable - MYSQL_TCP_PORT, which in turn can be overridden with command - line options. - */ - -#if MYSQL_PORT_DEFAULT == 0 - if ((serv_ptr= getservbyname("mysql", "tcp"))) - mysql_port= (uint) ntohs((ushort) serv_ptr->s_port); -#endif - if ((env= getenv("MYSQL_TCP_PORT"))) - mysql_port=(uint) atoi(env); - } - - if (!mysql_unix_port) - { - char *env; -#ifdef _WIN32 - mysql_unix_port = (char*) MYSQL_NAMEDPIPE; -#else - mysql_unix_port = (char*) MYSQL_UNIX_ADDR; -#endif - if ((env = getenv("MYSQL_UNIX_PORT"))) - mysql_unix_port = env; - } - mysql_debug(NullS); -#if defined(SIGPIPE) && !defined(_WIN32) - (void) signal(SIGPIPE, SIG_IGN); -#endif -#ifdef EMBEDDED_LIBRARY - if (argc > -1) - result= init_embedded_server(argc, argv, groups); -#endif - } - else - result= (int)my_thread_init(); /* Init if new thread */ - return result; -} - - -/* - Free all memory and resources used by the client library - - NOTES - When calling this there should not be any other threads using - the library. - - To make things simpler when used with windows dll's (which calls this - function automaticly), it's safe to call this function multiple times. -*/ - - -void STDCALL mysql_server_end() -{ - if (!mysql_client_init) - return; - - mysql_client_plugin_deinit(); - -#ifdef EMBEDDED_LIBRARY - end_embedded_server(); -#endif - finish_client_errs(); - vio_end(); - - /* If library called my_init(), free memory allocated by it */ - if (!org_my_init_done) - { - my_end(0); - } - else - { - free_charsets(); - mysql_thread_end(); - } - - mysql_client_init= org_my_init_done= 0; -} - -my_bool STDCALL mysql_thread_init() -{ - return my_thread_init(); -} - -void STDCALL mysql_thread_end() -{ - my_thread_end(); -} - - -/* - Expand wildcard to a sql string -*/ - -static void -append_wild(char *to, char *end, const char *wild) -{ - end-=5; /* Some extra */ - if (wild && wild[0]) - { - to=my_stpcpy(to," like '"); - while (*wild && to < end) - { - if (*wild == '\\' || *wild == '\'') - *to++='\\'; - *to++= *wild++; - } - if (*wild) /* Too small buffer */ - *to++='%'; /* Nicer this way */ - to[0]='\''; - to[1]=0; - } -} - - -/************************************************************************** - Init debugging if MYSQL_DEBUG environment variable is found -**************************************************************************/ - -void STDCALL -mysql_debug(const char *debug MY_ATTRIBUTE((unused))) -{ -#ifndef DBUG_OFF - char *env; - if (debug) - { - DBUG_PUSH(debug); - } - else if ((env = getenv("MYSQL_DEBUG"))) - { - DBUG_PUSH(env); -#if !defined(_WINVER) && !defined(WINVER) - puts("\n-------------------------------------------------------"); - puts("MYSQL_DEBUG found. libmysql started with the following:"); - puts(env); - puts("-------------------------------------------------------\n"); -#else - { - char buff[80]; - buff[sizeof(buff)-1]= 0; - strxnmov(buff,sizeof(buff)-1,"libmysql: ", env, NullS); - MessageBox((HWND) 0,"Debugging variable MYSQL_DEBUG used",buff,MB_OK); - } -#endif - } -#endif -} - - -/************************************************************************** - Change user and database -**************************************************************************/ - -my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, - const char *passwd, const char *db) -{ - int rc; - CHARSET_INFO *saved_cs= mysql->charset; - char *saved_user= mysql->user; - char *saved_passwd= mysql->passwd; - char *saved_db= mysql->db; - - DBUG_ENTER("mysql_change_user"); - - /* Get the connection-default character set. */ - - if (mysql_init_character_set(mysql)) - { - mysql->charset= saved_cs; - DBUG_RETURN(TRUE); - } - - /* - Use an empty string instead of NULL. - Alloc user and password on heap because mysql_reconnect() - calls mysql_close() on success. - */ - mysql->user= my_strdup(PSI_NOT_INSTRUMENTED, user ? user : "", MYF(MY_WME)); - mysql->passwd= my_strdup(PSI_NOT_INSTRUMENTED, passwd ? passwd : "", - MYF(MY_WME)); - mysql->db= 0; - - rc= run_plugin_auth(mysql, 0, 0, 0, db); - - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); - - /* - The server will close all statements no matter was the attempt - to change user successful or not. - */ - mysql_detach_stmt_list(&mysql->stmts, "mysql_change_user"); - if (rc == 0) - { - /* Free old connect information */ - my_free(saved_user); - my_free(saved_passwd); - my_free(saved_db); - - /* alloc new connect information */ - mysql->db= db ? my_strdup(PSI_NOT_INSTRUMENTED, - db, MYF(MY_WME)) : 0; - } - else - { - /* Free temporary connect information */ - my_free(mysql->user); - my_free(mysql->passwd); - my_free(mysql->db); - - /* Restore saved state */ - mysql->charset= saved_cs; - mysql->user= saved_user; - mysql->passwd= saved_passwd; - mysql->db= saved_db; - } - - DBUG_RETURN(rc); -} - -#if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL) -struct passwd *getpwuid(uid_t); -char* getlogin(void); -#endif - -#if !defined(_WIN32) - -void read_user_name(char *name) -{ - DBUG_ENTER("read_user_name"); - if (geteuid() == 0) - (void) my_stpcpy(name,"root"); /* allow use of surun */ - else - { -#ifdef HAVE_GETPWUID - struct passwd *skr; - const char *str; - if ((str=getlogin()) == NULL) - { - if ((skr=getpwuid(geteuid())) != NULL) - str=skr->pw_name; - else if (!(str=getenv("USER")) && !(str=getenv("LOGNAME")) && - !(str=getenv("LOGIN"))) - str="UNKNOWN_USER"; - } - (void) strmake(name,str,USERNAME_LENGTH); -#elif HAVE_CUSERID - (void) cuserid(name); -#else - my_stpcpy(name,"UNKNOWN_USER"); -#endif - } - DBUG_VOID_RETURN; -} - -#else /* If Windows */ - -void read_user_name(char *name) -{ - char *str=getenv("USER"); /* ODBC will send user variable */ - strmake(name,str ? str : "ODBC", USERNAME_LENGTH); -} - -#endif - -my_bool handle_local_infile(MYSQL *mysql, const char *net_filename) -{ - my_bool result= 1; - uint packet_length=MY_ALIGN(mysql->net.max_packet-16,IO_SIZE); - NET *net= &mysql->net; - int readcount; - void *li_ptr; /* pass state to local_infile functions */ - char *buf; /* buffer to be filled by local_infile_read */ - struct st_mysql_options *options= &mysql->options; - DBUG_ENTER("handle_local_infile"); - - /* check that we've got valid callback functions */ - if (!(options->local_infile_init && - options->local_infile_read && - options->local_infile_end && - options->local_infile_error)) - { - /* if any of the functions is invalid, set the default */ - mysql_set_local_infile_default(mysql); - } - - /* copy filename into local memory and allocate read buffer */ - if (!(buf=my_malloc(PSI_NOT_INSTRUMENTED, - packet_length, MYF(0)))) - { - set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); - DBUG_RETURN(1); - } - - /* initialize local infile (open file, usually) */ - if ((*options->local_infile_init)(&li_ptr, net_filename, - options->local_infile_userdata)) - { - MYSQL_TRACE(SEND_FILE, mysql, (0, NULL)); - (void) my_net_write(net,(const uchar*) "",0); /* Server needs one packet */ - net_flush(net); - MYSQL_TRACE(PACKET_SENT, mysql, (0)); - my_stpcpy(net->sqlstate, unknown_sqlstate); - net->last_errno= - (*options->local_infile_error)(li_ptr, - net->last_error, - sizeof(net->last_error)-1); - MYSQL_TRACE(ERROR, mysql, ()); - goto err; - } - - /* read blocks of data from local infile callback */ - while ((readcount = - (*options->local_infile_read)(li_ptr, buf, - packet_length)) > 0) - { - MYSQL_TRACE(SEND_FILE, mysql, ((size_t)readcount, (const unsigned char*)buf)); - if (my_net_write(net, (uchar*) buf, readcount)) - { - DBUG_PRINT("error", - ("Lost connection to MySQL server during LOAD DATA of local file")); - set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); - goto err; - } - MYSQL_TRACE(PACKET_SENT, mysql, (readcount)); - } - - /* Send empty packet to mark end of file */ - MYSQL_TRACE(SEND_FILE, mysql, (0, NULL)); - if (my_net_write(net, (const uchar*) "", 0) || net_flush(net)) - { - set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); - goto err; - } - MYSQL_TRACE(PACKET_SENT, mysql, (0)); - - if (readcount < 0) - { - net->last_errno= - (*options->local_infile_error)(li_ptr, - net->last_error, - sizeof(net->last_error)-1); - MYSQL_TRACE(ERROR, mysql, ()); - goto err; - } - - result=0; /* Ok */ - -err: - /* free up memory allocated with _init, usually */ - (*options->local_infile_end)(li_ptr); - my_free(buf); - DBUG_RETURN(result); -} - - -/**************************************************************************** - Default handlers for LOAD LOCAL INFILE -****************************************************************************/ - -typedef struct st_default_local_infile -{ - int fd; - int error_num; - const char *filename; - char error_msg[LOCAL_INFILE_ERROR_LEN]; -} default_local_infile_data; - - -/* - Open file for LOAD LOCAL INFILE - - SYNOPSIS - default_local_infile_init() - ptr Store pointer to internal data here - filename File name to open. This may be in unix format ! - - - NOTES - Even if this function returns an error, the load data interface - guarantees that default_local_infile_end() is called. - - RETURN - 0 ok - 1 error -*/ - -static int default_local_infile_init(void **ptr, const char *filename, - void *userdata MY_ATTRIBUTE ((unused))) -{ - default_local_infile_data *data; - char tmp_name[FN_REFLEN]; - - if (!(*ptr= data= ((default_local_infile_data *) - my_malloc(PSI_NOT_INSTRUMENTED, - sizeof(default_local_infile_data), MYF(0))))) - return 1; /* out of memory */ - - data->error_msg[0]= 0; - data->error_num= 0; - data->filename= filename; - - fn_format(tmp_name, filename, "", "", MY_UNPACK_FILENAME); - if ((data->fd = my_open(tmp_name, O_RDONLY, MYF(0))) < 0) - { - char errbuf[MYSYS_STRERROR_SIZE]; - data->error_num= my_errno(); - my_snprintf(data->error_msg, sizeof(data->error_msg)-1, - EE(EE_FILENOTFOUND), tmp_name, data->error_num, - my_strerror(errbuf, sizeof(errbuf), data->error_num)); - return 1; - } - return 0; /* ok */ -} - - -/* - Read data for LOAD LOCAL INFILE - - SYNOPSIS - default_local_infile_read() - ptr Points to handle allocated by _init - buf Read data here - buf_len Ammount of data to read - - RETURN - > 0 number of bytes read - == 0 End of data - < 0 Error -*/ - -static int default_local_infile_read(void *ptr, char *buf, uint buf_len) -{ - int count; - default_local_infile_data*data = (default_local_infile_data *) ptr; - - if ((count= (int) my_read(data->fd, (uchar *) buf, buf_len, MYF(0))) < 0) - { - char errbuf[MYSYS_STRERROR_SIZE]; - data->error_num= EE_READ; /* the errmsg for not entire file read */ - my_snprintf(data->error_msg, sizeof(data->error_msg)-1, - EE(EE_READ), data->filename, - my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno())); - } - return count; -} - - -/* - Read data for LOAD LOCAL INFILE - - SYNOPSIS - default_local_infile_end() - ptr Points to handle allocated by _init - May be NULL if _init failed! - - RETURN -*/ - -static void default_local_infile_end(void *ptr) -{ - default_local_infile_data *data= (default_local_infile_data *) ptr; - if (data) /* If not error on open */ - { - if (data->fd >= 0) - my_close(data->fd, MYF(MY_WME)); - my_free(ptr); - } -} - - -/* - Return error from LOAD LOCAL INFILE - - SYNOPSIS - default_local_infile_end() - ptr Points to handle allocated by _init - May be NULL if _init failed! - error_msg Store error text here - error_msg_len Max lenght of error_msg - - RETURN - error message number -*/ - -static int -default_local_infile_error(void *ptr, char *error_msg, uint error_msg_len) -{ - default_local_infile_data *data = (default_local_infile_data *) ptr; - if (data) /* If not error on open */ - { - strmake(error_msg, data->error_msg, error_msg_len); - return data->error_num; - } - /* This can only happen if we got error on malloc of handle */ - my_stpcpy(error_msg, ER(CR_OUT_OF_MEMORY)); - return CR_OUT_OF_MEMORY; -} - - -void -mysql_set_local_infile_handler(MYSQL *mysql, - int (*local_infile_init)(void **, const char *, - void *), - int (*local_infile_read)(void *, char *, uint), - void (*local_infile_end)(void *), - int (*local_infile_error)(void *, char *, uint), - void *userdata) -{ - mysql->options.local_infile_init= local_infile_init; - mysql->options.local_infile_read= local_infile_read; - mysql->options.local_infile_end= local_infile_end; - mysql->options.local_infile_error= local_infile_error; - mysql->options.local_infile_userdata = userdata; -} - - -void mysql_set_local_infile_default(MYSQL *mysql) -{ - mysql->options.local_infile_init= default_local_infile_init; - mysql->options.local_infile_read= default_local_infile_read; - mysql->options.local_infile_end= default_local_infile_end; - mysql->options.local_infile_error= default_local_infile_error; -} - - -/************************************************************************** - Do a query. If query returned rows, free old rows. - Read data by mysql_store_result or by repeat call of mysql_fetch_row -**************************************************************************/ - -int STDCALL -mysql_query(MYSQL *mysql, const char *query) -{ - return mysql_real_query(mysql,query, (ulong) strlen(query)); -} - - -/************************************************************************** - Return next field of the query results -**************************************************************************/ - -MYSQL_FIELD * STDCALL -mysql_fetch_field(MYSQL_RES *result) -{ - if (result->current_field >= result->field_count) - return(NULL); - return &result->fields[result->current_field++]; -} - - -/************************************************************************** - Move to a specific row and column -**************************************************************************/ - -void STDCALL -mysql_data_seek(MYSQL_RES *result, my_ulonglong row) -{ - MYSQL_ROWS *tmp=0; - DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row)); - if (result->data) - for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ; - result->current_row=0; - result->data_cursor = tmp; -} - - -/************************************************************************* - put the row or field cursor one a position one got from mysql_row_tell() - This doesn't restore any data. The next mysql_fetch_row or - mysql_fetch_field will return the next row or field after the last used -*************************************************************************/ - -MYSQL_ROW_OFFSET STDCALL -mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET row) -{ - MYSQL_ROW_OFFSET return_value=result->data_cursor; - result->current_row= 0; - result->data_cursor= row; - return return_value; -} - - -MYSQL_FIELD_OFFSET STDCALL -mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET field_offset) -{ - MYSQL_FIELD_OFFSET return_value=result->current_field; - result->current_field=field_offset; - return return_value; -} - - -/***************************************************************************** - List all databases -*****************************************************************************/ - -MYSQL_RES * STDCALL -mysql_list_dbs(MYSQL *mysql, const char *wild) -{ - char buff[255]; - DBUG_ENTER("mysql_list_dbs"); - - append_wild(my_stpcpy(buff,"show databases"),buff+sizeof(buff),wild); - if (mysql_query(mysql,buff)) - DBUG_RETURN(0); - DBUG_RETURN (mysql_store_result(mysql)); -} - - -/***************************************************************************** - List all tables in a database - If wild is given then only the tables matching wild is returned -*****************************************************************************/ - -MYSQL_RES * STDCALL -mysql_list_tables(MYSQL *mysql, const char *wild) -{ - char buff[255]; - DBUG_ENTER("mysql_list_tables"); - - append_wild(my_stpcpy(buff,"show tables"),buff+sizeof(buff),wild); - if (mysql_query(mysql,buff)) - DBUG_RETURN(0); - DBUG_RETURN (mysql_store_result(mysql)); -} - - -MYSQL_FIELD *cli_list_fields(MYSQL *mysql) -{ - MYSQL_DATA *query; - MYSQL_FIELD *result; - - MYSQL_TRACE_STAGE(mysql, WAIT_FOR_FIELD_DEF); - query= cli_read_rows(mysql,(MYSQL_FIELD*) 0, - protocol_41(mysql) ? 8 : 6); - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); - - if (!query) - return NULL; - - mysql->field_count= (uint) query->rows; - result= unpack_fields(mysql, query->data,&mysql->field_alloc, - mysql->field_count, 1, mysql->server_capabilities); - free_rows(query); - return result; -} - - -/************************************************************************** - List all fields in a table - If wild is given then only the fields matching wild is returned - Instead of this use query: - show fields in 'table' like "wild" -**************************************************************************/ - -MYSQL_RES * STDCALL -mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) -{ - MYSQL_RES *result; - MYSQL_FIELD *fields; - char buff[258],*end; - DBUG_ENTER("mysql_list_fields"); - DBUG_PRINT("enter",("table: '%s' wild: '%s'",table,wild ? wild : "")); - - end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128); - free_old_query(mysql); - if (simple_command(mysql, COM_FIELD_LIST, (uchar*) buff, - (ulong) (end-buff), 1) || - !(fields= (*mysql->methods->list_fields)(mysql))) - DBUG_RETURN(NULL); - - if (!(result = (MYSQL_RES *) my_malloc(PSI_NOT_INSTRUMENTED, - sizeof(MYSQL_RES), - MYF(MY_WME | MY_ZEROFILL)))) - DBUG_RETURN(NULL); - - result->methods= mysql->methods; - result->field_alloc=mysql->field_alloc; - mysql->fields=0; - result->field_count = mysql->field_count; - result->fields= fields; - result->eof=1; - DBUG_RETURN(result); -} - -/* List all running processes (threads) in server */ - -MYSQL_RES * STDCALL -mysql_list_processes(MYSQL *mysql) -{ - uint field_count; - uchar *pos; - DBUG_ENTER("mysql_list_processes"); - - if (simple_command(mysql,COM_PROCESS_INFO,0,0,0)) - DBUG_RETURN(0); - free_old_query(mysql); - pos=(uchar*) mysql->net.read_pos; - field_count=(uint) net_field_length(&pos); - if (!(mysql->fields=cli_read_metadata(mysql, field_count, protocol_41(mysql) ? 7:5))) - DBUG_RETURN(NULL); - mysql->status=MYSQL_STATUS_GET_RESULT; - mysql->field_count=field_count; - DBUG_RETURN(mysql_store_result(mysql)); -} - - -int STDCALL -mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) -{ - DBUG_ENTER("mysql_shutdown"); - if (mysql_get_server_version(mysql) < 50709) - { - uchar level[1]; - level[0]= (uchar) shutdown_level; - DBUG_RETURN (simple_command(mysql, COM_SHUTDOWN, level, 1, 0)); - } - - DBUG_RETURN(mysql_real_query(mysql, C_STRING_WITH_LEN("shutdown"))); -} - - -int STDCALL -mysql_refresh(MYSQL *mysql,uint options) -{ - uchar bits[1]; - DBUG_ENTER("mysql_refresh"); - bits[0]= (uchar) options; - DBUG_RETURN(simple_command(mysql, COM_REFRESH, bits, 1, 0)); -} - - -int STDCALL -mysql_kill(MYSQL *mysql,ulong pid) -{ - uchar buff[4]; - DBUG_ENTER("mysql_kill"); - /* - Sanity check: if ulong is 64-bits, user can submit a PID here that - overflows our 32-bit parameter to the somewhat obsolete COM_PROCESS_KILL. - If this is the case, we'll flag an error here. - The SQL statement KILL CONNECTION is the safer option here. - There is an analog of this failsafe in the server as we might see old - libmysql connection to a new server as well as the other way around. - */ - if (pid & (~0xfffffffful)) - DBUG_RETURN(CR_INVALID_CONN_HANDLE); - int4store(buff,pid); - DBUG_RETURN(simple_command(mysql,COM_PROCESS_KILL,buff,sizeof(buff),0)); -} - - -int STDCALL -mysql_set_server_option(MYSQL *mysql, enum enum_mysql_set_option option) -{ - uchar buff[2]; - DBUG_ENTER("mysql_set_server_option"); - int2store(buff, (uint) option); - DBUG_RETURN(simple_command(mysql, COM_SET_OPTION, buff, sizeof(buff), 0)); -} - - -int STDCALL -mysql_dump_debug_info(MYSQL *mysql) -{ - DBUG_ENTER("mysql_dump_debug_info"); - DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0)); -} - - -const char *cli_read_statistics(MYSQL *mysql) -{ - mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */ - if (!mysql->net.read_pos[0]) - { - set_mysql_error(mysql, CR_WRONG_HOST_INFO, unknown_sqlstate); - return mysql->net.last_error; - } - /* - After reading the single packet with reply to COM_STATISTICS - we are ready for new commands. - */ - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); - return (char*) mysql->net.read_pos; -} - - -const char * STDCALL -mysql_stat(MYSQL *mysql) -{ - DBUG_ENTER("mysql_stat"); - if (simple_command(mysql,COM_STATISTICS,0,0,0)) - DBUG_RETURN(mysql->net.last_error); - DBUG_RETURN((*mysql->methods->read_statistics)(mysql)); -} - - -int STDCALL -mysql_ping(MYSQL *mysql) -{ - int res; - DBUG_ENTER("mysql_ping"); - res= simple_command(mysql,COM_PING,0,0,0); - if (res == CR_SERVER_LOST && mysql->reconnect) - res= simple_command(mysql,COM_PING,0,0,0); - DBUG_RETURN(res); -} - - -const char * STDCALL -mysql_get_server_info(MYSQL *mysql) -{ - return((char*) mysql->server_version); -} - - -const char * STDCALL -mysql_get_host_info(MYSQL *mysql) -{ - return(mysql->host_info); -} - - -uint STDCALL -mysql_get_proto_info(MYSQL *mysql) -{ - return (mysql->protocol_version); -} - -const char * STDCALL -mysql_get_client_info(void) -{ - return (char*) MYSQL_SERVER_VERSION; -} - -ulong STDCALL mysql_get_client_version(void) -{ - return MYSQL_VERSION_ID; -} - -my_bool STDCALL mysql_eof(MYSQL_RES *res) -{ - return res->eof; -} - -MYSQL_FIELD * STDCALL mysql_fetch_field_direct(MYSQL_RES *res,uint fieldnr) -{ - return &(res)->fields[fieldnr]; -} - -MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res) -{ - return (res)->fields; -} - -MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res) -{ - return res->data_cursor; -} - -MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res) -{ - return (res)->current_field; -} - -/* MYSQL */ - -unsigned int STDCALL mysql_field_count(MYSQL *mysql) -{ - return mysql->field_count; -} - -my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) -{ - return mysql->affected_rows; -} - -my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) -{ - return mysql->insert_id; -} - -const char *STDCALL mysql_sqlstate(MYSQL *mysql) -{ - return mysql ? mysql->net.sqlstate : cant_connect_sqlstate; -} - -uint STDCALL mysql_warning_count(MYSQL *mysql) -{ - return mysql->warning_count; -} - -const char *STDCALL mysql_info(MYSQL *mysql) -{ - if (!mysql) - { -#if defined(CLIENT_PROTOCOL_TRACING) - return "protocol tracing enabled"; -#else - return NULL; -#endif - } - return mysql->info; -} - -ulong STDCALL mysql_thread_id(MYSQL *mysql) -{ - /* - ulong may be 64-bit, but we currently only transmit 32-bit. - mysql_thread_id() is usually used in conjunction with mysql_kill() - which is similarly limited (and obsolete). - SELECTION CONNECTION_ID() / KILL CONNECTION avoid this issue. - */ - return (mysql)->thread_id; -} - -const char * STDCALL mysql_character_set_name(MYSQL *mysql) -{ - return mysql->charset->csname; -} - -void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *csinfo) -{ - csinfo->number = mysql->charset->number; - csinfo->state = mysql->charset->state; - csinfo->csname = mysql->charset->csname; - csinfo->name = mysql->charset->name; - csinfo->comment = mysql->charset->comment; - csinfo->mbminlen = mysql->charset->mbminlen; - csinfo->mbmaxlen = mysql->charset->mbmaxlen; - - if (mysql->options.charset_dir) - csinfo->dir = mysql->options.charset_dir; - else - csinfo->dir = charsets_dir; -} - -uint STDCALL mysql_thread_safe(void) -{ - return 1; -} - - -my_bool STDCALL mysql_embedded(void) -{ -#ifdef EMBEDDED_LIBRARY - return 1; -#else - return 0; -#endif -} - -/**************************************************************************** - Some support functions -****************************************************************************/ - -/* - Functions called my my_net_init() to set some application specific variables -*/ - -void my_net_local_init(NET *net) -{ - ulong local_net_buffer_length= 0; - ulong local_max_allowed_packet= 0; - - (void) mysql_get_option(NULL, MYSQL_OPT_MAX_ALLOWED_PACKET, - &local_max_allowed_packet); - (void) mysql_get_option(NULL, MYSQL_OPT_NET_BUFFER_LENGTH, - &local_net_buffer_length); - - net->max_packet= (uint) local_net_buffer_length; - my_net_set_read_timeout(net, CLIENT_NET_READ_TIMEOUT); - my_net_set_write_timeout(net, CLIENT_NET_WRITE_TIMEOUT); - net->retry_count= 1; - net->max_packet_size= MY_MAX(local_net_buffer_length, local_max_allowed_packet); -} - -/* - This function is used to create HEX string that you - can use in a SQL statement in of the either ways: - INSERT INTO blob_column VALUES (0xAABBCC); (any MySQL version) - INSERT INTO blob_column VALUES (X'AABBCC'); (4.1 and higher) - - The string in "from" is encoded to a HEX string. - The result is placed in "to" and a terminating null byte is appended. - - The string pointed to by "from" must be "length" bytes long. - You must allocate the "to" buffer to be at least length*2+1 bytes long. - Each character needs two bytes, and you need room for the terminating - null byte. When mysql_hex_string() returns, the contents of "to" will - be a null-terminated string. The return value is the length of the - encoded string, not including the terminating null character. - - The return value does not contain any leading 0x or a leading X' and - trailing '. The caller must supply whichever of those is desired. -*/ - -ulong STDCALL -mysql_hex_string(char *to, const char *from, ulong length) -{ - char *to0= to; - const char *end; - - for (end= from + length; from < end; from++) - { - *to++= _dig_vec_upper[((unsigned char) *from) >> 4]; - *to++= _dig_vec_upper[((unsigned char) *from) & 0x0F]; - } - *to= '\0'; - return (ulong) (to-to0); -} - -/* - Add escape characters to a string (blob?) to make it suitable for a insert - to should at least have place for length*2+1 chars - Returns the length of the to string -*/ - -ulong STDCALL -mysql_escape_string(char *to,const char *from,ulong length) -{ - return (uint) escape_string_for_mysql(default_charset_info, to, 0, from, length); -} - -/** - Escapes special characters in a string for use in an SQL statement. - - Escapes special characters in the unescaped string, taking into account - the current character set and sql mode of the connection so that is safe - to place the string in a mysql_query(). This function must be used for - binary data. - - This function does not work correctly when NO_BACKSLASH_ESCAPES sql mode - is used and string quote in the SQL statement is different than '\''. - - @deprecated This function should not be used. - Use mysql_real_escape_string_quote instead. - - @see mysql_real_escape_string_quote - - @param mysql [in] MySQL connection structure. - @param to [out] Escaped string output buffer. - @param from [in] String to escape. - @param length [in] String to escape length. - - @return Result value. - @retval != (ulong)-1 Succeeded. Number of bytes written to the output - buffer without the '\0' character. - @retval (ulong)-1 Failed. Use mysql_error() to get error message. -*/ - -ulong STDCALL -mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, - ulong length) -{ - if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) - { - DBUG_PRINT("error", - ("NO_BACKSLASH_ESCAPES sql mode requires usage of the " - "mysql_real_escape_string_quote function")); - set_mysql_extended_error(mysql, CR_INSECURE_API_ERR, unknown_sqlstate, - ER(CR_INSECURE_API_ERR), - "mysql_real_escape_string", - "mysql_real_escape_string_quote"); - return (ulong)-1; - } - - return (uint)mysql_real_escape_string_quote(mysql, to, from, length, '\''); -} - -/** - Escapes special characters in a string for use in an SQL statement. - - Escapes special characters in the unescaped string, taking into account - the current character set and sql mode of the connection so that is safe - to place the string in a mysql_query(). This function must be used for - binary data. - - This function should be used for escaping identifiers and string parameters. - - @param mysql [in] MySQL connection structure. - @param to [out] Escaped string output buffer. - @param from [in] String to escape. - @param length [in] String to escape length. - @param quote [in] String quoting character used in an SQL statement. This - should be one of '\'', '"' or '`' depending on the - parameter quoting applied in the SQL statement. - - @return Result value. - @retval != (ulong)-1 Succeeded. Number of bytes written to the output - buffer without the '\0' character. - @retval (ulong)-1 Failed. -*/ - -ulong STDCALL -mysql_real_escape_string_quote(MYSQL *mysql, char *to, const char *from, - ulong length, char quote) -{ - if (quote == '`' || mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) - return (uint)escape_quotes_for_mysql(mysql->charset, to, 0, - from, length, quote); - return (uint)escape_string_for_mysql(mysql->charset, to, 0, from, length); -} - -void STDCALL -myodbc_remove_escape(MYSQL *mysql,char *name) -{ - char *to; - my_bool use_mb_flag=use_mb(mysql->charset); - char *end= NULL; - if (use_mb_flag) - for (end=name; *end ; end++) ; - - for (to=name ; *name ; name++) - { - int l; - if (use_mb_flag && (l = my_ismbchar( mysql->charset, name , end ) ) ) - { - while (l--) - *to++ = *name++; - name--; - continue; - } - if (*name == '\\' && name[1]) - name++; - *to++= *name; - } - *to=0; -} - -/******************************************************************** - Implementation of new client API for 4.1 version. - - mysql_stmt_* are real prototypes used by applications. - - To make API work in embedded library all functions performing - real I/O are prefixed with 'cli_' (abbreviated from 'Call Level - Interface'). This functions are invoked via pointers set in - MYSQL::methods structure. Embedded counterparts, prefixed with - 'emb_' reside in libmysqld/lib_sql.cc. -*********************************************************************/ - -/******************* Declarations ***********************************/ - -/* Default number of rows fetched per one COM_STMT_FETCH command. */ - -#define DEFAULT_PREFETCH_ROWS (ulong) 1 - -/* - These functions are called by function pointer MYSQL_STMT::read_row_func. - Each function corresponds to one of the read methods: - - mysql_stmt_fetch without prior mysql_stmt_store_result, - - mysql_stmt_fetch when result is stored, - - mysql_stmt_fetch when there are no rows (always returns MYSQL_NO_DATA) -*/ - -static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row); -static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row); -static int stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row); -static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); -static int stmt_read_row_no_result_set(MYSQL_STMT *stmt, unsigned char **row); - -/* - This function is used in mysql_stmt_store_result if - STMT_ATTR_UPDATE_MAX_LENGTH attribute is set. -*/ -static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data); -static my_bool setup_one_fetch_function(MYSQL_BIND *, MYSQL_FIELD *field); - -/* Auxilary function used to reset statement handle. */ - -#define RESET_SERVER_SIDE 1 -#define RESET_LONG_DATA 2 -#define RESET_STORE_RESULT 4 -#define RESET_CLEAR_ERROR 8 - -static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags); - -/* - Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME - values stored in network buffer. -*/ - -/* 1 (length) + 2 (year) + 1 (month) + 1 (day) */ -#define MAX_DATE_REP_LENGTH 5 - -/* - 1 (length) + 1 (is negative) + 4 (day count) + 1 (hour) - + 1 (minute) + 1 (seconds) + 4 (microseconds) -*/ -#define MAX_TIME_REP_LENGTH 13 - -/* - 1 (length) + 2 (year) + 1 (month) + 1 (day) + - 1 (hour) + 1 (minute) + 1 (second) + 4 (microseconds) -*/ -#define MAX_DATETIME_REP_LENGTH 12 - -#define MAX_DOUBLE_STRING_REP_LENGTH 331 - -/* A macro to check truncation errors */ - -#define IS_TRUNCATED(value, is_unsigned, min, max, umax) \ - ((is_unsigned) ? (((value) > (umax) || (value) < 0) ? 1 : 0) : \ - (((value) > (max) || (value) < (min)) ? 1 : 0)) - -#define BIND_RESULT_DONE 1 -/* - We report truncations only if at least one of MYSQL_BIND::error - pointers is set. In this case stmt->bind_result_done |-ed with - this flag. -*/ -#define REPORT_DATA_TRUNCATION 2 - -/**************** Misc utility functions ****************************/ - -/* - Reallocate the NET package to have at least length bytes available. - - SYNPOSIS - my_realloc_str() - net The NET structure to modify. - length Ensure that net->buff has space for at least - this number of bytes. - - RETURN VALUES - 0 Success. - 1 Error, i.e. out of memory or requested packet size is bigger - than max_allowed_packet. The error code is stored in net->last_errno. -*/ - -static my_bool my_realloc_str(NET *net, ulong length) -{ - ulong buf_length= (ulong) (net->write_pos - net->buff); - my_bool res=0; - DBUG_ENTER("my_realloc_str"); - if (buf_length + length > net->max_packet) - { - res= net_realloc(net, buf_length + length); - if (res) - { - if (net->last_errno == ER_OUT_OF_RESOURCES) - net->last_errno= CR_OUT_OF_MEMORY; - else if (net->last_errno == ER_NET_PACKET_TOO_LARGE) - net->last_errno= CR_NET_PACKET_TOO_LARGE; - - my_stpcpy(net->sqlstate, unknown_sqlstate); - my_stpcpy(net->last_error, ER(net->last_errno)); - } - net->write_pos= net->buff+ buf_length; - } - DBUG_RETURN(res); -} - - -static void stmt_clear_error(MYSQL_STMT *stmt) -{ - if (stmt->last_errno) - { - stmt->last_errno= 0; - stmt->last_error[0]= '\0'; - my_stpcpy(stmt->sqlstate, not_error_sqlstate); - } -} - -/** - Set statement error code, sqlstate, and error message - from given errcode and sqlstate. -*/ - -void set_stmt_error(MYSQL_STMT * stmt, int errcode, - const char *sqlstate, const char *err) -{ - DBUG_ENTER("set_stmt_error"); - DBUG_PRINT("enter", ("error: %d '%s'", errcode, ER(errcode))); - DBUG_ASSERT(stmt != 0); - - if (err == 0) - err= ER(errcode); - - stmt->last_errno= errcode; - my_stpcpy(stmt->last_error, ER(errcode)); - my_stpcpy(stmt->sqlstate, sqlstate); - - DBUG_VOID_RETURN; -} - - -/** - Set statement error code, sqlstate, and error message from NET. - - @param stmt a statement handle. Copy the error here. - @param net mysql->net. Source of the error. -*/ - -void set_stmt_errmsg(MYSQL_STMT *stmt, NET *net) -{ - DBUG_ENTER("set_stmt_errmsg"); - DBUG_PRINT("enter", ("error: %d/%s '%s'", - net->last_errno, - net->sqlstate, - net->last_error)); - DBUG_ASSERT(stmt != 0); - - stmt->last_errno= net->last_errno; - if (net->last_error[0] != '\0') - my_stpcpy(stmt->last_error, net->last_error); - my_stpcpy(stmt->sqlstate, net->sqlstate); - - DBUG_VOID_RETURN; -} - -/* - Read and unpack server reply to COM_STMT_PREPARE command (sent from - mysql_stmt_prepare). - - SYNOPSIS - cli_read_prepare_result() - mysql connection handle - stmt statement handle - - RETURN VALUES - 0 ok - 1 error -*/ - -my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) -{ - uchar *pos; - uint field_count, param_count; - ulong packet_length; - DBUG_ENTER("cli_read_prepare_result"); - - /* free old result and initialize mysql->field_alloc */ - free_old_query(mysql); - - if ((packet_length= cli_safe_read(mysql, NULL)) == packet_error) - DBUG_RETURN(1); - mysql->warning_count= 0; - - pos= (uchar*) mysql->net.read_pos; - stmt->stmt_id= uint4korr(pos+1); pos+= 5; - /* Number of columns in result set */ - field_count= uint2korr(pos); pos+= 2; - /* Number of placeholders in the statement */ - param_count= uint2korr(pos); pos+= 2; - if (packet_length >= 12) - mysql->warning_count= uint2korr(pos+1); - - if (param_count != 0) - { - MYSQL_TRACE_STAGE(mysql, WAIT_FOR_PARAM_DEF); - /* skip parameters data: we don't support it yet */ - if (!(cli_read_metadata(mysql, param_count, 7))) - DBUG_RETURN(1); - /* free memory allocated by cli_read_metadata() for parameters data */ - free_root(&mysql->field_alloc, MYF(0)); - } - - if (field_count != 0) - { - if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) - mysql->server_status|= SERVER_STATUS_IN_TRANS; - - MYSQL_TRACE_STAGE(mysql, WAIT_FOR_FIELD_DEF); - if (!(stmt->fields= cli_read_metadata_ex(mysql, &stmt->mem_root, - field_count, 7))) - DBUG_RETURN(1); - } - - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); - - stmt->field_count= field_count; - stmt->param_count= (ulong) param_count; - DBUG_PRINT("exit",("field_count: %u param_count: %u warning_count: %u", - field_count, param_count, (uint) mysql->warning_count)); - - DBUG_RETURN(0); -} - - -/* - Allocate memory and init prepared statement structure. - - SYNOPSIS - mysql_stmt_init() - mysql connection handle - - DESCRIPTION - This is an entry point of the new API. Returned handle stands for - a server-side prepared statement. Memory for this structure (~700 - bytes) is allocated using 'malloc'. Once created, the handle can be - reused many times. Created statement handle is bound to connection - handle provided to this call: its lifetime is limited by lifetime - of connection. - 'mysql_stmt_init()' is a pure local call, server side structure is - created only in mysql_stmt_prepare. - Next steps you may want to make: - - set a statement attribute (mysql_stmt_attr_set()), - - prepare statement handle with a query (mysql_stmt_prepare()), - - close statement handle and free its memory (mysql_stmt_close()), - - reset statement with mysql_stmt_reset() (a no-op which will - just return). - Behaviour of the rest of API calls on this statement is not defined yet - (though we're working on making each wrong call sequence return - error). - - RETURN VALUE - statement structure upon success and NULL if out of - memory -*/ - -MYSQL_STMT * STDCALL -mysql_stmt_init(MYSQL *mysql) -{ - MYSQL_STMT *stmt; - DBUG_ENTER("mysql_stmt_init"); - - if (!(stmt= - (MYSQL_STMT *) my_malloc(PSI_NOT_INSTRUMENTED, - sizeof (MYSQL_STMT), - MYF(MY_WME | MY_ZEROFILL))) || - !(stmt->extension= - (MYSQL_STMT_EXT *) my_malloc(PSI_NOT_INSTRUMENTED, - sizeof (MYSQL_STMT_EXT), - MYF(MY_WME | MY_ZEROFILL)))) - { - set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); - my_free(stmt); - DBUG_RETURN(NULL); - } - - init_alloc_root(PSI_NOT_INSTRUMENTED, &stmt->mem_root, 2048, 2048); - init_alloc_root(PSI_NOT_INSTRUMENTED, &stmt->result.alloc, 4096, 4096); - stmt->result.alloc.min_malloc= sizeof(MYSQL_ROWS); - mysql->stmts= list_add(mysql->stmts, &stmt->list); - stmt->list.data= stmt; - stmt->state= MYSQL_STMT_INIT_DONE; - stmt->mysql= mysql; - stmt->read_row_func= stmt_read_row_no_result_set; - stmt->prefetch_rows= DEFAULT_PREFETCH_ROWS; - my_stpcpy(stmt->sqlstate, not_error_sqlstate); - /* The rest of statement members was zeroed inside malloc */ - - init_alloc_root(PSI_NOT_INSTRUMENTED, &stmt->extension->fields_mem_root, 2048, 0); - - DBUG_RETURN(stmt); -} - - -/* - Prepare server side statement with query. - - SYNOPSIS - mysql_stmt_prepare() - stmt statement handle - query statement to prepare - length statement length - - DESCRIPTION - Associate statement with statement handle. This is done both on - client and server sides. At this point the server parses given query - and creates an internal structure to represent it. - Next steps you may want to make: - - find out if this statement returns a result set by - calling mysql_stmt_field_count(), and get result set metadata - with mysql_stmt_result_metadata(), - - if query contains placeholders, bind input parameters to placeholders - using mysql_stmt_bind_param(), - - otherwise proceed directly to mysql_stmt_execute(). - - IMPLEMENTATION NOTES - - if this is a re-prepare of the statement, first close previous data - structure on the server and free old statement data - - then send the query to server and get back number of placeholders, - number of columns in result set (if any), and result set metadata. - At the same time allocate memory for input and output parameters - to have less checks in mysql_stmt_bind_{param, result}. - - RETURN VALUES - 0 success - !0 error -*/ - -int STDCALL -mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) -{ - MYSQL *mysql= stmt->mysql; - DBUG_ENTER("mysql_stmt_prepare"); - - if (!mysql) - { - /* mysql can be reset in mysql_close called from mysql_reconnect */ - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - /* - Reset the last error in any case: that would clear the statement - if the previous prepare failed. - */ - stmt->last_errno= 0; - stmt->last_error[0]= '\0'; - - if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) - { - /* This is second prepare with another statement */ - uchar buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ - - if (reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT)) - DBUG_RETURN(1); - /* - These members must be reset for API to - function in case of error or misuse. - */ - stmt->bind_param_done= stmt->bind_result_done= FALSE; - stmt->param_count= stmt->field_count= 0; - free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); - free_root(&stmt->extension->fields_mem_root, MYF(0)); - - int4store(buff, stmt->stmt_id); - - /* - Close statement in server - - If there was a 'use' result from another statement, or from - mysql_use_result it won't be freed in mysql_stmt_free_result and - we should get 'Commands out of sync' here. - */ - stmt->state= MYSQL_STMT_INIT_DONE; - if (stmt_command(mysql, COM_STMT_CLOSE, buff, 4, stmt)) - { - set_stmt_errmsg(stmt, &mysql->net); - DBUG_RETURN(1); - } - } - - if (stmt_command(mysql, COM_STMT_PREPARE, (const uchar*) query, length, stmt)) - { - set_stmt_errmsg(stmt, &mysql->net); - DBUG_RETURN(1); - } - - if ((*mysql->methods->read_prepare_result)(mysql, stmt)) - { - set_stmt_errmsg(stmt, &mysql->net); - DBUG_RETURN(1); - } - - /* - alloc_root will return valid address even in case when param_count - and field_count are zero. Thus we should never rely on stmt->bind - or stmt->params when checking for existence of placeholders or - result set. - */ - if (!(stmt->params= (MYSQL_BIND *) alloc_root(&stmt->mem_root, - sizeof(MYSQL_BIND)* - (stmt->param_count + - stmt->field_count)))) - { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - stmt->bind= stmt->params + stmt->param_count; - stmt->state= MYSQL_STMT_PREPARE_DONE; - DBUG_PRINT("info", ("Parameter count: %u", stmt->param_count)); - DBUG_RETURN(0); -} - -/* - Get result set metadata from reply to mysql_stmt_execute. - This is used mainly for SHOW commands, as metadata for these - commands is sent only with result set. - To be removed when all commands will fully support prepared mode. -*/ - -static void alloc_stmt_fields(MYSQL_STMT *stmt) -{ - MYSQL_FIELD *fields, *field, *end; - MEM_ROOT *fields_mem_root= &stmt->extension->fields_mem_root; - MYSQL *mysql= stmt->mysql; - - DBUG_ASSERT(stmt->field_count); - - free_root(fields_mem_root, MYF(0)); - - /* - Get the field information for non-select statements - like SHOW and DESCRIBE commands - */ - if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(fields_mem_root, - sizeof(MYSQL_FIELD) * - stmt->field_count)) || - !(stmt->bind= (MYSQL_BIND *) alloc_root(fields_mem_root, - sizeof(MYSQL_BIND) * - stmt->field_count))) - { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); - return; - } - - for (fields= mysql->fields, end= fields+stmt->field_count, - field= stmt->fields; - field && fields < end; fields++, field++) - { - *field= *fields; /* To copy all numeric parts. */ - field->catalog= strmake_root(fields_mem_root, - fields->catalog, - fields->catalog_length); - field->db= strmake_root(fields_mem_root, - fields->db, - fields->db_length); - field->table= strmake_root(fields_mem_root, - fields->table, - fields->table_length); - field->org_table= strmake_root(fields_mem_root, - fields->org_table, - fields->org_table_length); - field->name= strmake_root(fields_mem_root, - fields->name, - fields->name_length); - field->org_name= strmake_root(fields_mem_root, - fields->org_name, - fields->org_name_length); - if (fields->def) - { - field->def= strmake_root(fields_mem_root, - fields->def, - fields->def_length); - field->def_length= fields->def_length; - } - else - { - field->def= NULL; - field->def_length= 0; - } - field->extension= 0; /* Avoid dangling links. */ - field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */ - } -} - - -/** - Update result set columns metadata if it was sent again in - reply to COM_STMT_EXECUTE. - - @note If the new field count is different from the original one, - an error is set and no update is performed. -*/ - -static void update_stmt_fields(MYSQL_STMT *stmt) -{ - MYSQL_FIELD *field= stmt->mysql->fields; - MYSQL_FIELD *field_end= field + stmt->field_count; - MYSQL_FIELD *stmt_field= stmt->fields; - MYSQL_BIND *my_bind= stmt->bind_result_done ? stmt->bind : 0; - - if (stmt->field_count != stmt->mysql->field_count) - { - /* - The tables used in the statement were altered, - and the query now returns a different number of columns. - There is no way to continue without reallocating the bind - array: - - if the number of columns increased, mysql_stmt_fetch() - will write beyond allocated memory - - if the number of columns decreased, some user-bound - buffers will be left unassigned without user knowing - that. - */ - set_stmt_error(stmt, CR_NEW_STMT_METADATA, unknown_sqlstate, NULL); - return; - } - - for (; field < field_end; ++field, ++stmt_field) - { - stmt_field->charsetnr= field->charsetnr; - stmt_field->length = field->length; - stmt_field->type = field->type; - stmt_field->flags = field->flags; - stmt_field->decimals = field->decimals; - if (my_bind) - { - /* Ignore return value: it should be 0 if bind_result succeeded. */ - (void) setup_one_fetch_function(my_bind++, stmt_field); - } - } -} - -/* - Returns prepared statement metadata in the form of a result set. - - SYNOPSIS - mysql_stmt_result_metadata() - stmt statement handle - - DESCRIPTION - This function should be used after mysql_stmt_execute(). - You can safely check that prepared statement has a result set by calling - mysql_stmt_field_count(): if number of fields is not zero, you can call - this function to get fields metadata. - Next steps you may want to make: - - find out number of columns in result set by calling - mysql_num_fields(res) (the same value is returned by - mysql_stmt_field_count()) - - fetch metadata for any column with mysql_fetch_field, - mysql_fetch_field_direct, mysql_fetch_fields, mysql_field_seek. - - free returned MYSQL_RES structure with mysql_free_result. - - proceed to binding of output parameters. - - RETURN - NULL statement contains no result set or out of memory. - In the latter case you can retreive error message - with mysql_stmt_error. - MYSQL_RES a result set with no rows -*/ - -MYSQL_RES * STDCALL -mysql_stmt_result_metadata(MYSQL_STMT *stmt) -{ - MYSQL_RES *result; - DBUG_ENTER("mysql_stmt_result_metadata"); - - /* - stmt->fields is only defined if stmt->field_count is not null; - stmt->field_count is initialized in prepare. - */ - if (!stmt->field_count) - DBUG_RETURN(0); - - if (!(result=(MYSQL_RES*) my_malloc(PSI_NOT_INSTRUMENTED, - sizeof(*result), - MYF(MY_WME | MY_ZEROFILL)))) - { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); - DBUG_RETURN(0); - } - - result->methods= stmt->mysql->methods; - result->eof= 1; /* Marker for buffered */ - result->fields= stmt->fields; - result->field_count= stmt->field_count; - /* The rest of members of 'result' was zeroed inside malloc */ - DBUG_RETURN(result); -} - - -/* - Returns parameter columns meta information in the form of - result set. - - SYNOPSYS - mysql_stmt_param_metadata() - stmt statement handle - - DESCRIPTION - This function can be called after you prepared the statement handle - with mysql_stmt_prepare(). - XXX: not implemented yet. - - RETURN - MYSQL_RES on success, 0 if there is no metadata. - Currently this function always returns 0. -*/ - -MYSQL_RES * STDCALL -mysql_stmt_param_metadata(MYSQL_STMT *stmt) -{ - DBUG_ENTER("mysql_stmt_param_metadata"); - - if (!stmt->param_count) - DBUG_RETURN(0); - - /* - TODO: Fix this when server sends the information. - Till then keep a dummy prototype. - */ - DBUG_RETURN(0); -} - - -/* Store type of parameter in network buffer. */ - -static void store_param_type(unsigned char **pos, MYSQL_BIND *param) -{ - uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0); - int2store(*pos, typecode); - *pos+= 2; -} - - -/* - Functions to store parameter data in network packet. - - SYNOPSIS - store_param_xxx() - net MySQL NET connection - param MySQL bind param - - DESCRIPTION - These funtions are invoked from mysql_stmt_execute() by - MYSQL_BIND::store_param_func pointer. This pointer is set once per - many executions in mysql_stmt_bind_param(). The caller must ensure - that network buffer have enough capacity to store parameter - (MYSQL_BIND::buffer_length contains needed number of bytes). -*/ - -static void store_param_tinyint(NET *net, MYSQL_BIND *param) -{ - *(net->write_pos++)= *(uchar *) param->buffer; -} - -static void store_param_short(NET *net, MYSQL_BIND *param) -{ - short value= *(short*) param->buffer; - int2store(net->write_pos,value); - net->write_pos+=2; -} - -static void store_param_int32(NET *net, MYSQL_BIND *param) -{ - int32 value= *(int32*) param->buffer; - int4store(net->write_pos,value); - net->write_pos+=4; -} - -static void store_param_int64(NET *net, MYSQL_BIND *param) -{ - longlong value= *(longlong*) param->buffer; - int8store(net->write_pos,value); - net->write_pos+= 8; -} - -static void store_param_float(NET *net, MYSQL_BIND *param) -{ - float value= *(float*) param->buffer; - float4store(net->write_pos, value); - net->write_pos+= 4; -} - -static void store_param_double(NET *net, MYSQL_BIND *param) -{ - double value= *(double*) param->buffer; - float8store(net->write_pos, value); - net->write_pos+= 8; -} - -static void store_param_time(NET *net, MYSQL_BIND *param) -{ - MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer; - uchar buff[MAX_TIME_REP_LENGTH], *pos; - uint length; - - pos= buff+1; - pos[0]= tm->neg ? 1: 0; - int4store(pos+1, tm->day); - pos[5]= (uchar) tm->hour; - pos[6]= (uchar) tm->minute; - pos[7]= (uchar) tm->second; - int4store(pos+8, tm->second_part); - if (tm->second_part) - length= 12; - else if (tm->hour || tm->minute || tm->second || tm->day) - length= 8; - else - length= 0; - buff[0]= (char) length++; - memcpy((char *)net->write_pos, buff, length); - net->write_pos+= length; -} - -static void net_store_datetime(NET *net, MYSQL_TIME *tm) -{ - uchar buff[MAX_DATETIME_REP_LENGTH], *pos; - uint length; - - pos= buff+1; - - int2store(pos, tm->year); - pos[2]= (uchar) tm->month; - pos[3]= (uchar) tm->day; - pos[4]= (uchar) tm->hour; - pos[5]= (uchar) tm->minute; - pos[6]= (uchar) tm->second; - int4store(pos+7, tm->second_part); - if (tm->second_part) - length= 11; - else if (tm->hour || tm->minute || tm->second) - length= 7; - else if (tm->year || tm->month || tm->day) - length= 4; - else - length= 0; - buff[0]= (char) length++; - memcpy((char *)net->write_pos, buff, length); - net->write_pos+= length; -} - -static void store_param_date(NET *net, MYSQL_BIND *param) -{ - MYSQL_TIME tm= *((MYSQL_TIME *) param->buffer); - tm.hour= tm.minute= tm.second= tm.second_part= 0; - net_store_datetime(net, &tm); -} - -static void store_param_datetime(NET *net, MYSQL_BIND *param) -{ - MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer; - net_store_datetime(net, tm); -} - -static void store_param_str(NET *net, MYSQL_BIND *param) -{ - /* param->length is always set in mysql_stmt_bind_param */ - ulong length= *param->length; - uchar *to= net_store_length(net->write_pos, length); - memcpy(to, param->buffer, length); - net->write_pos= to+length; -} - - -/* - Mark if the parameter is NULL. - - SYNOPSIS - store_param_null() - net MySQL NET connection - param MySQL bind param - - DESCRIPTION - A data package starts with a string of bits where we set a bit - if a parameter is NULL. Unlike bit string in result set row, here - we don't have reserved bits for OK/error packet. -*/ - -static void store_param_null(NET *net, MYSQL_BIND *param) -{ - uint pos= param->param_number; - net->buff[pos/8]|= (uchar) (1 << (pos & 7)); -} - - -/* - Store one parameter in network packet: data is read from - client buffer and saved in network packet by means of one - of store_param_xxxx functions. -*/ - -static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) -{ - NET *net= &stmt->mysql->net; - DBUG_ENTER("store_param"); - DBUG_PRINT("enter",("type: %d buffer: 0x%lx length: %lu is_null: %d", - param->buffer_type, - (long) (param->buffer ? param->buffer : NullS), - *param->length, *param->is_null)); - - if (*param->is_null) - store_param_null(net, param); - else - { - /* - Param->length should ALWAYS point to the correct length for the type - Either to the length pointer given by the user or param->buffer_length - */ - if ((my_realloc_str(net, *param->length))) - { - set_stmt_errmsg(stmt, net); - DBUG_RETURN(1); - } - (*param->store_param_func)(net, param); - } - DBUG_RETURN(0); -} - -static inline int add_binary_row(NET *net, MYSQL_STMT *stmt, ulong pkt_len, MYSQL_ROWS ***prev_ptr) -{ - MYSQL_ROWS *row; - uchar *cp= net->read_pos; - MYSQL_DATA *result= &stmt->result; - if (!(row= (MYSQL_ROWS*) alloc_root(&result->alloc, - sizeof(MYSQL_ROWS) + pkt_len - 1))) - { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); - return 1; - } - row->data= (MYSQL_ROW) (row+1); - **prev_ptr= row; - *prev_ptr= &row->next; - memcpy((char *) row->data, (char *) cp+1, pkt_len-1); - row->length= pkt_len; /* To allow us to do sanity checks */ - result->rows++; - return 0; -} - -/* - Auxilary function to send COM_STMT_EXECUTE packet to server and read reply. - Used from cli_stmt_execute, which is in turn used by mysql_stmt_execute. -*/ - -static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) -{ - MYSQL *mysql= stmt->mysql; - NET *net= &mysql->net; - uchar buff[4 /* size of stmt id */ + - 5 /* execution flags */]; - my_bool res; - my_bool is_data_packet= FALSE; - ulong pkt_len; - MYSQL_ROWS **prev_ptr= NULL; - DBUG_ENTER("execute"); - DBUG_DUMP("packet", (uchar *) packet, length); - - int4store(buff, stmt->stmt_id); /* Send stmt id to server */ - buff[4]= (char) stmt->flags; - int4store(buff+5, 1); /* iteration count */ - - res= MY_TEST(cli_advanced_command(mysql, COM_STMT_EXECUTE, buff, sizeof(buff), - (uchar*) packet, length, 1, stmt) || - (*mysql->methods->read_query_result)(mysql)); - - if ((mysql->server_capabilities & CLIENT_DEPRECATE_EOF)) - { - if (mysql->server_status & SERVER_STATUS_CURSOR_EXISTS) - mysql->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; - - if (!res && (stmt->flags & CURSOR_TYPE_READ_ONLY)) - { - /* - server can now respond with a cursor - then the respond will be - or with binary rows result set . - The former can be the case when the prepared statement is a procedure - invocation, ie. call(). There also other cases. When server responds - with (cursor) packet we read it and get the server status. In case - it responds with binary row we add it to the binary rows result set - (the reset of the result set will be read in prepare_to_fetch_result). - */ - - if ((pkt_len= cli_safe_read(mysql, &is_data_packet)) == packet_error) - DBUG_RETURN(1); - - if (is_data_packet) - { - DBUG_ASSERT(stmt->result.rows == 0); - prev_ptr= &stmt->result.data; - if (add_binary_row(net, stmt, pkt_len, &prev_ptr)) - DBUG_RETURN(1); - } - else - read_ok_ex(mysql, pkt_len); - } - } - - stmt->affected_rows= mysql->affected_rows; - stmt->server_status= mysql->server_status; - stmt->insert_id= mysql->insert_id; - if (res) - { - /* - Don't set stmt error if stmt->mysql is NULL, as the error in this case - has already been set by mysql_prune_stmt_list(). - */ - if (stmt->mysql) - set_stmt_errmsg(stmt, net); - DBUG_RETURN(1); - } - else if (mysql->status == MYSQL_STATUS_GET_RESULT) - stmt->mysql->status= MYSQL_STATUS_STATEMENT_GET_RESULT; - DBUG_RETURN(0); -} - - -int cli_stmt_execute(MYSQL_STMT *stmt) -{ - DBUG_ENTER("cli_stmt_execute"); - - if (stmt->param_count) - { - MYSQL *mysql= stmt->mysql; - NET *net= &mysql->net; - MYSQL_BIND *param, *param_end; - char *param_data; - ulong length; - uint null_count; - my_bool result; - - if (!stmt->bind_param_done) - { - set_stmt_error(stmt, CR_PARAMS_NOT_BOUND, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - if (mysql->status != MYSQL_STATUS_READY || - mysql->server_status & SERVER_MORE_RESULTS_EXISTS) - { - set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - if (net->vio) - net_clear(net, 1); /* Sets net->write_pos */ - else - { - set_stmt_errmsg(stmt, net); - DBUG_RETURN(1); - } - - /* Reserve place for null-marker bytes */ - null_count= (stmt->param_count+7) /8; - if (my_realloc_str(net, null_count + 1)) - { - set_stmt_errmsg(stmt, net); - DBUG_RETURN(1); - } - memset(net->write_pos, 0, null_count); - net->write_pos+= null_count; - param_end= stmt->params + stmt->param_count; - - /* In case if buffers (type) altered, indicate to server */ - *(net->write_pos)++= (uchar) stmt->send_types_to_server; - if (stmt->send_types_to_server) - { - if (my_realloc_str(net, 2 * stmt->param_count)) - { - set_stmt_errmsg(stmt, net); - DBUG_RETURN(1); - } - /* - Store types of parameters in first in first package - that is sent to the server. - */ - for (param= stmt->params; param < param_end ; param++) - store_param_type(&net->write_pos, param); - } - - for (param= stmt->params; param < param_end; param++) - { - /* check if mysql_stmt_send_long_data() was used */ - if (param->long_data_used) - param->long_data_used= 0; /* Clear for next execute call */ - else if (store_param(stmt, param)) - DBUG_RETURN(1); - } - length= (ulong) (net->write_pos - net->buff); - /* TODO: Look into avoding the following memdup */ - if (!(param_data= my_memdup(PSI_NOT_INSTRUMENTED, - net->buff, length, MYF(0)))) - { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - result= execute(stmt, param_data, length); - stmt->send_types_to_server=0; - my_free(param_data); - DBUG_RETURN(result); - } - DBUG_RETURN((int) execute(stmt,0,0)); -} - -/* - Read one row from buffered result set. Result set is created by prior - call to mysql_stmt_store_result(). - SYNOPSIS - stmt_read_row_buffered() - - RETURN VALUE - 0 - success; *row is set to valid row pointer (row data - is stored in result set buffer) - MYSQL_NO_DATA - end of result set. *row is set to NULL -*/ - -static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row) -{ - if (stmt->data_cursor) - { - *row= (uchar *) stmt->data_cursor->data; - stmt->data_cursor= stmt->data_cursor->next; - return 0; - } - *row= 0; - return MYSQL_NO_DATA; -} - -/* - Read one row from network: unbuffered non-cursor fetch. - If last row was read, or error occured, erase this statement - from record pointing to object unbuffered fetch is performed from. - - SYNOPSIS - stmt_read_row_unbuffered() - stmt statement handle - row pointer to write pointer to row data; - - RETURN VALUE - 0 - success; *row contains valid address of a row; - row data is stored in network buffer - 1 - error; error code is written to - stmt->last_{errno,error}; *row is not changed - MYSQL_NO_DATA - end of file was read from network; - *row is set to NULL -*/ - -static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row) -{ - int rc= 1; - MYSQL *mysql= stmt->mysql; - /* - This function won't be called if stmt->field_count is zero - or execution wasn't done: this is ensured by mysql_stmt_execute. - */ - if (!mysql) - { - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); - return 1; - } - if (mysql->status != MYSQL_STATUS_STATEMENT_GET_RESULT) - { - set_stmt_error(stmt, stmt->unbuffered_fetch_cancelled ? - CR_FETCH_CANCELED : CR_COMMANDS_OUT_OF_SYNC, - unknown_sqlstate, NULL); - goto error; - } - if ((*mysql->methods->unbuffered_fetch)(mysql, (char**) row)) - { - set_stmt_errmsg(stmt, &mysql->net); - /* - If there was an error, there are no more pending rows: - reset statement status to not hang up in following - mysql_stmt_close (it will try to flush result set before - closing the statement). - */ - mysql->status= MYSQL_STATUS_READY; - goto error; - } - if (!*row) - { - mysql->status= MYSQL_STATUS_READY; - rc= MYSQL_NO_DATA; - goto error; - } - return 0; -error: - if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) - mysql->unbuffered_fetch_owner= 0; - return rc; -} - - -/* - Fetch statement row using server side cursor. - - SYNOPSIS - stmt_read_row_from_cursor() - - RETURN VALUE - 0 success - 1 error - MYSQL_NO_DATA end of data -*/ - -static int -stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row) -{ - if (stmt->data_cursor) - return stmt_read_row_buffered(stmt, row); - if (stmt->server_status & SERVER_STATUS_LAST_ROW_SENT) - stmt->server_status &= ~SERVER_STATUS_LAST_ROW_SENT; - else - { - MYSQL *mysql= stmt->mysql; - NET *net= &mysql->net; - MYSQL_DATA *result= &stmt->result; - uchar buff[4 /* statement id */ + - 4 /* number of rows to fetch */]; - - free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); - result->data= NULL; - result->rows= 0; - /* Send row request to the server */ - int4store(buff, stmt->stmt_id); - int4store(buff + 4, stmt->prefetch_rows); /* number of rows to fetch */ - if ((*mysql->methods->advanced_command)(mysql, COM_STMT_FETCH, - buff, sizeof(buff), (uchar*) 0, 0, - 1, stmt)) - { - /* - Don't set stmt error if stmt->mysql is NULL, as the error in this case - has already been set by mysql_prune_stmt_list(). - */ - if (stmt->mysql) - set_stmt_errmsg(stmt, net); - return 1; - } - if ((*mysql->methods->read_rows_from_cursor)(stmt)) - return 1; - stmt->server_status= mysql->server_status; - - stmt->data_cursor= result->data; - return stmt_read_row_buffered(stmt, row); - } - *row= 0; - return MYSQL_NO_DATA; -} - - -/* - Default read row function to not SIGSEGV in client in - case of wrong sequence of API calls. -*/ - -static int -stmt_read_row_no_data(MYSQL_STMT *stmt MY_ATTRIBUTE((unused)), - unsigned char **row MY_ATTRIBUTE((unused))) -{ - return MYSQL_NO_DATA; -} - -static int -stmt_read_row_no_result_set(MYSQL_STMT *stmt MY_ATTRIBUTE((unused)), - unsigned char **row MY_ATTRIBUTE((unused))) -{ - set_stmt_error(stmt, CR_NO_RESULT_SET, unknown_sqlstate, NULL); - return 1; -} - - -/* - Get/set statement attributes - - SYNOPSIS - mysql_stmt_attr_get() - mysql_stmt_attr_set() - - attr_type statement attribute - value casted to const void * pointer to value. - - RETURN VALUE - 0 success - !0 wrong attribute type -*/ - -my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, - enum enum_stmt_attr_type attr_type, - const void *value) -{ - switch (attr_type) { - case STMT_ATTR_UPDATE_MAX_LENGTH: - stmt->update_max_length= value ? *(const my_bool*) value : 0; - break; - case STMT_ATTR_CURSOR_TYPE: - { - ulong cursor_type; - cursor_type= value ? *(ulong*) value : 0UL; - if (cursor_type > (ulong) CURSOR_TYPE_READ_ONLY) - goto err_not_implemented; - stmt->flags= cursor_type; - break; - } - case STMT_ATTR_PREFETCH_ROWS: - { - ulong prefetch_rows= value ? *(ulong*) value : DEFAULT_PREFETCH_ROWS; - if (value == 0) - return TRUE; - stmt->prefetch_rows= prefetch_rows; - break; - } - default: - goto err_not_implemented; - } - return FALSE; -err_not_implemented: - set_stmt_error(stmt, CR_NOT_IMPLEMENTED, unknown_sqlstate, NULL); - return TRUE; -} - - -my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, - enum enum_stmt_attr_type attr_type, - void *value) -{ - switch (attr_type) { - case STMT_ATTR_UPDATE_MAX_LENGTH: - *(my_bool*) value= stmt->update_max_length; - break; - case STMT_ATTR_CURSOR_TYPE: - *(ulong*) value= stmt->flags; - break; - case STMT_ATTR_PREFETCH_ROWS: - *(ulong*) value= stmt->prefetch_rows; - break; - default: - return TRUE; - } - return FALSE; -} - - -/** - Update statement result set metadata from with the new field - information sent during statement execute. - - @pre mysql->field_count is not zero - - @retval TRUE if error: out of memory or the new - result set has a different number of columns - @retval FALSE success -*/ - -static void reinit_result_set_metadata(MYSQL_STMT *stmt) -{ - /* Server has sent result set metadata */ - if (stmt->field_count == 0) - { - /* - This is 'SHOW'/'EXPLAIN'-like query. Current implementation of - prepared statements can't send result set metadata for these queries - on prepare stage. Read it now. - */ - - stmt->field_count= stmt->mysql->field_count; - - alloc_stmt_fields(stmt); - } - else - { - /* - Update result set metadata if it for some reason changed between - prepare and execute, i.e.: - - in case of 'SELECT ?' we don't know column type unless data was - supplied to mysql_stmt_execute, so updated column type is sent - now. - - if data dictionary changed between prepare and execute, for - example a table used in the query was altered. - Note, that now (4.1.3) we always send metadata in reply to - COM_STMT_EXECUTE (even if it is not necessary), so either this or - previous branch always works. - TODO: send metadata only when it's really necessary and add a warning - 'Metadata changed' when it's sent twice. - */ - update_stmt_fields(stmt); - } -} - - -static void prepare_to_fetch_result(MYSQL_STMT *stmt) -{ - if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) - { - stmt->mysql->status= MYSQL_STATUS_READY; - stmt->read_row_func= stmt_read_row_from_cursor; - } - else if (stmt->flags & CURSOR_TYPE_READ_ONLY) - { - /* - This is a single-row result set, a result set with no rows, EXPLAIN, - SHOW VARIABLES, or some other command which either a) bypasses the - cursors framework in the server and writes rows directly to the - network or b) is more efficient if all (few) result set rows are - precached on client and server's resources are freed. - */ - mysql_stmt_store_result(stmt); - } - else - { - stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; - stmt->unbuffered_fetch_cancelled= FALSE; - stmt->read_row_func= stmt_read_row_unbuffered; - } -} - - -/* - Send placeholders data to server (if there are placeholders) - and execute prepared statement. - - SYNOPSIS - mysql_stmt_execute() - stmt statement handle. The handle must be created - with mysql_stmt_init() and prepared with - mysql_stmt_prepare(). If there are placeholders - in the statement they must be bound to local - variables with mysql_stmt_bind_param(). - - DESCRIPTION - This function will automatically flush pending result - set (if there is one), send parameters data to the server - and read result of statement execution. - If previous result set was cached with mysql_stmt_store_result() - it will also be freed in the beginning of this call. - The server can return 3 types of responses to this command: - - error, can be retrieved with mysql_stmt_error() - - ok, no result set pending. In this case we just update - stmt->insert_id and stmt->affected_rows. - - the query returns a result set: there could be 0 .. N - rows in it. In this case the server can also send updated - result set metadata. - - Next steps you may want to make: - - find out if there is result set with mysql_stmt_field_count(). - If there is one: - - optionally, cache entire result set on client to unblock - connection with mysql_stmt_store_result() - - bind client variables to result set columns and start read rows - with mysql_stmt_fetch(). - - reset statement with mysql_stmt_reset() or close it with - mysql_stmt_close() - Otherwise: - - find out last insert id and number of affected rows with - mysql_stmt_insert_id(), mysql_stmt_affected_rows() - - RETURN - 0 success - 1 error, message can be retrieved with mysql_stmt_error(). -*/ - -int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) -{ - MYSQL *mysql= stmt->mysql; - DBUG_ENTER("mysql_stmt_execute"); - - if (!mysql) - { - /* Error is already set in mysql_detatch_stmt_list */ - DBUG_RETURN(1); - } - - if (reset_stmt_handle(stmt, RESET_STORE_RESULT | RESET_CLEAR_ERROR)) - DBUG_RETURN(1); - /* - No need to check for stmt->state: if the statement wasn't - prepared we'll get 'unknown statement handler' error from server. - */ - if (mysql->methods->stmt_execute(stmt)) - DBUG_RETURN(1); - stmt->state= MYSQL_STMT_EXECUTE_DONE; - if (mysql->field_count) - { - reinit_result_set_metadata(stmt); - prepare_to_fetch_result(stmt); - } - DBUG_RETURN(MY_TEST(stmt->last_errno)); -} - - -/* - Return total parameters count in the statement -*/ - -ulong STDCALL mysql_stmt_param_count(MYSQL_STMT * stmt) -{ - DBUG_ENTER("mysql_stmt_param_count"); - DBUG_RETURN(stmt->param_count); -} - -/* - Return total affected rows from the last statement -*/ - -my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt) -{ - return stmt->affected_rows; -} - - -/* - Returns the number of result columns for the most recent query - run on this statement. -*/ - -unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt) -{ - return stmt->field_count; -} - -/* - Return last inserted id for auto_increment columns. - - SYNOPSIS - mysql_stmt_insert_id() - stmt statement handle - - DESCRIPTION - Current implementation of this call has a caveat: stmt->insert_id is - unconditionally updated from mysql->insert_id in the end of each - mysql_stmt_execute(). This works OK if mysql->insert_id contains new - value (sent in reply to mysql_stmt_execute()), otherwise stmt->insert_id - value gets undefined, as it's updated from some arbitrary value saved in - connection structure during some other call. -*/ - -my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) -{ - return stmt->insert_id; -} - - -static my_bool int_is_null_true= 1; /* Used for MYSQL_TYPE_NULL */ -static my_bool int_is_null_false= 0; - - -/* - Set up input data buffers for a statement. - - SYNOPSIS - mysql_stmt_bind_param() - stmt statement handle - The statement must be prepared with mysql_stmt_prepare(). - my_bind Array of mysql_stmt_param_count() bind parameters. - This function doesn't check that size of this argument - is >= mysql_stmt_field_count(): it's user's responsibility. - - DESCRIPTION - Use this call after mysql_stmt_prepare() to bind user variables to - placeholders. - Each element of bind array stands for a placeholder. Placeholders - are counted from 0. For example statement - 'INSERT INTO t (a, b) VALUES (?, ?)' - contains two placeholders, and for such statement you should supply - bind array of two elements (MYSQL_BIND bind[2]). - - By properly initializing bind array you can bind virtually any - C language type to statement's placeholders: - First, it's strongly recommended to always zero-initialize entire - bind structure before setting its members. This will both shorten - your application code and make it robust to future extensions of - MYSQL_BIND structure. - Then you need to assign typecode of your application buffer to - MYSQL_BIND::buffer_type. The following typecodes with their - correspondence to C language types are supported: - MYSQL_TYPE_TINY for 8-bit integer variables. Normally it's - 'signed char' and 'unsigned char'; - MYSQL_TYPE_SHORT for 16-bit signed and unsigned variables. This - is usually 'short' and 'unsigned short'; - MYSQL_TYPE_LONG for 32-bit signed and unsigned variables. It - corresponds to 'int' and 'unsigned int' on - vast majority of platforms. On IA-32 and some - other 32-bit systems you can also use 'long' - here; - MYSQL_TYPE_LONGLONG 64-bit signed or unsigned integer. Stands for - '[unsigned] long long' on most platforms; - MYSQL_TYPE_FLOAT 32-bit floating point type, 'float' on most - systems; - MYSQL_TYPE_DOUBLE 64-bit floating point type, 'double' on most - systems; - MYSQL_TYPE_TIME broken-down time stored in MYSQL_TIME - structure - MYSQL_TYPE_DATE date stored in MYSQL_TIME structure - MYSQL_TYPE_DATETIME datetime stored in MYSQL_TIME structure See - more on how to use these types for sending - dates and times below; - MYSQL_TYPE_STRING character string, assumed to be in - character-set-client. If character set of - client is not equal to character set of - column, value for this placeholder will be - converted to destination character set before - insert. - MYSQL_TYPE_BLOB sequence of bytes. This sequence is assumed to - be in binary character set (which is the same - as no particular character set), and is never - converted to any other character set. See also - notes about supplying string/blob length - below. - MYSQL_TYPE_NULL special typecode for binding nulls. - These C/C++ types are not supported yet by the API: long double, - bool. - - As you can see from the list above, it's responsibility of - application programmer to ensure that chosen typecode properly - corresponds to host language type. For example on all platforms - where we build MySQL packages (as of MySQL 4.1.4) int is a 32-bit - type. So for int you can always assume that proper typecode is - MYSQL_TYPE_LONG (however queer it sounds, the name is legacy of the - old MySQL API). In contrary sizeof(long) can be 4 or 8 8-bit bytes, - depending on platform. - - TODO: provide client typedefs for each integer and floating point - typecode, i. e. int8, uint8, float32, etc. - - Once typecode was set, it's necessary to assign MYSQL_BIND::buffer - to point to the buffer of given type. Finally, additional actions - may be taken for some types or use cases: - - Binding integer types. - For integer types you might also need to set MYSQL_BIND::is_unsigned - member. Set it to TRUE when binding unsigned char, unsigned short, - unsigned int, unsigned long, unsigned long long. - - Binding floating point types. - For floating point types you just need to set - MYSQL_BIND::buffer_type and MYSQL_BIND::buffer. The rest of the - members should be zero-initialized. - - Binding NULLs. - You might have a column always NULL, never NULL, or sometimes - NULL. For an always NULL column set MYSQL_BIND::buffer_type to - MYSQL_TYPE_NULL. The rest of the members just need to be - zero-initialized. For never NULL columns set - MYSQL_BIND::is_null to 0, or this has already been done if you - zero-initialized the entire structure. If you set - MYSQL_TYPE::is_null to point to an application buffer of type - 'my_bool', then this buffer will be checked on each execution: - this way you can set the buffer to TRUE, or any non-0 value for - NULLs, and to FALSE or 0 for not NULL data. - - Binding text strings and sequences of bytes. - For strings, in addition to MYSQL_BIND::buffer_type and - MYSQL_BIND::buffer you need to set MYSQL_BIND::length or - MYSQL_BIND::buffer_length. If 'length' is set, 'buffer_length' - is ignored. 'buffer_length' member should be used when size of - string doesn't change between executions. If you want to vary - buffer length for each value, set 'length' to point to an - application buffer of type 'unsigned long' and set this long to - length of the string before each mysql_stmt_execute(). - - Binding dates and times. - For binding dates and times prepared statements API provides - clients with MYSQL_TIME structure. A pointer to instance of this - structure should be assigned to MYSQL_BIND::buffer whenever - MYSQL_TYPE_TIME, MYSQL_TYPE_DATE, MYSQL_TYPE_DATETIME typecodes - are used. When typecode is MYSQL_TYPE_TIME, only members - 'hour', 'minute', 'second' and 'neg' (is time offset negative) - are used. These members only will be sent to the server. - MYSQL_TYPE_DATE implies use of 'year', 'month', 'day', 'neg'. - MYSQL_TYPE_DATETIME utilizes both parts of MYSQL_TIME structure. - You don't have to set MYSQL_TIME::time_type member: it's not - used when sending data to the server, typecode information is - enough. 'second_part' member can hold microsecond precision of - time value, but now it's only supported on protocol level: you - can't store microsecond in a column, or use in temporal - calculations. However, if you send a time value with microsecond - part for 'SELECT ?', statement, you'll get it back unchanged - from the server. - - Data conversion. - If conversion from host language type to data representation, - corresponding to SQL type, is required it's done on the server. - Data truncation is possible when conversion is lossy. For - example, if you supply MYSQL_TYPE_DATETIME value out of valid - SQL type TIMESTAMP range, the same conversion will be applied as - if this value would have been sent as string in the old - protocol. TODO: document how the server will behave in case of - truncation/data loss. - - After variables were bound, you can repeatedly set/change their - values and mysql_stmt_execute() the statement. - - See also: mysql_stmt_send_long_data() for sending long text/blob - data in pieces, examples in tests/mysql_client_test.c. - Next steps you might want to make: - - execute statement with mysql_stmt_execute(), - - reset statement using mysql_stmt_reset() or reprepare it with - another query using mysql_stmt_prepare() - - close statement with mysql_stmt_close(). - - IMPLEMENTATION - The function copies given bind array to internal storage of the - statement, and sets up typecode-specific handlers to perform - serialization of bound data. This means that although you don't need - to call this routine after each assignment to bind buffers, you - need to call it each time you change parameter typecodes, or other - members of MYSQL_BIND array. - This is a pure local call. Data types of client buffers are sent - along with buffers' data at first execution of the statement. - - RETURN - 0 success - 1 error, can be retrieved with mysql_stmt_error. -*/ - -my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *my_bind) -{ - uint count=0; - MYSQL_BIND *param, *end; - DBUG_ENTER("mysql_stmt_bind_param"); - - if (!stmt->param_count) - { - if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) - { - set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - DBUG_RETURN(0); - } - - /* Allocated on prepare */ - memcpy((char*) stmt->params, (char*) my_bind, - sizeof(MYSQL_BIND) * stmt->param_count); - - for (param= stmt->params, end= param+stmt->param_count; - param < end ; - param++) - { - param->param_number= count++; - param->long_data_used= 0; - - /* If param->is_null is not set, then the value can never be NULL */ - if (!param->is_null) - param->is_null= &int_is_null_false; - - /* Setup data copy functions for the different supported types */ - switch (param->buffer_type) { - case MYSQL_TYPE_NULL: - param->is_null= &int_is_null_true; - break; - case MYSQL_TYPE_TINY: - /* Force param->length as this is fixed for this type */ - param->length= ¶m->buffer_length; - param->buffer_length= 1; - param->store_param_func= store_param_tinyint; - break; - case MYSQL_TYPE_SHORT: - param->length= ¶m->buffer_length; - param->buffer_length= 2; - param->store_param_func= store_param_short; - break; - case MYSQL_TYPE_LONG: - param->length= ¶m->buffer_length; - param->buffer_length= 4; - param->store_param_func= store_param_int32; - break; - case MYSQL_TYPE_LONGLONG: - param->length= ¶m->buffer_length; - param->buffer_length= 8; - param->store_param_func= store_param_int64; - break; - case MYSQL_TYPE_FLOAT: - param->length= ¶m->buffer_length; - param->buffer_length= 4; - param->store_param_func= store_param_float; - break; - case MYSQL_TYPE_DOUBLE: - param->length= ¶m->buffer_length; - param->buffer_length= 8; - param->store_param_func= store_param_double; - break; - case MYSQL_TYPE_TIME: - param->store_param_func= store_param_time; - param->buffer_length= MAX_TIME_REP_LENGTH; - break; - case MYSQL_TYPE_DATE: - param->store_param_func= store_param_date; - param->buffer_length= MAX_DATE_REP_LENGTH; - break; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - param->store_param_func= store_param_datetime; - param->buffer_length= MAX_DATETIME_REP_LENGTH; - break; - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - case MYSQL_TYPE_JSON: - param->store_param_func= store_param_str; - /* - For variable length types user must set either length or - buffer_length. - */ - break; - default: - my_stpcpy(stmt->sqlstate, unknown_sqlstate); - sprintf(stmt->last_error, - ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE), - param->buffer_type, count); - DBUG_RETURN(1); - } - /* - If param->length is not given, change it to point to buffer_length. - This way we can always use *param->length to get the length of data - */ - if (!param->length) - param->length= ¶m->buffer_length; - } - /* We have to send/resend type information to MySQL */ - stmt->send_types_to_server= TRUE; - stmt->bind_param_done= TRUE; - DBUG_RETURN(0); -} - - -/******************************************************************** - Long data implementation -*********************************************************************/ - -/* - Send long data in pieces to the server - - SYNOPSIS - mysql_stmt_send_long_data() - stmt Statement handler - param_number Parameter number (0 - N-1) - data Data to send to server - length Length of data to send (may be 0) - - DESCRIPTION - This call can be used repeatedly to send long data in pieces - for any string/binary placeholder. Data supplied for - a placeholder is saved at server side till execute, and then - used instead of value from MYSQL_BIND object. More precisely, - if long data for a parameter was supplied, MYSQL_BIND object - corresponding to this parameter is not sent to server. In the - end of execution long data states of placeholders are reset, - so next time values of such placeholders will be taken again - from MYSQL_BIND array. - The server does not reply to this call: if there was an error - in data handling (which now only can happen if server run out - of memory) it would be returned in reply to - mysql_stmt_execute(). - You should choose type of long data carefully if you care - about character set conversions performed by server when the - statement is executed. No conversion is performed at all for - MYSQL_TYPE_BLOB and other binary typecodes. For - MYSQL_TYPE_STRING and the rest of text placeholders data is - converted from client character set to character set of - connection. If these character sets are different, this - conversion may require additional memory at server, equal to - total size of supplied pieces. - - RETURN VALUES - 0 ok - 1 error -*/ - -my_bool STDCALL -mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, - const char *data, ulong length) -{ - MYSQL_BIND *param; - DBUG_ENTER("mysql_stmt_send_long_data"); - DBUG_ASSERT(stmt != 0); - DBUG_PRINT("enter",("param no: %d data: 0x%lx, length : %ld", - param_number, (long) data, length)); - - /* - We only need to check for stmt->param_count, if it's not null - prepare was done. - */ - if (param_number >= stmt->param_count) - { - set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - param= stmt->params+param_number; - if (!IS_LONGDATA(param->buffer_type)) - { - /* Long data handling should be used only for string/binary types */ - my_stpcpy(stmt->sqlstate, unknown_sqlstate); - sprintf(stmt->last_error, ER(stmt->last_errno= CR_INVALID_BUFFER_USE), - param->param_number); - DBUG_RETURN(1); - } - - /* - Send long data packet if there is data or we're sending long data - for the first time. - */ - if (length || param->long_data_used == 0) - { - MYSQL *mysql= stmt->mysql; - /* Packet header: stmt id (4 bytes), param no (2 bytes) */ - uchar buff[MYSQL_LONG_DATA_HEADER]; - - int4store(buff, stmt->stmt_id); - int2store(buff + 4, param_number); - param->long_data_used= 1; - - /* - Note that we don't get any ok packet from the server in this case - This is intentional to save bandwidth. - */ - if ((*mysql->methods->advanced_command)(mysql, COM_STMT_SEND_LONG_DATA, - buff, sizeof(buff), (uchar*) data, - length, 1, stmt)) - { - /* - Don't set stmt error if stmt->mysql is NULL, as the error in this case - has already been set by mysql_prune_stmt_list(). - */ - if (stmt->mysql) - set_stmt_errmsg(stmt, &mysql->net); - DBUG_RETURN(1); - } - } - DBUG_RETURN(0); -} - - -/******************************************************************** - Fetch and conversion of result set rows (binary protocol). -*********************************************************************/ - -/* - Read date, (time, datetime) value from network buffer and store it - in MYSQL_TIME structure. - - SYNOPSIS - read_binary_{date,time,datetime}() - tm MYSQL_TIME structure to fill - pos pointer to current position in network buffer. - These functions increase pos to point to the beginning of the - next column. - - Auxiliary functions to read time (date, datetime) values from network - buffer and store in MYSQL_TIME structure. Jointly used by conversion - and no-conversion fetching. -*/ - -static void read_binary_time(MYSQL_TIME *tm, uchar **pos) -{ - /* net_field_length will set pos to the first byte of data */ - uint length= net_field_length(pos); - - if (length) - { - uchar *to= *pos; - tm->neg= to[0]; - - tm->day= (ulong) sint4korr(to+1); - tm->hour= (uint) to[5]; - tm->minute= (uint) to[6]; - tm->second= (uint) to[7]; - tm->second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0; - tm->year= tm->month= 0; - if (tm->day) - { - /* Convert days to hours at once */ - tm->hour+= tm->day*24; - tm->day= 0; - } - tm->time_type= MYSQL_TIMESTAMP_TIME; - - *pos+= length; - } - else - set_zero_time(tm, MYSQL_TIMESTAMP_TIME); -} - -static void read_binary_datetime(MYSQL_TIME *tm, uchar **pos) -{ - uint length= net_field_length(pos); - - if (length) - { - uchar *to= *pos; - - tm->neg= 0; - tm->year= (uint) sint2korr(to); - tm->month= (uint) to[2]; - tm->day= (uint) to[3]; - - if (length > 4) - { - tm->hour= (uint) to[4]; - tm->minute= (uint) to[5]; - tm->second= (uint) to[6]; - } - else - tm->hour= tm->minute= tm->second= 0; - tm->second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0; - tm->time_type= MYSQL_TIMESTAMP_DATETIME; - - *pos+= length; - } - else - set_zero_time(tm, MYSQL_TIMESTAMP_DATETIME); -} - -static void read_binary_date(MYSQL_TIME *tm, uchar **pos) -{ - uint length= net_field_length(pos); - - if (length) - { - uchar *to= *pos; - tm->year = (uint) sint2korr(to); - tm->month= (uint) to[2]; - tm->day= (uint) to[3]; - - tm->hour= tm->minute= tm->second= 0; - tm->second_part= 0; - tm->neg= 0; - tm->time_type= MYSQL_TIMESTAMP_DATE; - - *pos+= length; - } - else - set_zero_time(tm, MYSQL_TIMESTAMP_DATE); -} - - -/* - Convert string to supplied buffer of any type. - - SYNOPSIS - fetch_string_with_conversion() - param output buffer descriptor - value column data - length data length -*/ - -static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, - size_t length) -{ - uchar *buffer= param->buffer; - char *endptr= value + length; - - /* - This function should support all target buffer types: the rest - of conversion functions can delegate conversion to it. - */ - switch (param->buffer_type) { - case MYSQL_TYPE_NULL: /* do nothing */ - break; - case MYSQL_TYPE_TINY: - { - int err; - longlong data= my_strtoll10(value, &endptr, &err); - *param->error= (IS_TRUNCATED(data, param->is_unsigned, - INT_MIN8, INT_MAX8, UINT_MAX8) || err > 0); - *buffer= (uchar) data; - break; - } - case MYSQL_TYPE_SHORT: - { - int err; - longlong data= my_strtoll10(value, &endptr, &err); - *param->error= (IS_TRUNCATED(data, param->is_unsigned, - INT_MIN16, INT_MAX16, UINT_MAX16) || err > 0); - shortstore(buffer, (short) data); - break; - } - case MYSQL_TYPE_LONG: - { - int err; - longlong data= my_strtoll10(value, &endptr, &err); - *param->error= (IS_TRUNCATED(data, param->is_unsigned, - INT_MIN32, INT_MAX32, UINT_MAX32) || err > 0); - longstore(buffer, (int32) data); - break; - } - case MYSQL_TYPE_LONGLONG: - { - int err; - longlong data= my_strtoll10(value, &endptr, &err); - *param->error= param->is_unsigned ? err != 0 : - (err > 0 || (err == 0 && data < 0)); - longlongstore(buffer, data); - break; - } - case MYSQL_TYPE_FLOAT: - { - int err; - double data= my_strntod(&my_charset_latin1, value, length, &endptr, &err); - float fdata= (float) data; - *param->error= (fdata != data) | MY_TEST(err); - floatstore(buffer, fdata); - break; - } - case MYSQL_TYPE_DOUBLE: - { - int err; - double data= my_strntod(&my_charset_latin1, value, length, &endptr, &err); - *param->error= MY_TEST(err); - doublestore(buffer, data); - break; - } - case MYSQL_TYPE_TIME: - { - MYSQL_TIME_STATUS status; - MYSQL_TIME *tm= (MYSQL_TIME *)buffer; - str_to_time(value, length, tm, &status); - *param->error= MY_TEST(status.warnings); - break; - } - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - { - MYSQL_TIME_STATUS status; - MYSQL_TIME *tm= (MYSQL_TIME *)buffer; - (void) str_to_datetime(value, length, tm, TIME_FUZZY_DATE, &status); - *param->error= MY_TEST(status.warnings) && - (param->buffer_type == MYSQL_TYPE_DATE && - tm->time_type != MYSQL_TIMESTAMP_DATE); - break; - } - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - default: - { - /* - Copy column data to the buffer taking into account offset, - data length and buffer length. - */ - char *start= value + param->offset; - char *end= value + length; - size_t copy_length; - if (start < end) - { - copy_length= end - start; - /* We've got some data beyond offset: copy up to buffer_length bytes */ - if (param->buffer_length) - memcpy(buffer, start, MY_MIN(copy_length, param->buffer_length)); - } - else - copy_length= 0; - if (copy_length < param->buffer_length) - buffer[copy_length]= '\0'; - *param->error= copy_length > param->buffer_length; - /* - param->length will always contain length of entire column; - number of copied bytes may be way different: - */ - *param->length= (unsigned long)length; - break; - } - } -} - - -/* - Convert integer value to client buffer of any type. - - SYNOPSIS - fetch_long_with_conversion() - param output buffer descriptor - field column metadata - value column data -*/ - -static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, - longlong value, my_bool is_unsigned) -{ - uchar *buffer= param->buffer; - - switch (param->buffer_type) { - case MYSQL_TYPE_NULL: /* do nothing */ - break; - case MYSQL_TYPE_TINY: - *param->error= IS_TRUNCATED(value, param->is_unsigned, - INT_MIN8, INT_MAX8, UINT_MAX8); - *(uchar *)param->buffer= (uchar) value; - break; - case MYSQL_TYPE_SHORT: - *param->error= IS_TRUNCATED(value, param->is_unsigned, - INT_MIN16, INT_MAX16, UINT_MAX16); - shortstore(buffer, (short) value); - break; - case MYSQL_TYPE_LONG: - *param->error= IS_TRUNCATED(value, param->is_unsigned, - INT_MIN32, INT_MAX32, UINT_MAX32); - longstore(buffer, (int32) value); - break; - case MYSQL_TYPE_LONGLONG: - longlongstore(buffer, value); - *param->error= param->is_unsigned != is_unsigned && value < 0; - break; - case MYSQL_TYPE_FLOAT: - { - /* - We need to mark the local variable volatile to - workaround Intel FPU executive precision feature. - (See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 for details) - */ - volatile float data; - if (is_unsigned) - { - data= (float) ulonglong2double(value); - *param->error= ((ulonglong) value) != ((ulonglong) data); - } - else - { - data= (float)value; - *param->error= value != ((longlong) data); - } - floatstore(buffer, data); - break; - } - case MYSQL_TYPE_DOUBLE: - { - volatile double data; - if (is_unsigned) - { - data= ulonglong2double(value); - *param->error= ((ulonglong) value) != ((ulonglong) data); - } - else - { - data= (double)value; - *param->error= value != ((longlong) data); - } - doublestore(buffer, data); - break; - } - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATETIME: - { - int error; - value= number_to_datetime(value, (MYSQL_TIME *) buffer, TIME_FUZZY_DATE, - &error); - *param->error= MY_TEST(error); - break; - } - default: - { - uchar buff[22]; /* Enough for longlong */ - uchar *end= (uchar*) longlong10_to_str(value, (char*) buff, - is_unsigned ? 10: -10); - /* Resort to string conversion which supports all typecodes */ - uint length= (uint) (end-buff); - - if (field->flags & ZEROFILL_FLAG && length < field->length && - field->length < 21) - { - memmove(buff + field->length - length, buff, length); - memset(buff, '0', field->length - length); - length= field->length; - } - fetch_string_with_conversion(param, (char*) buff, length); - break; - } - } -} - -/* - Convert double/float column to supplied buffer of any type. - - SYNOPSIS - fetch_float_with_conversion() - param output buffer descriptor - field column metadata - value column data - type either MY_GCVT_ARG_FLOAT or MY_GCVT_ARG_DOUBLE. - Affects the maximum number of significant digits - returned by my_gcvt(). -*/ - -static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, - double value, my_gcvt_arg_type type) -{ - uchar *buffer= param->buffer; - double val64 = (value < 0 ? -floor(-value) : floor(value)); - - switch (param->buffer_type) { - case MYSQL_TYPE_NULL: /* do nothing */ - break; - case MYSQL_TYPE_TINY: - /* - We need to _store_ data in the buffer before the truncation check to - workaround Intel FPU executive precision feature. - (See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 for details) - Sic: AFAIU it does not guarantee to work. - */ - if (param->is_unsigned) - *buffer= (uint8) value; - else - *buffer= (int8) value; - *param->error= val64 != (param->is_unsigned ? (double)((uint8) *buffer) : - (double)((int8) *buffer)); - break; - case MYSQL_TYPE_SHORT: - if (param->is_unsigned) - { - ushort data= (ushort) value; - shortstore(buffer, data); - } - else - { - short data= (short) value; - shortstore(buffer, data); - } - *param->error= val64 != (param->is_unsigned ? (double) (*(ushort*) buffer): - (double) (*(short*) buffer)); - break; - case MYSQL_TYPE_LONG: - if (param->is_unsigned) - { - uint32 data= (uint32) value; - longstore(buffer, data); - } - else - { - int32 data= (int32) value; - longstore(buffer, data); - } - *param->error= val64 != (param->is_unsigned ? (double) (*(uint32*) buffer): - (double) (*(int32*) buffer)); - break; - case MYSQL_TYPE_LONGLONG: - if (param->is_unsigned) - { - ulonglong data= (ulonglong) value; - longlongstore(buffer, data); - } - else - { - longlong data= (longlong) value; - longlongstore(buffer, data); - } - *param->error= val64 != (param->is_unsigned ? - ulonglong2double(*(ulonglong*) buffer) : - (double) (*(longlong*) buffer)); - break; - case MYSQL_TYPE_FLOAT: - { - float data= (float) value; - floatstore(buffer, data); - *param->error= (*(float*) buffer) != value; - break; - } - case MYSQL_TYPE_DOUBLE: - { - doublestore(buffer, value); - break; - } - default: - { - /* - Resort to fetch_string_with_conversion: this should handle - floating point -> string conversion nicely, honor all typecodes - and param->offset possibly set in mysql_stmt_fetch_column - */ - char buff[FLOATING_POINT_BUFFER]; - size_t len; - if (field->decimals >= NOT_FIXED_DEC) - len= my_gcvt(value, type, - (int) MY_MIN(sizeof(buff)-1, param->buffer_length), - buff, NULL); - else - len= my_fcvt(value, (int) field->decimals, buff, NULL); - - if (field->flags & ZEROFILL_FLAG && len < field->length && - field->length < MAX_DOUBLE_STRING_REP_LENGTH - 1) - { - memmove(buff + field->length - len, buff, len); - memset(buff, '0', field->length - len); - len= field->length; - } - fetch_string_with_conversion(param, buff, len); - - break; - } - } -} - - -/* - Fetch time/date/datetime to supplied buffer of any type - - SYNOPSIS - param output buffer descriptor - time column data -*/ - -static void fetch_datetime_with_conversion(MYSQL_BIND *param, - MYSQL_FIELD *field, - MYSQL_TIME *my_time) -{ - switch (param->buffer_type) { - case MYSQL_TYPE_NULL: /* do nothing */ - break; - case MYSQL_TYPE_DATE: - *(MYSQL_TIME *)(param->buffer)= *my_time; - *param->error= my_time->time_type != MYSQL_TIMESTAMP_DATE; - break; - case MYSQL_TYPE_TIME: - *(MYSQL_TIME *)(param->buffer)= *my_time; - *param->error= my_time->time_type != MYSQL_TIMESTAMP_TIME; - break; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - *(MYSQL_TIME *)(param->buffer)= *my_time; - /* No error: time and date are compatible with datetime */ - break; - case MYSQL_TYPE_YEAR: - shortstore(param->buffer, my_time->year); - *param->error= 1; - break; - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - { - ulonglong value= TIME_to_ulonglong(my_time); - fetch_float_with_conversion(param, field, - ulonglong2double(value), MY_GCVT_ARG_DOUBLE); - break; - } - case MYSQL_TYPE_TINY: - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_LONGLONG: - { - longlong value= (longlong) TIME_to_ulonglong(my_time); - fetch_long_with_conversion(param, field, value, TRUE); - break; - } - default: - { - /* - Convert time value to string and delegate the rest to - fetch_string_with_conversion: - */ - char buff[MAX_DATE_STRING_REP_LENGTH]; - uint length= my_TIME_to_str(my_time, buff, field->decimals); - /* Resort to string conversion */ - fetch_string_with_conversion(param, (char *)buff, length); - break; - } - } -} - - -/* - Fetch and convert result set column to output buffer. - - SYNOPSIS - fetch_result_with_conversion() - param output buffer descriptor - field column metadata - row points to a column of result set tuple in binary format - - DESCRIPTION - This is a fallback implementation of column fetch used - if column and output buffer types do not match. - Increases tuple pointer to point at the next column within the - tuple. -*/ - -static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, - uchar **row) -{ - enum enum_field_types field_type= field->type; - uint field_is_unsigned= field->flags & UNSIGNED_FLAG; - - switch (field_type) { - case MYSQL_TYPE_TINY: - { - uchar value= **row; - /* sic: we need to cast to 'signed char' as 'char' may be unsigned */ - longlong data= field_is_unsigned ? (longlong) value : - (longlong) (signed char) value; - fetch_long_with_conversion(param, field, data, 0); - *row+= 1; - break; - } - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_YEAR: - { - short value= sint2korr(*row); - longlong data= field_is_unsigned ? (longlong) (unsigned short) value : - (longlong) value; - fetch_long_with_conversion(param, field, data, 0); - *row+= 2; - break; - } - case MYSQL_TYPE_INT24: /* mediumint is sent as 4 bytes int */ - case MYSQL_TYPE_LONG: - { - int32 value= sint4korr(*row); - longlong data= field_is_unsigned ? (longlong) (uint32) value : - (longlong) value; - fetch_long_with_conversion(param, field, data, 0); - *row+= 4; - break; - } - case MYSQL_TYPE_LONGLONG: - { - longlong value= (longlong)sint8korr(*row); - fetch_long_with_conversion(param, field, value, - field->flags & UNSIGNED_FLAG); - *row+= 8; - break; - } - case MYSQL_TYPE_FLOAT: - { - float value; - float4get(&value,*row); - fetch_float_with_conversion(param, field, value, MY_GCVT_ARG_FLOAT); - *row+= 4; - break; - } - case MYSQL_TYPE_DOUBLE: - { - double value; - float8get(&value,*row); - fetch_float_with_conversion(param, field, value, MY_GCVT_ARG_DOUBLE); - *row+= 8; - break; - } - case MYSQL_TYPE_DATE: - { - MYSQL_TIME tm; - - read_binary_date(&tm, row); - fetch_datetime_with_conversion(param, field, &tm); - break; - } - case MYSQL_TYPE_TIME: - { - MYSQL_TIME tm; - - read_binary_time(&tm, row); - fetch_datetime_with_conversion(param, field, &tm); - break; - } - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - { - MYSQL_TIME tm; - - read_binary_datetime(&tm, row); - fetch_datetime_with_conversion(param, field, &tm); - break; - } - default: - { - ulong length= net_field_length(row); - fetch_string_with_conversion(param, (char*) *row, length); - *row+= length; - break; - } - } -} - - -/* - Functions to fetch data to application buffers without conversion. - - All functions have the following characteristics: - - SYNOPSIS - fetch_result_xxx() - param MySQL bind param - pos Row value - - DESCRIPTION - These are no-conversion functions, used in binary protocol to store - rows in application buffers. A function used only if type of binary data - is compatible with type of application buffer. - - RETURN - none -*/ - -static void fetch_result_tinyint(MYSQL_BIND *param, MYSQL_FIELD *field, - uchar **row) -{ - my_bool field_is_unsigned= MY_TEST(field->flags & UNSIGNED_FLAG); - uchar data= **row; - *(uchar *)param->buffer= data; - *param->error= param->is_unsigned != field_is_unsigned && data > INT_MAX8; - (*row)++; -} - -static void fetch_result_short(MYSQL_BIND *param, MYSQL_FIELD *field, - uchar **row) -{ - my_bool field_is_unsigned= MY_TEST(field->flags & UNSIGNED_FLAG); - ushort data= (ushort) sint2korr(*row); - shortstore(param->buffer, data); - *param->error= param->is_unsigned != field_is_unsigned && data > INT_MAX16; - *row+= 2; -} - -static void fetch_result_int32(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - my_bool field_is_unsigned= MY_TEST(field->flags & UNSIGNED_FLAG); - uint32 data= (uint32) sint4korr(*row); - longstore(param->buffer, data); - *param->error= param->is_unsigned != field_is_unsigned && data > INT_MAX32; - *row+= 4; -} - -static void fetch_result_int64(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - my_bool field_is_unsigned= MY_TEST(field->flags & UNSIGNED_FLAG); - ulonglong data= (ulonglong) sint8korr(*row); - *param->error= param->is_unsigned != field_is_unsigned && data > LLONG_MAX; - longlongstore(param->buffer, data); - *row+= 8; -} - -static void fetch_result_float(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - float value; - float4get(&value,*row); - floatstore(param->buffer, value); - *row+= 4; -} - -static void fetch_result_double(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - double value; - float8get(&value,*row); - doublestore(param->buffer, value); - *row+= 8; -} - -static void fetch_result_time(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; - read_binary_time(tm, row); -} - -static void fetch_result_date(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; - read_binary_date(tm, row); -} - -static void fetch_result_datetime(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; - read_binary_datetime(tm, row); -} - -static void fetch_result_bin(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - ulong length= net_field_length(row); - ulong copy_length= MY_MIN(length, param->buffer_length); - memcpy(param->buffer, (char *)*row, copy_length); - *param->length= length; - *param->error= copy_length < length; - *row+= length; -} - -static void fetch_result_str(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) -{ - ulong length= net_field_length(row); - ulong copy_length= MY_MIN(length, param->buffer_length); - memcpy(param->buffer, (char *)*row, copy_length); - /* Add an end null if there is room in the buffer */ - if (copy_length != param->buffer_length) - ((uchar *)param->buffer)[copy_length]= '\0'; - *param->length= length; /* return total length */ - *param->error= copy_length < length; - *row+= length; -} - - -/* - functions to calculate max lengths for strings during - mysql_stmt_store_result() -*/ - -static void skip_result_fixed(MYSQL_BIND *param, - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) - -{ - (*row)+= param->pack_length; -} - - -static void skip_result_with_length(MYSQL_BIND *param MY_ATTRIBUTE((unused)), - MYSQL_FIELD *field MY_ATTRIBUTE((unused)), - uchar **row) - -{ - ulong length= net_field_length(row); - (*row)+= length; -} - - -static void skip_result_string(MYSQL_BIND *param MY_ATTRIBUTE((unused)), - MYSQL_FIELD *field, - uchar **row) - -{ - ulong length= net_field_length(row); - (*row)+= length; - if (field->max_length < length) - field->max_length= length; -} - - -/* - Check that two field types are binary compatible i. e. - have equal representation in the binary protocol and - require client-side buffers of the same type. - - SYNOPSIS - is_binary_compatible() - type1 parameter type supplied by user - type2 field type, obtained from result set metadata - - RETURN - TRUE or FALSE -*/ - -static my_bool is_binary_compatible(enum enum_field_types type1, - enum enum_field_types type2) -{ - static const enum enum_field_types - range1[]= { MYSQL_TYPE_SHORT, MYSQL_TYPE_YEAR, MYSQL_TYPE_NULL }, - range2[]= { MYSQL_TYPE_INT24, MYSQL_TYPE_LONG, MYSQL_TYPE_NULL }, - range3[]= { MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_NULL }, - range4[]= { MYSQL_TYPE_ENUM, MYSQL_TYPE_SET, MYSQL_TYPE_TINY_BLOB, - MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_BLOB, - MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY, - MYSQL_TYPE_DECIMAL, MYSQL_TYPE_NULL }; - static const enum enum_field_types - *range_list[]= { range1, range2, range3, range4 }, - **range_list_end= range_list + sizeof(range_list)/sizeof(*range_list); - const enum enum_field_types **range, *type; - - if (type1 == type2) - return TRUE; - for (range= range_list; range != range_list_end; ++range) - { - /* check that both type1 and type2 are in the same range */ - my_bool type1_found= FALSE, type2_found= FALSE; - for (type= *range; *type != MYSQL_TYPE_NULL; type++) - { - type1_found|= type1 == *type; - type2_found|= type2 == *type; - } - if (type1_found || type2_found) - return type1_found && type2_found; - } - return FALSE; -} - - -/* - Setup a fetch function for one column of a result set. - - SYNOPSIS - setup_one_fetch_function() - param output buffer descriptor - field column descriptor - - DESCRIPTION - When user binds result set buffers or when result set - metadata is changed, we need to setup fetch (and possibly - conversion) functions for all columns of the result set. - In addition to that here we set up skip_result function, used - to update result set metadata in case when - STMT_ATTR_UPDATE_MAX_LENGTH attribute is set. - Notice that while fetch_result is chosen depending on both - field->type and param->type, skip_result depends on field->type - only. - - RETURN - TRUE fetch function for this typecode was not found (typecode - is not supported by the client library) - FALSE success -*/ - -static my_bool setup_one_fetch_function(MYSQL_BIND *param, MYSQL_FIELD *field) -{ - DBUG_ENTER("setup_one_fetch_function"); - - /* Setup data copy functions for the different supported types */ - switch (param->buffer_type) { - case MYSQL_TYPE_NULL: /* for dummy binds */ - /* - It's not binary compatible with anything the server can return: - no need to setup fetch_result, as it'll be reset anyway - */ - *param->length= 0; - break; - case MYSQL_TYPE_TINY: - param->fetch_result= fetch_result_tinyint; - *param->length= 1; - break; - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_YEAR: - param->fetch_result= fetch_result_short; - *param->length= 2; - break; - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - param->fetch_result= fetch_result_int32; - *param->length= 4; - break; - case MYSQL_TYPE_LONGLONG: - param->fetch_result= fetch_result_int64; - *param->length= 8; - break; - case MYSQL_TYPE_FLOAT: - param->fetch_result= fetch_result_float; - *param->length= 4; - break; - case MYSQL_TYPE_DOUBLE: - param->fetch_result= fetch_result_double; - *param->length= 8; - break; - case MYSQL_TYPE_TIME: - param->fetch_result= fetch_result_time; - *param->length= sizeof(MYSQL_TIME); - break; - case MYSQL_TYPE_DATE: - param->fetch_result= fetch_result_date; - *param->length= sizeof(MYSQL_TIME); - break; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - param->fetch_result= fetch_result_datetime; - *param->length= sizeof(MYSQL_TIME); - break; - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_BIT: - DBUG_ASSERT(param->buffer_length != 0); - param->fetch_result= fetch_result_bin; - break; - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_JSON: - DBUG_ASSERT(param->buffer_length != 0); - param->fetch_result= fetch_result_str; - break; - default: - DBUG_PRINT("error", ("Unknown param->buffer_type: %u", - (uint) param->buffer_type)); - DBUG_RETURN(TRUE); - } - if (! is_binary_compatible(param->buffer_type, field->type)) - param->fetch_result= fetch_result_with_conversion; - - /* Setup skip_result functions (to calculate max_length) */ - param->skip_result= skip_result_fixed; - switch (field->type) { - case MYSQL_TYPE_NULL: /* for dummy binds */ - param->pack_length= 0; - field->max_length= 0; - break; - case MYSQL_TYPE_TINY: - param->pack_length= 1; - field->max_length= 4; /* as in '-127' */ - break; - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_SHORT: - param->pack_length= 2; - field->max_length= 6; /* as in '-32767' */ - break; - case MYSQL_TYPE_INT24: - field->max_length= 9; /* as in '16777216' or in '-8388607' */ - param->pack_length= 4; - break; - case MYSQL_TYPE_LONG: - field->max_length= 11; /* '-2147483647' */ - param->pack_length= 4; - break; - case MYSQL_TYPE_LONGLONG: - field->max_length= 21; /* '18446744073709551616' */ - param->pack_length= 8; - break; - case MYSQL_TYPE_FLOAT: - param->pack_length= 4; - field->max_length= MAX_DOUBLE_STRING_REP_LENGTH; - break; - case MYSQL_TYPE_DOUBLE: - param->pack_length= 8; - field->max_length= MAX_DOUBLE_STRING_REP_LENGTH; - break; - case MYSQL_TYPE_TIME: - field->max_length= 17; /* -819:23:48.123456 */ - param->skip_result= skip_result_with_length; - break; - case MYSQL_TYPE_DATE: - field->max_length= 10; /* 2003-11-11 */ - param->skip_result= skip_result_with_length; - break; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - param->skip_result= skip_result_with_length; - field->max_length= MAX_DATE_STRING_REP_LENGTH; - break; - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_GEOMETRY: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_BIT: - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_JSON: - param->skip_result= skip_result_string; - break; - default: - DBUG_PRINT("error", ("Unknown field->type: %u", (uint) field->type)); - DBUG_RETURN(TRUE); - } - DBUG_RETURN(FALSE); -} - - -/* - Setup the bind buffers for resultset processing -*/ - -my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *my_bind) -{ - MYSQL_BIND *param, *end; - MYSQL_FIELD *field; - ulong bind_count= stmt->field_count; - uint param_count= 0; - DBUG_ENTER("mysql_stmt_bind_result"); - DBUG_PRINT("enter",("field_count: %lu", bind_count)); - - if (!bind_count) - { - int errorcode= (int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE ? - CR_NO_PREPARE_STMT : CR_NO_STMT_METADATA; - set_stmt_error(stmt, errorcode, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - /* - We only need to check that stmt->field_count - if it is not null - stmt->bind was initialized in mysql_stmt_prepare - stmt->bind overlaps with bind if mysql_stmt_bind_param - is called from mysql_stmt_store_result. - BEWARE of buffer overwrite ... - */ - - if (stmt->bind != my_bind) - memcpy((char*) stmt->bind, (char*) my_bind, - sizeof(MYSQL_BIND) * bind_count); - - for (param= stmt->bind, end= param + bind_count, field= stmt->fields ; - param < end ; - param++, field++) - { - DBUG_PRINT("info",("buffer_type: %u field_type: %u", - (uint) param->buffer_type, (uint) field->type)); - /* - Set param->is_null to point to a dummy variable if it's not set. - This is to make the execute code easier - */ - if (!param->is_null) - param->is_null= ¶m->is_null_value; - - if (!param->length) - param->length= ¶m->length_value; - - if (!param->error) - param->error= ¶m->error_value; - - param->param_number= param_count++; - param->offset= 0; - - if (setup_one_fetch_function(param, field)) - { - my_stpcpy(stmt->sqlstate, unknown_sqlstate); - sprintf(stmt->last_error, - ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE), - field->type, param_count); - DBUG_RETURN(1); - } - } - stmt->bind_result_done= BIND_RESULT_DONE; - if (stmt->mysql->options.report_data_truncation) - stmt->bind_result_done|= REPORT_DATA_TRUNCATION; - - DBUG_RETURN(0); -} - - -/* - Fetch row data to bind buffers -*/ - -static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) -{ - MYSQL_BIND *my_bind, *end; - MYSQL_FIELD *field; - uchar *null_ptr, bit; - int truncation_count= 0; - /* - Precondition: if stmt->field_count is zero or row is NULL, read_row_* - function must return no data. - */ - DBUG_ASSERT(stmt->field_count); - DBUG_ASSERT(row); - - if (!stmt->bind_result_done) - { - /* If output parameters were not bound we should just return success */ - return 0; - } - - null_ptr= row; - row+= (stmt->field_count+9)/8; /* skip null bits */ - bit= 4; /* first 2 bits are reserved */ - - /* Copy complete row to application buffers */ - for (my_bind= stmt->bind, end= my_bind + stmt->field_count, - field= stmt->fields ; - my_bind < end ; - my_bind++, field++) - { - *my_bind->error= 0; - if (*null_ptr & bit) - { - /* - We should set both row_ptr and is_null to be able to see - nulls in mysql_stmt_fetch_column. This is because is_null may point - to user data which can be overwritten between mysql_stmt_fetch and - mysql_stmt_fetch_column, and in this case nullness of column will be - lost. See mysql_stmt_fetch_column for details. - */ - my_bind->row_ptr= NULL; - *my_bind->is_null= 1; - } - else - { - *my_bind->is_null= 0; - my_bind->row_ptr= row; - (*my_bind->fetch_result)(my_bind, field, &row); - truncation_count+= *my_bind->error; - } - if (!((bit<<=1) & 255)) - { - bit= 1; /* To next uchar */ - null_ptr++; - } - } - if (truncation_count && (stmt->bind_result_done & REPORT_DATA_TRUNCATION)) - return MYSQL_DATA_TRUNCATED; - return 0; -} - - -int cli_unbuffered_fetch(MYSQL *mysql, char **row) -{ - ulong len= 0; - my_bool is_data_packet; - if (packet_error == cli_safe_read(mysql, &is_data_packet)) - { - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); - return 1; - } - - if (mysql->net.read_pos[0] != 0 && !is_data_packet) - { - /* in case of new client read the OK packet */ - if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF) - read_ok_ex(mysql, len); - *row= NULL; - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); - } - else - { - *row= (char*) (mysql->net.read_pos + 1); - } - - return 0; -} - - -/* - Fetch and return row data to bound buffers, if any -*/ - -int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt) -{ - int rc; - uchar *row; - DBUG_ENTER("mysql_stmt_fetch"); - - if ((rc= (*stmt->read_row_func)(stmt, &row)) || - ((rc= stmt_fetch_row(stmt, row)) && rc != MYSQL_DATA_TRUNCATED)) - { - stmt->state= MYSQL_STMT_PREPARE_DONE; /* XXX: this is buggy */ - stmt->read_row_func= (rc == MYSQL_NO_DATA) ? - stmt_read_row_no_data : stmt_read_row_no_result_set; - } - else - { - /* This is to know in mysql_stmt_fetch_column that data was fetched */ - stmt->state= MYSQL_STMT_FETCH_DONE; - } - DBUG_RETURN(rc); -} - - -/* - Fetch data for one specified column data - - SYNOPSIS - mysql_stmt_fetch_column() - stmt Prepared statement handler - my_bind Where data should be placed. Should be filled in as - when calling mysql_stmt_bind_result() - column Column to fetch (first column is 0) - ulong offset Offset in result data (to fetch blob in pieces) - This is normally 0 - RETURN - 0 ok - 1 error -*/ - -int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *my_bind, - uint column, ulong offset) -{ - MYSQL_BIND *param= stmt->bind+column; - DBUG_ENTER("mysql_stmt_fetch_column"); - - if ((int) stmt->state < (int) MYSQL_STMT_FETCH_DONE) - { - set_stmt_error(stmt, CR_NO_DATA, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - if (column >= stmt->field_count) - { - set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - if (!my_bind->error) - my_bind->error= &my_bind->error_value; - *my_bind->error= 0; - if (param->row_ptr) - { - MYSQL_FIELD *field= stmt->fields+column; - uchar *row= param->row_ptr; - my_bind->offset= offset; - if (my_bind->is_null) - *my_bind->is_null= 0; - if (my_bind->length) /* Set the length if non char/binary types */ - *my_bind->length= *param->length; - else - my_bind->length= ¶m->length_value; /* Needed for fetch_result() */ - fetch_result_with_conversion(my_bind, field, &row); - } - else - { - if (my_bind->is_null) - *my_bind->is_null= 1; - } - DBUG_RETURN(0); -} - - -/* - Read all rows of data from server (binary format) -*/ - -int cli_read_binary_rows(MYSQL_STMT *stmt) -{ - ulong pkt_len; - uchar *cp; - MYSQL *mysql= stmt->mysql; - MYSQL_DATA *result= &stmt->result; - MYSQL_ROWS **prev_ptr= &result->data; - NET *net; - my_bool is_data_packet; - - DBUG_ENTER("cli_read_binary_rows"); - - if (!mysql) - { - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - net = &mysql->net; - /* - We could have read one row in execute() due to the lack of a cursor, - but one at most. - */ - DBUG_ASSERT(result->rows <= 1); - if (result->rows == 1) - prev_ptr= &result->data->next; - - while ((pkt_len= cli_safe_read(mysql, &is_data_packet)) != packet_error) - { - cp= net->read_pos; - if (*cp == 0 || is_data_packet) - { - if (add_binary_row(net, stmt, pkt_len, &prev_ptr)) - goto err; - } - else - { - /* end of data */ - *prev_ptr= 0; - /* read warning count from OK packet or EOF packet if it is old client */ - if (mysql->server_capabilities & CLIENT_DEPRECATE_EOF && - !is_data_packet) - read_ok_ex(mysql, pkt_len); - else - mysql->warning_count= uint2korr(cp + 1); - /* - OUT parameters result sets has SERVER_PS_OUT_PARAMS and - SERVER_MORE_RESULTS_EXISTS flags in first EOF_Packet only. - Last EOF_Packet of OUT parameters result sets have no - SERVER_MORE_RESULTS_EXISTS flag as described here: - http://dev.mysql.com/doc/internals/en/stored-procedures.html#out-parameter-set - Following code reads last EOF_Packet of result set and can clear - those flags in server_status if we don't preserve them. - Without SERVER_MORE_RESULTS_EXISTS flag mysql_stmt_next_result fails - to read OK_Packet after OUT parameters result set. - So we need to preserve SERVER_MORE_RESULTS_EXISTS flag for OUT - parameters result set. - */ - if (mysql->server_status & SERVER_PS_OUT_PARAMS) - { - mysql->server_status= uint2korr(cp+3) - | SERVER_PS_OUT_PARAMS - | (mysql->server_status & SERVER_MORE_RESULTS_EXISTS); - } - else - mysql->server_status= uint2korr(cp+3); - DBUG_PRINT("info",("status: %u warning_count: %u", - mysql->server_status, mysql->warning_count)); -#if defined(CLIENT_PROTOCOL_TRACING) - if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) - MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT); - else - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); -#endif - DBUG_RETURN(0); - } - } - set_stmt_errmsg(stmt, net); - -err: - DBUG_RETURN(1); -} - - -/* - Update meta data for statement - - SYNOPSIS - stmt_update_metadata() - stmt Statement handler - row Binary data - - NOTES - Only updates MYSQL_FIELD->max_length for strings -*/ - -static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data) -{ - MYSQL_BIND *my_bind, *end; - MYSQL_FIELD *field; - uchar *null_ptr, bit; - uchar *row= (uchar*) data->data; -#ifndef DBUG_OFF - uchar *row_end= row + data->length; -#endif - - null_ptr= row; - row+= (stmt->field_count+9)/8; /* skip null bits */ - bit= 4; /* first 2 bits are reserved */ - - /* Go through all fields and calculate metadata */ - for (my_bind= stmt->bind, end= my_bind + stmt->field_count, field= stmt->fields ; - my_bind < end ; - my_bind++, field++) - { - if (!(*null_ptr & bit)) - (*my_bind->skip_result)(my_bind, field, &row); - DBUG_ASSERT(row <= row_end); - if (!((bit<<=1) & 255)) - { - bit= 1; /* To next uchar */ - null_ptr++; - } - } -} - - -/* - Store or buffer the binary results to stmt -*/ - -int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) -{ - MYSQL *mysql= stmt->mysql; - MYSQL_DATA *result= &stmt->result; - DBUG_ENTER("mysql_stmt_store_result"); - - if (!mysql) - { - /* mysql can be reset in mysql_close called from mysql_reconnect */ - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - if (!stmt->field_count) - DBUG_RETURN(0); - - if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE) - { - set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - if (stmt->last_errno) - { - /* An attempt to use an invalid statement handle. */ - DBUG_RETURN(1); - } - - if (mysql->status == MYSQL_STATUS_READY && - stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) - { - /* - Server side cursor exist, tell server to start sending the rows - */ - NET *net= &mysql->net; - uchar buff[4 /* statement id */ + - 4 /* number of rows to fetch */]; - - /* Send row request to the server */ - int4store(buff, stmt->stmt_id); - int4store(buff + 4, (int)~0); /* number of rows to fetch */ - if (cli_advanced_command(mysql, COM_STMT_FETCH, buff, sizeof(buff), - (uchar*) 0, 0, 1, stmt)) - { - /* - Don't set stmt error if stmt->mysql is NULL, as the error in this case - has already been set by mysql_prune_stmt_list(). - */ - if (stmt->mysql) - set_stmt_errmsg(stmt, net); - DBUG_RETURN(1); - } - } - else if (mysql->status != MYSQL_STATUS_STATEMENT_GET_RESULT) - { - set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - - if (stmt->update_max_length && !stmt->bind_result_done) - { - /* - We must initalize the bind structure to be able to calculate - max_length - */ - MYSQL_BIND *my_bind, *end; - MYSQL_FIELD *field; - memset(stmt->bind, 0, sizeof(*stmt->bind) * stmt->field_count); - - for (my_bind= stmt->bind, end= my_bind + stmt->field_count, - field= stmt->fields; - my_bind < end ; - my_bind++, field++) - { - my_bind->buffer_type= MYSQL_TYPE_NULL; - my_bind->buffer_length=1; - } - - if (mysql_stmt_bind_result(stmt, stmt->bind)) - DBUG_RETURN(1); - stmt->bind_result_done= 0; /* No normal bind done */ - } - - if ((*mysql->methods->read_binary_rows)(stmt)) - { - free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); - result->data= NULL; - result->rows= 0; - mysql->status= MYSQL_STATUS_READY; - DBUG_RETURN(1); - } - - /* Assert that if there was a cursor, all rows have been fetched */ - DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY || - (mysql->server_status & SERVER_STATUS_LAST_ROW_SENT)); - - if (stmt->update_max_length) - { - MYSQL_ROWS *cur= result->data; - for(; cur; cur=cur->next) - stmt_update_metadata(stmt, cur); - } - - stmt->data_cursor= result->data; - mysql->affected_rows= stmt->affected_rows= result->rows; - stmt->read_row_func= stmt_read_row_buffered; - mysql->unbuffered_fetch_owner= 0; /* set in stmt_execute */ - mysql->status= MYSQL_STATUS_READY; /* server is ready */ - DBUG_RETURN(0); /* Data buffered, must be fetched with mysql_stmt_fetch() */ -} - - -/* - Seek to desired row in the statement result set -*/ - -MYSQL_ROW_OFFSET STDCALL -mysql_stmt_row_seek(MYSQL_STMT *stmt, MYSQL_ROW_OFFSET row) -{ - MYSQL_ROW_OFFSET offset= stmt->data_cursor; - DBUG_ENTER("mysql_stmt_row_seek"); - - stmt->data_cursor= row; - DBUG_RETURN(offset); -} - - -/* - Return the current statement row cursor position -*/ - -MYSQL_ROW_OFFSET STDCALL -mysql_stmt_row_tell(MYSQL_STMT *stmt) -{ - DBUG_ENTER("mysql_stmt_row_tell"); - - DBUG_RETURN(stmt->data_cursor); -} - - -/* - Move the stmt result set data cursor to specified row -*/ - -void STDCALL -mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong row) -{ - MYSQL_ROWS *tmp= stmt->result.data; - DBUG_ENTER("mysql_stmt_data_seek"); - DBUG_PRINT("enter",("row id to seek: %ld",(long) row)); - - for (; tmp && row; --row, tmp= tmp->next) - ; - stmt->data_cursor= tmp; - if (!row && tmp) - { - /* Rewind the counter */ - stmt->read_row_func= stmt_read_row_buffered; - stmt->state= MYSQL_STMT_EXECUTE_DONE; - } - DBUG_VOID_RETURN; -} - - -/* - Return total rows the current statement result set -*/ - -my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) -{ - DBUG_ENTER("mysql_stmt_num_rows"); - - DBUG_RETURN(stmt->result.rows); -} - - -/* - Free the client side memory buffers, reset long data state - on client if necessary, and reset the server side statement if - this has been requested. -*/ - -static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags) -{ - /* If statement hasn't been prepared there is nothing to reset */ - if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) - { - MYSQL *mysql= stmt->mysql; - MYSQL_DATA *result= &stmt->result; - - /* - Reset stored result set if so was requested or it's a part - of cursor fetch. - */ - if (flags & RESET_STORE_RESULT) - { - /* Result buffered */ - free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); - result->data= NULL; - result->rows= 0; - stmt->data_cursor= NULL; - } - if (flags & RESET_LONG_DATA) - { - MYSQL_BIND *param= stmt->params, *param_end= param + stmt->param_count; - /* Clear long_data_used flags */ - for (; param < param_end; param++) - param->long_data_used= 0; - } - stmt->read_row_func= stmt_read_row_no_result_set; - if (mysql) - { - if ((int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE) - { - if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) - mysql->unbuffered_fetch_owner= 0; - if (stmt->field_count && mysql->status != MYSQL_STATUS_READY) - { - /* There is a result set and it belongs to this statement */ - (*mysql->methods->flush_use_result)(mysql, FALSE); - if (mysql->unbuffered_fetch_owner) - *mysql->unbuffered_fetch_owner= TRUE; - mysql->status= MYSQL_STATUS_READY; - } - } - if (flags & RESET_SERVER_SIDE) - { - /* - Reset the server side statement and close the server side - cursor if it exists. - */ - uchar buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */ - int4store(buff, stmt->stmt_id); - if ((*mysql->methods->advanced_command)(mysql, COM_STMT_RESET, buff, - sizeof(buff), 0, 0, 0, stmt)) - { - set_stmt_errmsg(stmt, &mysql->net); - stmt->state= MYSQL_STMT_INIT_DONE; - return 1; - } - } - } - if (flags & RESET_CLEAR_ERROR) - stmt_clear_error(stmt); - stmt->state= MYSQL_STMT_PREPARE_DONE; - } - return 0; -} - -my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) -{ - DBUG_ENTER("mysql_stmt_free_result"); - - /* Free the client side and close the server side cursor if there is one */ - DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT | - RESET_CLEAR_ERROR)); -} - -/******************************************************************** - statement error handling and close -*********************************************************************/ - -/* - Close the statement handle by freeing all alloced resources - - SYNOPSIS - mysql_stmt_close() - stmt Statement handle - - RETURN VALUES - 0 ok - 1 error -*/ - -my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) -{ - MYSQL *mysql= stmt->mysql; - int rc= 0; - DBUG_ENTER("mysql_stmt_close"); - - free_root(&stmt->result.alloc, MYF(0)); - free_root(&stmt->mem_root, MYF(0)); - free_root(&stmt->extension->fields_mem_root, MYF(0)); - - if (mysql) - { - mysql->stmts= list_delete(mysql->stmts, &stmt->list); - /* - Clear NET error state: if the following commands come through - successfully, connection will still be usable for other commands. - */ - net_clear_error(&mysql->net); - if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) - { - uchar buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ - - if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) - mysql->unbuffered_fetch_owner= 0; - if (mysql->status != MYSQL_STATUS_READY) - { - /* - Flush result set of the connection. If it does not belong - to this statement, set a warning. - */ - (*mysql->methods->flush_use_result)(mysql, TRUE); - if (mysql->unbuffered_fetch_owner) - *mysql->unbuffered_fetch_owner= TRUE; - mysql->status= MYSQL_STATUS_READY; - } - int4store(buff, stmt->stmt_id); - /* - If stmt_command failed, it would have already raised - error using set_mysql_error. Caller should use - mysql_error() or mysql_errno() to find out details. - Memory allocated for stmt will be released regardless - of the error. - */ - rc= stmt_command(mysql, COM_STMT_CLOSE, buff, 4, stmt); - } - } - - my_free(stmt->extension); - my_free(stmt); - - DBUG_RETURN(MY_TEST(rc)); -} - -/* - Reset the statement buffers in server -*/ - -my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) -{ - DBUG_ENTER("mysql_stmt_reset"); - DBUG_ASSERT(stmt != 0); - if (!stmt->mysql) - { - /* mysql can be reset in mysql_close called from mysql_reconnect */ - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); - DBUG_RETURN(1); - } - /* Reset the client and server sides of the prepared statement */ - DBUG_RETURN(reset_stmt_handle(stmt, - RESET_SERVER_SIDE | RESET_LONG_DATA | - RESET_CLEAR_ERROR)); -} - -/* - Return statement error code -*/ - -uint STDCALL mysql_stmt_errno(MYSQL_STMT * stmt) -{ - DBUG_ENTER("mysql_stmt_errno"); - DBUG_RETURN(stmt->last_errno); -} - -const char *STDCALL mysql_stmt_sqlstate(MYSQL_STMT * stmt) -{ - DBUG_ENTER("mysql_stmt_sqlstate"); - DBUG_RETURN(stmt->sqlstate); -} - -/* - Return statement error message -*/ - -const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt) -{ - DBUG_ENTER("mysql_stmt_error"); - DBUG_RETURN(stmt->last_error); -} - - -/******************************************************************** - Transactional APIs -*********************************************************************/ - -/* - Commit the current transaction -*/ - -my_bool STDCALL mysql_commit(MYSQL * mysql) -{ - DBUG_ENTER("mysql_commit"); - DBUG_RETURN((my_bool) mysql_real_query(mysql, "commit", 6)); -} - -/* - Rollback the current transaction -*/ - -my_bool STDCALL mysql_rollback(MYSQL * mysql) -{ - DBUG_ENTER("mysql_rollback"); - DBUG_RETURN((my_bool) mysql_real_query(mysql, "rollback", 8)); -} - - -/* - Set autocommit to either true or false -*/ - -my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode) -{ - DBUG_ENTER("mysql_autocommit"); - DBUG_PRINT("enter", ("mode : %d", auto_mode)); - - DBUG_RETURN((my_bool) mysql_real_query(mysql, auto_mode ? - "set autocommit=1":"set autocommit=0", - 16)); -} - - -/******************************************************************** - Multi query execution + SPs APIs -*********************************************************************/ - -/* - Returns true/false to indicate whether any more query results exist - to be read using mysql_next_result() -*/ - -my_bool STDCALL mysql_more_results(MYSQL *mysql) -{ - my_bool res; - DBUG_ENTER("mysql_more_results"); - - res= ((mysql->server_status & SERVER_MORE_RESULTS_EXISTS) ? 1: 0); - DBUG_PRINT("exit",("More results exists ? %d", res)); - DBUG_RETURN(res); -} - - -/* - Reads and returns the next query results -*/ -int STDCALL mysql_next_result(MYSQL *mysql) -{ - DBUG_ENTER("mysql_next_result"); - - MYSQL_TRACE_STAGE(mysql, WAIT_FOR_RESULT); - - if (mysql->status != MYSQL_STATUS_READY) - { - set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); - DBUG_RETURN(1); - } - - net_clear_error(&mysql->net); - mysql->affected_rows= ~(my_ulonglong) 0; - - if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) - DBUG_RETURN((*mysql->methods->next_result)(mysql)); - else - { - MYSQL_TRACE_STAGE(mysql, READY_FOR_COMMAND); - } - - DBUG_RETURN(-1); /* No more results */ -} - - -int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) -{ - MYSQL *mysql= stmt->mysql; - int rc; - DBUG_ENTER("mysql_stmt_next_result"); - - if (!mysql) - DBUG_RETURN(1); - - if (stmt->last_errno) - DBUG_RETURN(stmt->last_errno); - - if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) - { - if (reset_stmt_handle(stmt, RESET_STORE_RESULT)) - DBUG_RETURN(1); - } - - rc= mysql_next_result(mysql); - - if (rc) - { - set_stmt_errmsg(stmt, &mysql->net); - DBUG_RETURN(rc); - } - - if (mysql->status == MYSQL_STATUS_GET_RESULT) - mysql->status= MYSQL_STATUS_STATEMENT_GET_RESULT; - - stmt->state= MYSQL_STMT_EXECUTE_DONE; - stmt->bind_result_done= FALSE; - stmt->field_count= mysql->field_count; - - if (mysql->field_count) - { - alloc_stmt_fields(stmt); - prepare_to_fetch_result(stmt); - } - - DBUG_RETURN(0); -} - - -MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql) -{ - return (*mysql->methods->use_result)(mysql); -} - -my_bool STDCALL mysql_read_query_result(MYSQL *mysql) -{ - return (*mysql->methods->read_query_result)(mysql); -} - -int STDCALL -mysql_reset_connection(MYSQL *mysql) -{ - DBUG_ENTER("mysql_reset_connection"); - if(simple_command(mysql,COM_RESET_CONNECTION,0,0,0)) - DBUG_RETURN(1); - else - { - mysql_detach_stmt_list(&mysql->stmts, "mysql_reset_connection"); - /* reset some of the members in mysql */ - mysql->insert_id= 0; - mysql->affected_rows= ~(my_ulonglong) 0; - free_old_query(mysql); - mysql->status=MYSQL_STATUS_READY; - DBUG_RETURN(0); - } -} diff --git a/mysql/libmysql/libmysql_exports_win32.def b/mysql/libmysql/libmysql_exports_win32.def deleted file mode 100644 index dd034a7..0000000 --- a/mysql/libmysql/libmysql_exports_win32.def +++ /dev/null @@ -1,120 +0,0 @@ -EXPORTS - mysql_thread_end - mysql_thread_init - mysql_affected_rows - mysql_autocommit - mysql_stmt_bind_param - mysql_stmt_bind_result - mysql_change_user - mysql_character_set_name - mysql_close - mysql_commit - mysql_data_seek - mysql_debug - mysql_dump_debug_info - mysql_eof - mysql_errno - mysql_error - mysql_escape_string - mysql_hex_string - mysql_stmt_execute - mysql_stmt_fetch - mysql_stmt_fetch_column - mysql_fetch_field - mysql_fetch_field_direct - mysql_fetch_fields - mysql_fetch_lengths - mysql_fetch_row - mysql_field_count - mysql_field_seek - mysql_field_tell - mysql_free_result - mysql_get_client_info - mysql_get_host_info - mysql_get_proto_info - mysql_get_server_info - mysql_get_client_version - mysql_get_ssl_cipher - mysql_info - mysql_init - mysql_insert_id - mysql_kill - mysql_set_server_option - mysql_list_dbs - mysql_list_fields - mysql_list_processes - mysql_list_tables - mysql_more_results - mysql_next_result - mysql_num_fields - mysql_num_rows - mysql_options - mysql_stmt_param_count - mysql_stmt_param_metadata - mysql_ping - mysql_stmt_result_metadata - mysql_query - mysql_read_query_result - mysql_real_connect - mysql_real_escape_string - mysql_real_escape_string_quote - mysql_real_query - mysql_refresh - mysql_rollback - mysql_row_seek - mysql_row_tell - mysql_select_db - mysql_stmt_send_long_data - mysql_send_query - mysql_shutdown - mysql_ssl_set - mysql_stat - mysql_stmt_affected_rows - mysql_stmt_close - mysql_stmt_reset - mysql_stmt_data_seek - mysql_stmt_errno - mysql_stmt_error - mysql_stmt_free_result - mysql_stmt_num_rows - mysql_stmt_row_seek - mysql_stmt_row_tell - mysql_stmt_store_result - mysql_store_result - mysql_thread_id - mysql_thread_safe - mysql_use_result - mysql_warning_count - mysql_stmt_sqlstate - mysql_sqlstate - mysql_get_server_version - mysql_stmt_prepare - mysql_stmt_init - mysql_stmt_insert_id - mysql_stmt_attr_get - mysql_stmt_attr_set - mysql_stmt_field_count - mysql_set_local_infile_default - mysql_set_local_infile_handler - mysql_server_init - mysql_server_end - mysql_set_character_set - mysql_get_character_set_info - mysql_stmt_next_result - my_init - mysql_client_find_plugin - mysql_client_register_plugin - mysql_load_plugin - mysql_load_plugin_v - mysql_options4 - mysql_plugin_options - mysql_reset_connection - mysql_get_option - mysql_session_track_get_first - mysql_session_track_get_next - get_tty_password - my_load_defaults - handle_options - my_make_scrambled_password - - diff --git a/mysql/libmysql/mysql_trace.c b/mysql/libmysql/mysql_trace.c deleted file mode 100644 index 3073479..0000000 --- a/mysql/libmysql/mysql_trace.c +++ /dev/null @@ -1,215 +0,0 @@ -/* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - @file - - ============================================= - Client-side protocol tracing infrastructure - ============================================= - - If a plugin of type MYSQL_CLIENT_TRACE_PLUGIN is loaded into - libmysql and its instance is pointed by the global trace_plugin - pointer, this plugin is notified of various protocol trace events. - See include/mysql/plugin_trace.h for documentation of trace plugin - methods and the list of possible trace events. - - These trace events are generated with MYSQL_TRACE() macro put in - relevant places in libmysql code. The macro calls mysql_trace_trace() - function defined here. This function calls trace plugin's - trace_event() method, if it is defined. - - For each traced connection, the state is kept in st_mysql_trace_info - structure (see mysql_trace.h). - - To correctly interpret each trace event, trace plugin is informed - of the current protocol stage (see include/mysql/plugin_trace.h for - list of stages). The current protocol stage is part of the - connection tracing state. It is updated with MYSQL_TRACE_STAGE() - hooks within libmysql code. -*/ - -#include -#include "mysql.h" -#include "mysql_trace.h" - -/* - Definition of the global trace_plugin pointer - see plugin_trace.h - for declaration and description. -*/ -struct st_mysql_client_plugin_TRACE *trace_plugin= NULL; - -/* - Macros for manipulating trace_info structure. -*/ -#define GET_DATA(TI) (TI)->trace_plugin_data -#define SET_DATA(TI,D) GET_DATA(TI) = (D) -#define GET_STAGE(TI) (TI)->stage -#define TEST_STAGE(TI,X) (GET_STAGE(TI) == PROTOCOL_STAGE_ ## X) -#define SET_STAGE(TI,X) GET_STAGE(TI) = PROTOCOL_STAGE_ ## X - - -/** - Initialize tracing in a given connection. - - This function is called from MYSQL_TRACE_STAGE() when the initial - CONNECTING stage is reported. It allocates and initializes trace_info - structure which is then attached to the connection handle. -*/ - -void mysql_trace_start(struct st_mysql *m) -{ - struct st_mysql_trace_info *trace_info; - - trace_info= my_malloc(PSI_NOT_INSTRUMENTED, - sizeof(struct st_mysql_trace_info), - MYF(MY_ZEROFILL)); - if (!trace_info) - { - /* - Note: in this case trace_data of the connection will - remain NULL and thus tracing will be disabled. - */ - return; - } - - /* - This function should be called only when a trace plugin - is loaded and thus trace_plugin pointer is not NULL. This - is handled in MYSQL_TRACE_STAGE() macro (mysql_trace.h). - */ - DBUG_ASSERT(trace_plugin); - - trace_info->plugin= trace_plugin; - trace_info->stage= PROTOCOL_STAGE_CONNECTING; - - /* - Call plugin's tracing_start() method, if defined. - */ - - if (trace_info->plugin->tracing_start) - { - trace_info->trace_plugin_data= - trace_info->plugin->tracing_start( - trace_info->plugin, - m, PROTOCOL_STAGE_CONNECTING); - } - else - { - trace_info->trace_plugin_data= NULL; - } - - /* Store trace_info in the connection handle. */ - - TRACE_DATA(m)= trace_info; -} - - -/** - Report a protocol trace event to trace plugin. - - Calls plugin's trace_event() method, if it is defined, passing to - it the event, the current protocol stage and event arguments (if any). - - Terminates tracing of the connection when appropriate. - - @param m MYSQL connection handle - @param ev trace event to be reported - @param args trace event arguments -*/ - -void mysql_trace_trace(struct st_mysql *m, - enum trace_event ev, - struct st_trace_event_args args) -{ - struct st_mysql_trace_info *trace_info= TRACE_DATA(m); - struct st_mysql_client_plugin_TRACE *plugin= trace_info ? trace_info->plugin : NULL; - int quit_tracing= 0; - - /* - If trace_info is NULL then this connection is not traced and this - function should not be called - this is handled inside MYSQL_TRACE() - macro. - */ - DBUG_ASSERT(trace_info); - - /* Call plugin's trace_event() method if defined */ - - if (plugin->trace_event) - { - /* - Temporarily disable tracing while executing plugin's method - by setting trace data pointer to NULL. Also, set reconnect - flag to 0 in case plugin executes any queries. - */ - my_bool saved_reconnect_flag= m->reconnect; - - TRACE_DATA(m)= NULL; - m->reconnect= 0; - quit_tracing= plugin->trace_event(plugin, GET_DATA(trace_info), m, - GET_STAGE(trace_info), ev, args); - m->reconnect= saved_reconnect_flag; - TRACE_DATA(m)= trace_info; - } - - /* Stop tracing if requested or end of connection. */ - - if (quit_tracing - || TEST_STAGE(trace_info, DISCONNECTED) - || TRACE_EVENT_DISCONNECTED == ev) - { - /* Note: this disables further tracing */ - TRACE_DATA(m)= NULL; - - if (plugin->tracing_stop) - plugin->tracing_stop(plugin, m, GET_DATA(trace_info)); - - my_free(trace_info); - } -} - - -#ifndef DBUG_OFF -/* - These functions are declared in plugin_trace.h. - - Consult documentation of *_LIST() macros (plugin_trace.h) to see how - switch() bodies are constructed with the *_get_name() macros. -*/ - -#define protocol_stage_get_name(X) case PROTOCOL_STAGE_ ## X: return #X; - -const char* protocol_stage_name(enum protocol_stage stage) -{ - switch(stage) - { - PROTOCOL_STAGE_LIST(get_name) - default: return ""; - } -} - - -#define trace_event_get_name(X) case TRACE_EVENT_ ## X: return #X; - -const char* trace_event_name(enum trace_event ev) -{ - switch(ev) - { - TRACE_EVENT_LIST(get_name) - default: return ""; - } -} - -#endif diff --git a/mysql/libmysql/mysql_trace.h b/mysql/libmysql/mysql_trace.h deleted file mode 100644 index 6ec4e76..0000000 --- a/mysql/libmysql/mysql_trace.h +++ /dev/null @@ -1,165 +0,0 @@ -/* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#ifndef MYSQL_TRACE_INCLUDED -#define MYSQL_TRACE_INCLUDED -/** - @file - - ===================================================== - Declarations for client-side tracing infrastructure - ===================================================== - - See libmysql/mysql_trace.c for a brief description. Trace plugin - declarations are in plugin_trace.h header. -*/ - - -C_MODE_START - -/* - Disable trace hooks if the infrastructure is not enabled or if - libmysql code is used from within the (embedded) server. -*/ -#if !defined(CLIENT_PROTOCOL_TRACING) \ - || defined(MYSQL_SERVER) \ - || defined(EMBEDDED_LIBRARY) - -#define MYSQL_TRACE(E, M, ARGS) -#define MYSQL_TRACE_STAGE(M, S) - -#else - -#include -#include /* for MYSQL_EXTENSION() */ -#include - - -/** - Per connection protocol tracing state - - For each connection which is traced an instance of this structure - is pointed by the trace_data member of st_mysql_extension structure - attached to that connection handle. - - If trace_data is NULL, for an initialized connection, then it means - that tracing in this connection is disabled. -*/ - -struct st_mysql_trace_info -{ - struct st_mysql_client_plugin_TRACE *plugin; - void *trace_plugin_data; - enum protocol_stage stage; -}; - -#define TRACE_DATA(M) (MYSQL_EXTENSION_PTR(M)->trace_data) - - -/* - See libmysql/mysql_trace.c for documentation and implementation of - these functions. -*/ - -void mysql_trace_trace(struct st_mysql*, - enum trace_event, - struct st_trace_event_args); -void mysql_trace_start(struct st_mysql*); - - -/** - The main protocol tracing hook. - - It is placed in places in the libmysql code where trace events occur. - If tracing of the connection is not disabled, it calls - mysql_trace_trace() function to report the event to the - trace plugin. - - @param E trace event (without TRACE_EVENT_ prefix) - @param M connection handle (pointer to MYSQL structure) - @param ARGS event specific arguments - - Event arguments are processed with appropriate TRACE_ARGS_* macro - (see below) to put them inside st_trace_event_args structure. -*/ - -#define MYSQL_TRACE(E, M, ARGS) \ - do { \ - if (NULL == TRACE_DATA(M)) break; \ - { \ - struct st_trace_event_args event_args= \ - TRACE_ARGS_ ## E ARGS; \ - mysql_trace_trace(M, \ - TRACE_EVENT_ ## E, \ - event_args); \ - } \ - } while(0) - - -/** - A hook to set the current protocol stage. - - @param M connection handle (pointer to MYSQL structure) - @param S the current stage (without PROTOCOL_STAGE_ prefix) - - If tracing is not disabled, the stage is stored in connection's - tracing state. A special case is if the current stage is the - initial CONNECTING one. In that case function mysql_trace_start() - is called to initialize tracing in this connection, provided that - a trace plugin is loaded. -*/ - -#define MYSQL_TRACE_STAGE(M, S) \ - do { \ - if (TRACE_DATA(M)) \ - TRACE_DATA(M)->stage= PROTOCOL_STAGE_ ## S; \ - else if (trace_plugin && \ - (PROTOCOL_STAGE_CONNECTING == PROTOCOL_STAGE_ ## S)) \ - mysql_trace_start(M); \ - } while(0) - -/* - Macros to parse event arguments and initialize the - st_trace_event_args structure accordingly. See description of - the structure in plugin_trace.h. -*/ - -#define TRACE_ARGS_SEND_SSL_REQUEST(Size, Packet) { NULL, 0, NULL, 0, Packet, Size } -#define TRACE_ARGS_SEND_AUTH_RESPONSE(Size, Packet) { NULL, 0, NULL, 0, Packet, Size } -#define TRACE_ARGS_SEND_AUTH_DATA(Size, Packet) { NULL, 0, NULL, 0, Packet, Size } -#define TRACE_ARGS_AUTH_PLUGIN(PluginName) { PluginName, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_SEND_COMMAND(Command, HdrSize, ArgSize, Header, Args) \ - { NULL, Command, Header, HdrSize, Args, ArgSize } -#define TRACE_ARGS_SEND_FILE(Size, Packet) { NULL, 0, NULL, 0, Packet, Size } - -#define TRACE_ARGS_PACKET_SENT(Size) { NULL, 0, NULL, 0, NULL, Size } -#define TRACE_ARGS_PACKET_RECEIVED(Size, Packet) { NULL, 0, NULL, 0, Packet, Size } -#define TRACE_ARGS_INIT_PACKET_RECEIVED(Size, Packet) { NULL, 0, NULL, 0, Packet, Size } - -#define TRACE_ARGS_ERROR() { NULL, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_READ_PACKET() { NULL, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_CONNECTING() { NULL, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_CONNECTED() { NULL, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_DISCONNECTED() { NULL, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_AUTHENTICATED() { NULL, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_SSL_CONNECT() { NULL, 0, NULL, 0, NULL, 0 } -#define TRACE_ARGS_SSL_CONNECTED() { NULL, 0, NULL, 0, NULL, 0 } - - -#endif /* !defined(CLIENT_PROTOCOL_TRACING) || defined(MYSQL_SERVER) */ - -C_MODE_END - -#endif -- cgit v1.1