You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-07 02:42:49 +03:00
Added support for connection attributes
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
/************************************************************************************
|
||||||
|
Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
|
||||||
|
Monty Program AB
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Library General Public
|
modify it under the terms of the GNU Library General Public
|
||||||
@@ -11,11 +13,13 @@
|
|||||||
Library General Public License for more details.
|
Library General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
You should have received a copy of the GNU Library General Public
|
||||||
License along with this library; if not, write to the Free
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
or write to the Free Software Foundation, Inc.,
|
||||||
MA 02111-1307, USA */
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
|
||||||
/* Dynamic hashing of record with different key-length */
|
Part of this code includes code from the PHP project which
|
||||||
|
is freely available from http://www.php.net
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
#ifndef _hash_h
|
#ifndef _hash_h
|
||||||
#define _hash_h
|
#define _hash_h
|
||||||
@@ -23,7 +27,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef byte *(*hash_get_key)(const byte *,uint*,my_bool);
|
typedef uchar *(*hash_get_key)(const uchar *,size_t*,my_bool);
|
||||||
typedef void (*hash_free_key)(void *);
|
typedef void (*hash_free_key)(void *);
|
||||||
|
|
||||||
/* flags for hash_init */
|
/* flags for hash_init */
|
||||||
@@ -31,7 +35,7 @@ typedef void (*hash_free_key)(void *);
|
|||||||
|
|
||||||
typedef struct st_hash_info {
|
typedef struct st_hash_info {
|
||||||
uint next; /* index to next key */
|
uint next; /* index to next key */
|
||||||
byte *data; /* data for current entry */
|
uchar *data; /* data for current entry */
|
||||||
} HASH_LINK;
|
} HASH_LINK;
|
||||||
|
|
||||||
typedef struct st_hash {
|
typedef struct st_hash {
|
||||||
@@ -41,7 +45,7 @@ typedef struct st_hash {
|
|||||||
DYNAMIC_ARRAY array; /* Place for hash_keys */
|
DYNAMIC_ARRAY array; /* Place for hash_keys */
|
||||||
hash_get_key get_key;
|
hash_get_key get_key;
|
||||||
void (*free)(void *);
|
void (*free)(void *);
|
||||||
uint (*calc_hashnr)(const byte *key,uint length);
|
uint (*calc_hashnr)(const uchar *key,uint length);
|
||||||
} HASH;
|
} HASH;
|
||||||
|
|
||||||
#define hash_init(A,B,C,D,E,F,G) _hash_init(A,B,C,D,E,F,G CALLER_INFO)
|
#define hash_init(A,B,C,D,E,F,G) _hash_init(A,B,C,D,E,F,G CALLER_INFO)
|
||||||
@@ -49,12 +53,12 @@ my_bool _hash_init(HASH *hash,uint default_array_elements, uint key_offset,
|
|||||||
uint key_length, hash_get_key get_key,
|
uint key_length, hash_get_key get_key,
|
||||||
void (*free_element)(void*), uint flags CALLER_INFO_PROTO);
|
void (*free_element)(void*), uint flags CALLER_INFO_PROTO);
|
||||||
void hash_free(HASH *tree);
|
void hash_free(HASH *tree);
|
||||||
byte *hash_element(HASH *hash,uint idx);
|
uchar *hash_element(HASH *hash,uint idx);
|
||||||
gptr hash_search(HASH *info,const byte *key,uint length);
|
gptr hash_search(HASH *info,const uchar *key,uint length);
|
||||||
gptr hash_next(HASH *info,const byte *key,uint length);
|
gptr hash_next(HASH *info,const uchar *key,uint length);
|
||||||
my_bool hash_insert(HASH *info,const byte *data);
|
my_bool hash_insert(HASH *info,const uchar *data);
|
||||||
my_bool hash_delete(HASH *hash,byte *record);
|
my_bool hash_delete(HASH *hash,uchar *record);
|
||||||
my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length);
|
my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,uint old_key_length);
|
||||||
my_bool hash_check(HASH *hash); /* Only in debug library */
|
my_bool hash_check(HASH *hash); /* Only in debug library */
|
||||||
|
|
||||||
#define hash_clear(H) bzero((char*) (H),sizeof(*(H)))
|
#define hash_clear(H) bzero((char*) (H),sizeof(*(H)))
|
||||||
|
@@ -275,3 +275,4 @@
|
|||||||
#cmakedefine SHAREDIR "@SHAREDIR@"
|
#cmakedefine SHAREDIR "@SHAREDIR@"
|
||||||
#cmakedefine DEFAULT_CHARSET_HOME "@DEFAULT_CHARSET_HOME@"
|
#cmakedefine DEFAULT_CHARSET_HOME "@DEFAULT_CHARSET_HOME@"
|
||||||
#cmakedefine PLUGINDIR "@PLUGINDIR@"
|
#cmakedefine PLUGINDIR "@PLUGINDIR@"
|
||||||
|
|
||||||
|
@@ -54,6 +54,8 @@ typedef int my_socket;
|
|||||||
#include "mysql_version.h"
|
#include "mysql_version.h"
|
||||||
#include "my_list.h"
|
#include "my_list.h"
|
||||||
#include "m_ctype.h"
|
#include "m_ctype.h"
|
||||||
|
#include "my_sys.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
#ifndef ST_USED_MEM_DEFINED
|
#ifndef ST_USED_MEM_DEFINED
|
||||||
#define ST_USED_MEM_DEFINED
|
#define ST_USED_MEM_DEFINED
|
||||||
@@ -189,6 +191,10 @@ enum mysql_option
|
|||||||
MYSQL_OPT_SSL_CIPHER,
|
MYSQL_OPT_SSL_CIPHER,
|
||||||
MYSQL_OPT_SSL_CRL,
|
MYSQL_OPT_SSL_CRL,
|
||||||
MYSQL_OPT_SSL_CRLPATH,
|
MYSQL_OPT_SSL_CRLPATH,
|
||||||
|
/* Connection attribute options */
|
||||||
|
MYSQL_OPT_CONNECT_ATTR_RESET,
|
||||||
|
MYSQL_OPT_CONNECT_ATTR_ADD,
|
||||||
|
MYSQL_OPT_CONNECT_ATTR_DELETE,
|
||||||
|
|
||||||
/* MariaDB specific */
|
/* MariaDB specific */
|
||||||
MYSQL_PROGRESS_CALLBACK=5999,
|
MYSQL_PROGRESS_CALLBACK=5999,
|
||||||
@@ -290,6 +296,8 @@ struct st_mysql_options_extention {
|
|||||||
char *ssl_crl;
|
char *ssl_crl;
|
||||||
char *ssl_crlpath;
|
char *ssl_crlpath;
|
||||||
char *server_public_key_path;
|
char *server_public_key_path;
|
||||||
|
HASH connect_attrs;
|
||||||
|
size_t connect_attrs_len;
|
||||||
void (*report_progress)(const MYSQL *mysql,
|
void (*report_progress)(const MYSQL *mysql,
|
||||||
unsigned int stage,
|
unsigned int stage,
|
||||||
unsigned int max_stage,
|
unsigned int max_stage,
|
||||||
@@ -468,6 +476,8 @@ CHARSET_INFO * STDCALL mysql_get_charset_by_name(const char *csname);
|
|||||||
CHARSET_INFO * STDCALL mysql_get_charset_by_nr(uint csnr);
|
CHARSET_INFO * STDCALL mysql_get_charset_by_nr(uint csnr);
|
||||||
size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, CHARSET_INFO *from_cs,
|
size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, CHARSET_INFO *from_cs,
|
||||||
char *to, size_t *to_len, CHARSET_INFO *to_cs, int *errorcode);
|
char *to, size_t *to_len, CHARSET_INFO *to_cs, int *errorcode);
|
||||||
|
int STDCALL mysql_options4(MYSQL *mysql,enum mysql_option option,
|
||||||
|
const void *arg1, const void *arg2);
|
||||||
|
|
||||||
#include <my_stmt.h>
|
#include <my_stmt.h>
|
||||||
|
|
||||||
|
@@ -149,6 +149,7 @@ enum enum_server_command
|
|||||||
#define CLIENT_MULTI_RESULTS (1UL << 17)
|
#define CLIENT_MULTI_RESULTS (1UL << 17)
|
||||||
#define CLIENT_PS_MULTI_RESULTS (1UL << 18)
|
#define CLIENT_PS_MULTI_RESULTS (1UL << 18)
|
||||||
#define CLIENT_PLUGIN_AUTH (1UL << 19)
|
#define CLIENT_PLUGIN_AUTH (1UL << 19)
|
||||||
|
#define CLIENT_CONNECT_ATTRS (1UL << 20)
|
||||||
#define CLIENT_PROGRESS (1UL << 29) /* client supports progress indicator */
|
#define CLIENT_PROGRESS (1UL << 29) /* client supports progress indicator */
|
||||||
#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
|
#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
|
||||||
#define CLIENT_REMEMBER_OPTIONS (1UL << 31)
|
#define CLIENT_REMEMBER_OPTIONS (1UL << 31)
|
||||||
@@ -173,7 +174,8 @@ enum enum_server_command
|
|||||||
CLIENT_MULTI_RESULTS |\
|
CLIENT_MULTI_RESULTS |\
|
||||||
CLIENT_PROGRESS |\
|
CLIENT_PROGRESS |\
|
||||||
CLIENT_SSL_VERIFY_SERVER_CERT |\
|
CLIENT_SSL_VERIFY_SERVER_CERT |\
|
||||||
CLIENT_REMEMBER_OPTIONS)
|
CLIENT_REMEMBER_OPTIONS |\
|
||||||
|
CLIENT_CONNECT_ATTRS)
|
||||||
|
|
||||||
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD |\
|
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD |\
|
||||||
CLIENT_LONG_FLAG |\
|
CLIENT_LONG_FLAG |\
|
||||||
@@ -182,7 +184,8 @@ enum enum_server_command
|
|||||||
CLIENT_MULTI_RESULTS | \
|
CLIENT_MULTI_RESULTS | \
|
||||||
CLIENT_PS_MULTI_RESULTS |\
|
CLIENT_PS_MULTI_RESULTS |\
|
||||||
CLIENT_PROTOCOL_41 |\
|
CLIENT_PROTOCOL_41 |\
|
||||||
CLIENT_PLUGIN_AUTH)
|
CLIENT_PLUGIN_AUTH |\
|
||||||
|
CLIENT_CONNECT_ATTRS)
|
||||||
|
|
||||||
#define CLIENT_DEFAULT_FLAGS ((CLIENT_SUPPORTED_FLAGS & ~CLIENT_COMPRESS)\
|
#define CLIENT_DEFAULT_FLAGS ((CLIENT_SUPPORTED_FLAGS & ~CLIENT_COMPRESS)\
|
||||||
& ~CLIENT_SSL)
|
& ~CLIENT_SSL)
|
||||||
|
@@ -16,6 +16,10 @@
|
|||||||
#define MYSQL_UNIX_ADDR "@MYSQL_UNIX_ADDR@"
|
#define MYSQL_UNIX_ADDR "@MYSQL_UNIX_ADDR@"
|
||||||
#define MYSQL_CONFIG_NAME "my"
|
#define MYSQL_CONFIG_NAME "my"
|
||||||
|
|
||||||
|
#define MARIADB_PACKAGE_VERSION "@CPACK_PACKAGE_VERSION@"
|
||||||
|
#define MARIADB_SYSTEM_TYPE "@CMAKE_SYSTEM_NAME@"
|
||||||
|
#define MARIADB_MACHINE_TYPE "@CMAKE_SYSTEM_PROCESSOR@"
|
||||||
|
|
||||||
/* mysqld compile time options */
|
/* mysqld compile time options */
|
||||||
#ifndef MYSQL_CHARSET
|
#ifndef MYSQL_CHARSET
|
||||||
#define MYSQL_CHARSET "@default_charset@"
|
#define MYSQL_CHARSET "@default_charset@"
|
||||||
|
@@ -23,6 +23,7 @@ bchange.c
|
|||||||
bmove.c
|
bmove.c
|
||||||
bmove_upp.c
|
bmove_upp.c
|
||||||
my_charset.c
|
my_charset.c
|
||||||
|
hash.c
|
||||||
violite.c
|
violite.c
|
||||||
net.c
|
net.c
|
||||||
charset.c
|
charset.c
|
||||||
|
644
libmariadb/hash.c
Normal file
644
libmariadb/hash.c
Normal file
@@ -0,0 +1,644 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
|
||||||
|
Monty Program AB
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
|
||||||
|
Part of this code includes code from the PHP project which
|
||||||
|
is freely available from http://www.php.net
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
/* The hash functions used for saveing keys */
|
||||||
|
/* One of key_length or key_length_offset must be given */
|
||||||
|
/* Key length of 0 isn't allowed */
|
||||||
|
|
||||||
|
#include "mysys_priv.h"
|
||||||
|
#include <m_string.h>
|
||||||
|
#include <m_ctype.h>
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#define NO_RECORD ((uint) -1)
|
||||||
|
#define LOWFIND 1
|
||||||
|
#define LOWUSED 2
|
||||||
|
#define HIGHFIND 4
|
||||||
|
#define HIGHUSED 8
|
||||||
|
|
||||||
|
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength);
|
||||||
|
static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink);
|
||||||
|
static uint calc_hashnr(const uchar *key,uint length);
|
||||||
|
static uint calc_hashnr_caseup(const uchar *key,uint length);
|
||||||
|
static int hashcmp(HASH *hash,HASH_LINK *pos,const uchar *key,uint length);
|
||||||
|
|
||||||
|
|
||||||
|
my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length,
|
||||||
|
hash_get_key get_key,
|
||||||
|
void (*free_element)(void*),uint flags CALLER_INFO_PROTO)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("hash_init");
|
||||||
|
DBUG_PRINT("enter",("hash: %lx size: %d",hash,size));
|
||||||
|
|
||||||
|
hash->records=0;
|
||||||
|
if (my_init_dynamic_array_ci(&hash->array,sizeof(HASH_LINK),size,0))
|
||||||
|
{
|
||||||
|
hash->free=0; /* Allow call to hash_free */
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
hash->key_offset=key_offset;
|
||||||
|
hash->key_length=key_length;
|
||||||
|
hash->blength=1;
|
||||||
|
hash->current_record= NO_RECORD; /* For the future */
|
||||||
|
hash->get_key=get_key;
|
||||||
|
hash->free=free_element;
|
||||||
|
hash->flags=flags;
|
||||||
|
if (flags & HASH_CASE_INSENSITIVE)
|
||||||
|
hash->calc_hashnr=calc_hashnr_caseup;
|
||||||
|
else
|
||||||
|
hash->calc_hashnr=calc_hashnr;
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hash_free(HASH *hash)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("hash_free");
|
||||||
|
if (hash->free)
|
||||||
|
{
|
||||||
|
uint i,records;
|
||||||
|
HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
|
||||||
|
for (i=0,records=hash->records ; i < records ; i++)
|
||||||
|
(*hash->free)(data[i].data);
|
||||||
|
hash->free=0;
|
||||||
|
}
|
||||||
|
delete_dynamic(&hash->array);
|
||||||
|
hash->records=0;
|
||||||
|
DBUG_VOID_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* some helper functions */
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function is char* instead of uchar* as HPUX11 compiler can't
|
||||||
|
handle inline functions that are not defined as native types
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline char*
|
||||||
|
hash_key(HASH *hash,const uchar *record,uint *length,my_bool first)
|
||||||
|
{
|
||||||
|
if (hash->get_key)
|
||||||
|
return (*hash->get_key)(record,length,first);
|
||||||
|
*length=hash->key_length;
|
||||||
|
return (uchar*) record+hash->key_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate pos according to keys */
|
||||||
|
|
||||||
|
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
|
||||||
|
{
|
||||||
|
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
|
||||||
|
return (hashnr & ((buffmax >> 1) -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint hash_rec_mask(HASH *hash,HASH_LINK *pos,uint buffmax,
|
||||||
|
uint maxlength)
|
||||||
|
{
|
||||||
|
uint length;
|
||||||
|
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
|
||||||
|
return hash_mask((*hash->calc_hashnr)(key,length),buffmax,maxlength);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NEW_HASH_FUNCTION
|
||||||
|
|
||||||
|
/* Calc hashvalue for a key */
|
||||||
|
|
||||||
|
static uint calc_hashnr(const uchar *key,uint length)
|
||||||
|
{
|
||||||
|
register uint nr=1, nr2=4;
|
||||||
|
while (length--)
|
||||||
|
{
|
||||||
|
nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8);
|
||||||
|
nr2+=3;
|
||||||
|
}
|
||||||
|
return((uint) nr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calc hashvalue for a key, case indepenently */
|
||||||
|
|
||||||
|
static uint calc_hashnr_caseup(const uchar *key,uint length)
|
||||||
|
{
|
||||||
|
register uint nr=1, nr2=4;
|
||||||
|
while (length--)
|
||||||
|
{
|
||||||
|
nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8);
|
||||||
|
nr2+=3;
|
||||||
|
}
|
||||||
|
return((uint) nr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fowler/Noll/Vo hash
|
||||||
|
*
|
||||||
|
* The basis of the hash algorithm was taken from an idea sent by email to the
|
||||||
|
* IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and
|
||||||
|
* Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com)
|
||||||
|
* later improved on their algorithm.
|
||||||
|
*
|
||||||
|
* The magic is in the interesting relationship between the special prime
|
||||||
|
* 16777619 (2^24 + 403) and 2^32 and 2^8.
|
||||||
|
*
|
||||||
|
* This hash produces the fewest collisions of any function that we've seen so
|
||||||
|
* far, and works well on both numbers and strings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint calc_hashnr(const uchar *key, uint len)
|
||||||
|
{
|
||||||
|
const uchar *end=key+len;
|
||||||
|
uint hash;
|
||||||
|
for (hash = 0; key < end; key++)
|
||||||
|
{
|
||||||
|
hash *= 16777619;
|
||||||
|
hash ^= (uint) *(uchar*) key;
|
||||||
|
}
|
||||||
|
return (hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint calc_hashnr_caseup(const uchar *key, uint len)
|
||||||
|
{
|
||||||
|
const uchar *end=key+len;
|
||||||
|
uint hash;
|
||||||
|
for (hash = 0; key < end; key++)
|
||||||
|
{
|
||||||
|
hash *= 16777619;
|
||||||
|
hash ^= (uint) (uchar) toupper(*key);
|
||||||
|
}
|
||||||
|
return (hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __SUNPRO_C /* SUNPRO can't handle this */
|
||||||
|
inline
|
||||||
|
#endif
|
||||||
|
unsigned int rec_hashnr(HASH *hash,const uchar *record)
|
||||||
|
{
|
||||||
|
uint length;
|
||||||
|
uchar *key= (uchar*) hash_key(hash,record,&length,0);
|
||||||
|
return (*hash->calc_hashnr)(key,length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Search after a record based on a key */
|
||||||
|
/* Sets info->current_ptr to found record */
|
||||||
|
|
||||||
|
gptr hash_search(HASH *hash,const uchar *key,uint length)
|
||||||
|
{
|
||||||
|
HASH_LINK *pos;
|
||||||
|
uint flag,idx;
|
||||||
|
DBUG_ENTER("hash_search");
|
||||||
|
|
||||||
|
flag=1;
|
||||||
|
if (hash->records)
|
||||||
|
{
|
||||||
|
idx=hash_mask((*hash->calc_hashnr)(key,length ? length :
|
||||||
|
hash->key_length),
|
||||||
|
hash->blength,hash->records);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
pos= dynamic_element(&hash->array,idx,HASH_LINK*);
|
||||||
|
if (!hashcmp(hash,pos,key,length))
|
||||||
|
{
|
||||||
|
DBUG_PRINT("exit",("found key at %d",idx));
|
||||||
|
hash->current_record= idx;
|
||||||
|
DBUG_RETURN (pos->data);
|
||||||
|
}
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
flag=0; /* Reset flag */
|
||||||
|
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
|
||||||
|
break; /* Wrong link */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ((idx=pos->next) != NO_RECORD);
|
||||||
|
}
|
||||||
|
hash->current_record= NO_RECORD;
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get next record with identical key */
|
||||||
|
/* Can only be called if previous calls was hash_search */
|
||||||
|
|
||||||
|
gptr hash_next(HASH *hash,const uchar *key,uint length)
|
||||||
|
{
|
||||||
|
HASH_LINK *pos;
|
||||||
|
uint idx;
|
||||||
|
|
||||||
|
if (hash->current_record != NO_RECORD)
|
||||||
|
{
|
||||||
|
HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
|
||||||
|
for (idx=data[hash->current_record].next; idx != NO_RECORD ; idx=pos->next)
|
||||||
|
{
|
||||||
|
pos=data+idx;
|
||||||
|
if (!hashcmp(hash,pos,key,length))
|
||||||
|
{
|
||||||
|
hash->current_record= idx;
|
||||||
|
return pos->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash->current_record=NO_RECORD;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Change link from pos to new_link */
|
||||||
|
|
||||||
|
static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink)
|
||||||
|
{
|
||||||
|
HASH_LINK *old_link;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
old_link=array+next_link;
|
||||||
|
}
|
||||||
|
while ((next_link=old_link->next) != find);
|
||||||
|
old_link->next= newlink;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare a key in a record to a whole key. Return 0 if identical */
|
||||||
|
|
||||||
|
static int hashcmp(HASH *hash,HASH_LINK *pos,const uchar *key,uint length)
|
||||||
|
{
|
||||||
|
uint rec_keylength;
|
||||||
|
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
|
||||||
|
return (length && length != rec_keylength) ||
|
||||||
|
memcmp(rec_key,key,rec_keylength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Write a hash-key to the hash-index */
|
||||||
|
|
||||||
|
my_bool hash_insert(HASH *info,const uchar *record)
|
||||||
|
{
|
||||||
|
int flag;
|
||||||
|
uint halfbuff,hash_nr,first_index,idx;
|
||||||
|
uchar *ptr_to_rec,*ptr_to_rec2;
|
||||||
|
HASH_LINK *data,*empty,*gpos,*gpos2,*pos;
|
||||||
|
|
||||||
|
LINT_INIT(gpos); LINT_INIT(gpos2);
|
||||||
|
LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
|
||||||
|
|
||||||
|
flag=0;
|
||||||
|
if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
|
||||||
|
return(TRUE); /* No more memory */
|
||||||
|
|
||||||
|
info->current_record= NO_RECORD;
|
||||||
|
data=dynamic_element(&info->array,0,HASH_LINK*);
|
||||||
|
halfbuff= info->blength >> 1;
|
||||||
|
|
||||||
|
idx=first_index=info->records-halfbuff;
|
||||||
|
if (idx != info->records) /* If some records */
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
pos=data+idx;
|
||||||
|
hash_nr=rec_hashnr(info,pos->data);
|
||||||
|
if (flag == 0) /* First loop; Check if ok */
|
||||||
|
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
|
||||||
|
break;
|
||||||
|
if (!(hash_nr & halfbuff))
|
||||||
|
{ /* Key will not move */
|
||||||
|
if (!(flag & LOWFIND))
|
||||||
|
{
|
||||||
|
if (flag & HIGHFIND)
|
||||||
|
{
|
||||||
|
flag=LOWFIND | HIGHFIND;
|
||||||
|
/* key shall be moved to the current empty position */
|
||||||
|
gpos=empty;
|
||||||
|
ptr_to_rec=pos->data;
|
||||||
|
empty=pos; /* This place is now free */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flag=LOWFIND | LOWUSED; /* key isn't changed */
|
||||||
|
gpos=pos;
|
||||||
|
ptr_to_rec=pos->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(flag & LOWUSED))
|
||||||
|
{
|
||||||
|
/* Change link of previous LOW-key */
|
||||||
|
gpos->data=ptr_to_rec;
|
||||||
|
gpos->next=(uint) (pos-data);
|
||||||
|
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
|
||||||
|
}
|
||||||
|
gpos=pos;
|
||||||
|
ptr_to_rec=pos->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* key will be moved */
|
||||||
|
if (!(flag & HIGHFIND))
|
||||||
|
{
|
||||||
|
flag= (flag & LOWFIND) | HIGHFIND;
|
||||||
|
/* key shall be moved to the last (empty) position */
|
||||||
|
gpos2 = empty; empty=pos;
|
||||||
|
ptr_to_rec2=pos->data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(flag & HIGHUSED))
|
||||||
|
{
|
||||||
|
/* Change link of previous hash-key and save */
|
||||||
|
gpos2->data=ptr_to_rec2;
|
||||||
|
gpos2->next=(uint) (pos-data);
|
||||||
|
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
|
||||||
|
}
|
||||||
|
gpos2=pos;
|
||||||
|
ptr_to_rec2=pos->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ((idx=pos->next) != NO_RECORD);
|
||||||
|
|
||||||
|
if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
|
||||||
|
{
|
||||||
|
gpos->data=ptr_to_rec;
|
||||||
|
gpos->next=NO_RECORD;
|
||||||
|
}
|
||||||
|
if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
|
||||||
|
{
|
||||||
|
gpos2->data=ptr_to_rec2;
|
||||||
|
gpos2->next=NO_RECORD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check if we are at the empty position */
|
||||||
|
|
||||||
|
idx=hash_mask(rec_hashnr(info,record),info->blength,info->records+1);
|
||||||
|
pos=data+idx;
|
||||||
|
if (pos == empty)
|
||||||
|
{
|
||||||
|
pos->data=(uchar*) record;
|
||||||
|
pos->next=NO_RECORD;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Check if more records in same hash-nr family */
|
||||||
|
empty[0]=pos[0];
|
||||||
|
gpos=data+hash_rec_mask(info,pos,info->blength,info->records+1);
|
||||||
|
if (pos == gpos)
|
||||||
|
{
|
||||||
|
pos->data=(uchar*) record;
|
||||||
|
pos->next=(uint) (empty - data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos->data=(uchar*) record;
|
||||||
|
pos->next=NO_RECORD;
|
||||||
|
movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (++info->records == info->blength)
|
||||||
|
info->blength+= info->blength;
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
** Remove one record from hash-table. The record with the same record
|
||||||
|
** ptr is removed.
|
||||||
|
** if there is a free-function it's called for record if found
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
my_bool hash_delete(HASH *hash,uchar *record)
|
||||||
|
{
|
||||||
|
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
|
||||||
|
HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
|
||||||
|
DBUG_ENTER("hash_delete");
|
||||||
|
if (!hash->records)
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
|
blength=hash->blength;
|
||||||
|
data=dynamic_element(&hash->array,0,HASH_LINK*);
|
||||||
|
/* Search after record with key */
|
||||||
|
pos=data+ hash_mask(rec_hashnr(hash,record),blength,hash->records);
|
||||||
|
gpos = 0;
|
||||||
|
|
||||||
|
while (pos->data != record)
|
||||||
|
{
|
||||||
|
gpos=pos;
|
||||||
|
if (pos->next == NO_RECORD)
|
||||||
|
DBUG_RETURN(1); /* Key not found */
|
||||||
|
pos=data+pos->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
|
||||||
|
hash->current_record= NO_RECORD;
|
||||||
|
lastpos=data+hash->records;
|
||||||
|
|
||||||
|
/* Remove link to record */
|
||||||
|
empty=pos; empty_index=(uint) (empty-data);
|
||||||
|
if (gpos)
|
||||||
|
gpos->next=pos->next; /* unlink current ptr */
|
||||||
|
else if (pos->next != NO_RECORD)
|
||||||
|
{
|
||||||
|
empty=data+(empty_index=pos->next);
|
||||||
|
pos->data=empty->data;
|
||||||
|
pos->next=empty->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty == lastpos) /* last key at wrong pos or no next link */
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* Move the last key (lastpos) */
|
||||||
|
lastpos_hashnr=rec_hashnr(hash,lastpos->data);
|
||||||
|
/* pos is where lastpos should be */
|
||||||
|
pos=data+hash_mask(lastpos_hashnr,hash->blength,hash->records);
|
||||||
|
if (pos == empty) /* Move to empty position. */
|
||||||
|
{
|
||||||
|
empty[0]=lastpos[0];
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
pos_hashnr=rec_hashnr(hash,pos->data);
|
||||||
|
/* pos3 is where the pos should be */
|
||||||
|
pos3= data+hash_mask(pos_hashnr,hash->blength,hash->records);
|
||||||
|
if (pos != pos3)
|
||||||
|
{ /* pos is on wrong posit */
|
||||||
|
empty[0]=pos[0]; /* Save it here */
|
||||||
|
pos[0]=lastpos[0]; /* This should be here */
|
||||||
|
movelink(data,(uint) (pos-data),(uint) (pos3-data),empty_index);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
pos2= hash_mask(lastpos_hashnr,blength,hash->records+1);
|
||||||
|
if (pos2 == hash_mask(pos_hashnr,blength,hash->records+1))
|
||||||
|
{ /* Identical key-positions */
|
||||||
|
if (pos2 != hash->records)
|
||||||
|
{
|
||||||
|
empty[0]=lastpos[0];
|
||||||
|
movelink(data,(uint) (lastpos-data),(uint) (pos-data),empty_index);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
idx= (uint) (pos-data); /* Link pos->next after lastpos */
|
||||||
|
}
|
||||||
|
else idx= NO_RECORD; /* Different positions merge */
|
||||||
|
|
||||||
|
empty[0]=lastpos[0];
|
||||||
|
movelink(data,idx,empty_index,pos->next);
|
||||||
|
pos->next=empty_index;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
VOID(pop_dynamic(&hash->array));
|
||||||
|
if (hash->free)
|
||||||
|
(*hash->free)((uchar*) record);
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Update keys when record has changed.
|
||||||
|
This is much more efficent than using a delete & insert.
|
||||||
|
*/
|
||||||
|
|
||||||
|
my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,uint old_key_length)
|
||||||
|
{
|
||||||
|
uint idx,new_index,new_pos_index,blength,records,empty;
|
||||||
|
HASH_LINK org_link,*data,*previous,*pos;
|
||||||
|
DBUG_ENTER("hash_update");
|
||||||
|
|
||||||
|
data=dynamic_element(&hash->array,0,HASH_LINK*);
|
||||||
|
blength=hash->blength; records=hash->records;
|
||||||
|
|
||||||
|
/* Search after record with key */
|
||||||
|
|
||||||
|
idx=hash_mask((*hash->calc_hashnr)(old_key,(old_key_length ?
|
||||||
|
old_key_length :
|
||||||
|
hash->key_length)),
|
||||||
|
blength,records);
|
||||||
|
new_index=hash_mask(rec_hashnr(hash,record),blength,records);
|
||||||
|
if (idx == new_index)
|
||||||
|
DBUG_RETURN(0); /* Nothing to do (No record check) */
|
||||||
|
previous=0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ((pos= data+idx)->data == record)
|
||||||
|
break;
|
||||||
|
previous=pos;
|
||||||
|
if ((idx=pos->next) == NO_RECORD)
|
||||||
|
DBUG_RETURN(1); /* Not found in links */
|
||||||
|
}
|
||||||
|
hash->current_record= NO_RECORD;
|
||||||
|
org_link= *pos;
|
||||||
|
empty=idx;
|
||||||
|
|
||||||
|
/* Relink record from current chain */
|
||||||
|
|
||||||
|
if (!previous)
|
||||||
|
{
|
||||||
|
if (pos->next != NO_RECORD)
|
||||||
|
{
|
||||||
|
empty=pos->next;
|
||||||
|
*pos= data[pos->next];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
previous->next=pos->next; /* unlink pos */
|
||||||
|
|
||||||
|
/* Move data to correct position */
|
||||||
|
pos=data+new_index;
|
||||||
|
new_pos_index=hash_rec_mask(hash,pos,blength,records);
|
||||||
|
if (new_index != new_pos_index)
|
||||||
|
{ /* Other record in wrong position */
|
||||||
|
data[empty] = *pos;
|
||||||
|
movelink(data,new_index,new_pos_index,empty);
|
||||||
|
org_link.next=NO_RECORD;
|
||||||
|
data[new_index]= org_link;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* Link in chain at right position */
|
||||||
|
org_link.next=data[new_index].next;
|
||||||
|
data[empty]=org_link;
|
||||||
|
data[new_index].next=empty;
|
||||||
|
}
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uchar *hash_element(HASH *hash,uint idx)
|
||||||
|
{
|
||||||
|
if (idx < hash->records)
|
||||||
|
return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
|
||||||
|
my_bool hash_check(HASH *hash)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
uint i,rec_link,found,max_links,seek,links,idx;
|
||||||
|
uint records,blength;
|
||||||
|
HASH_LINK *data,*hash_info;
|
||||||
|
|
||||||
|
records=hash->records; blength=hash->blength;
|
||||||
|
data=dynamic_element(&hash->array,0,HASH_LINK*);
|
||||||
|
error=0;
|
||||||
|
|
||||||
|
for (i=found=max_links=seek=0 ; i < records ; i++)
|
||||||
|
{
|
||||||
|
if (hash_rec_mask(hash,data+i,blength,records) == i)
|
||||||
|
{
|
||||||
|
found++; seek++; links=1;
|
||||||
|
for (idx=data[i].next ;
|
||||||
|
idx != NO_RECORD && found < records + 1;
|
||||||
|
idx=hash_info->next)
|
||||||
|
{
|
||||||
|
if (idx >= records)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("error",
|
||||||
|
("Found pointer outside array to %d from link starting at %d",
|
||||||
|
idx,i));
|
||||||
|
error=1;
|
||||||
|
}
|
||||||
|
hash_info=data+idx;
|
||||||
|
seek+= ++links;
|
||||||
|
if ((rec_link=hash_rec_mask(hash,hash_info,blength,records)) != i)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("error",
|
||||||
|
("Record in wrong link at %d: Start %d Record: %lx Record-link %d", idx,i,hash_info->data,rec_link));
|
||||||
|
error=1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
if (links > max_links) max_links=links;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found != records)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("error",("Found %ld of %ld records"));
|
||||||
|
error=1;
|
||||||
|
}
|
||||||
|
if (records)
|
||||||
|
DBUG_PRINT("info",
|
||||||
|
("records: %ld seeks: %d max links: %d hitrate: %.2f",
|
||||||
|
records,seek,max_links,(float) seek / (float) records));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
#endif
|
@@ -66,6 +66,7 @@
|
|||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <ma_dyncol.h>
|
||||||
|
|
||||||
static my_bool mysql_client_init=0;
|
static my_bool mysql_client_init=0;
|
||||||
static void mysql_close_options(MYSQL *mysql);
|
static void mysql_close_options(MYSQL *mysql);
|
||||||
@@ -86,6 +87,7 @@ extern int mthd_stmt_fetch_row(MYSQL_STMT *stmt, unsigned char **row);
|
|||||||
extern int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row);
|
extern int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row);
|
||||||
extern int mthd_stmt_read_all_rows(MYSQL_STMT *stmt);
|
extern int mthd_stmt_read_all_rows(MYSQL_STMT *stmt);
|
||||||
extern void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt);
|
extern void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt);
|
||||||
|
extern unsigned char *mysql_net_store_length(unsigned char *packet, size_t length);
|
||||||
|
|
||||||
uint mysql_port=0;
|
uint mysql_port=0;
|
||||||
my_string mysql_unix_port=0;
|
my_string mysql_unix_port=0;
|
||||||
@@ -829,11 +831,14 @@ enum option_val
|
|||||||
OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type
|
OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type
|
||||||
};
|
};
|
||||||
|
|
||||||
#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \
|
#define CHECK_OPT_EXTENSION_SET(OPTS)\
|
||||||
if (!(OPTS)->extension) \
|
if (!(OPTS)->extension) \
|
||||||
(OPTS)->extension= (struct st_mysql_options_extention *) \
|
(OPTS)->extension= (struct st_mysql_options_extention *) \
|
||||||
my_malloc(sizeof(struct st_mysql_options_extention), \
|
my_malloc(sizeof(struct st_mysql_options_extention), \
|
||||||
MYF(MY_WME | MY_ZEROFILL)); \
|
MYF(MY_WME | MY_ZEROFILL)); \
|
||||||
|
|
||||||
|
#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \
|
||||||
|
CHECK_OPT_EXTENSION_SET(OPTS) \
|
||||||
if (IS_STRING) { \
|
if (IS_STRING) { \
|
||||||
my_free((char *)(OPTS)->extension->KEY, MYF(MY_ALLOW_ZERO_PTR)); \
|
my_free((char *)(OPTS)->extension->KEY, MYF(MY_ALLOW_ZERO_PTR)); \
|
||||||
(OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME)); \
|
(OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME)); \
|
||||||
@@ -1382,6 +1387,70 @@ mysql_connect(MYSQL *mysql,const char *host,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer)
|
||||||
|
{
|
||||||
|
if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS)
|
||||||
|
{
|
||||||
|
buffer= mysql_net_store_length((unsigned char *)buffer, (mysql->options.extension) ?
|
||||||
|
mysql->options.extension->connect_attrs_len : 0);
|
||||||
|
if (mysql->options.extension &&
|
||||||
|
hash_inited(&mysql->options.extension->connect_attrs))
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < mysql->options.extension->connect_attrs.records; i++)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
uchar *p= hash_element(&mysql->options.extension->connect_attrs, i);
|
||||||
|
|
||||||
|
len= *(size_t *)p;
|
||||||
|
buffer= mysql_net_store_length(buffer, len);
|
||||||
|
p+= sizeof(size_t);
|
||||||
|
memcpy(buffer, p, len);
|
||||||
|
buffer+= len;
|
||||||
|
p+= len;
|
||||||
|
len= *(size_t *)p;
|
||||||
|
buffer= mysql_net_store_length(buffer, len);
|
||||||
|
p+= sizeof(size_t);
|
||||||
|
memcpy(buffer, p, len);
|
||||||
|
buffer+= len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set some default attributes */
|
||||||
|
static my_bool
|
||||||
|
ma_set_connect_attrs(MYSQL *mysql)
|
||||||
|
{
|
||||||
|
char buffer[255];
|
||||||
|
int rc= 0;
|
||||||
|
|
||||||
|
rc= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_name") +
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_version") +
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_os") +
|
||||||
|
#ifdef _WIN32
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_thread") +
|
||||||
|
#endif
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_pid") +
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_platform");
|
||||||
|
|
||||||
|
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "libmariadb");
|
||||||
|
+ mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_version", MARIADB_PACKAGE_VERSION);
|
||||||
|
+ mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_os", MARIADB_SYSTEM_TYPE);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
snprintf(buffer, 255, "%lu", (ulong) GetCurrentThreadId());
|
||||||
|
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buffer);
|
||||||
|
snprintf(buffer, 255, "%lu", (ulong) GetCurrentProcessId());
|
||||||
|
#else
|
||||||
|
snprintf(buffer, 255, "%lu", (ulong) getpid());
|
||||||
|
#endif
|
||||||
|
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buffer);
|
||||||
|
|
||||||
|
rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_platform", MARIADB_MACHINE_TYPE);
|
||||||
|
return(test(rc>0));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Note that the mysql argument must be initialized with mysql_init()
|
** Note that the mysql argument must be initialized with mysql_init()
|
||||||
@@ -1426,6 +1495,8 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
db ? db : "(Null)",
|
db ? db : "(Null)",
|
||||||
user ? user : "(Null)"));
|
user ? user : "(Null)"));
|
||||||
|
|
||||||
|
ma_set_connect_attrs(mysql);
|
||||||
|
|
||||||
if (net->vio) /* check if we are already connected */
|
if (net->vio) /* check if we are already connected */
|
||||||
{
|
{
|
||||||
SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0);
|
SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0);
|
||||||
@@ -2047,6 +2118,8 @@ static void mysql_close_options(MYSQL *mysql)
|
|||||||
my_free((gptr)mysql->options.extension->db_driver, MYF(MY_ALLOW_ZERO_PTR));
|
my_free((gptr)mysql->options.extension->db_driver, MYF(MY_ALLOW_ZERO_PTR));
|
||||||
my_free(mysql->options.extension->ssl_crl, MYF(MY_ALLOW_ZERO_PTR));
|
my_free(mysql->options.extension->ssl_crl, MYF(MY_ALLOW_ZERO_PTR));
|
||||||
my_free(mysql->options.extension->ssl_crlpath, MYF(MY_ALLOW_ZERO_PTR));
|
my_free(mysql->options.extension->ssl_crlpath, MYF(MY_ALLOW_ZERO_PTR));
|
||||||
|
if(hash_inited(&mysql->options.extension->connect_attrs))
|
||||||
|
hash_free(&mysql->options.extension->connect_attrs);
|
||||||
}
|
}
|
||||||
my_free((gptr)mysql->options.extension, MYF(MY_ALLOW_ZERO_PTR));
|
my_free((gptr)mysql->options.extension, MYF(MY_ALLOW_ZERO_PTR));
|
||||||
/* clear all pointer */
|
/* clear all pointer */
|
||||||
@@ -2664,6 +2737,16 @@ mysql_get_client_info(void)
|
|||||||
return (char*) MYSQL_CLIENT_VERSION;
|
return (char*) MYSQL_CLIENT_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t get_store_length(size_t length)
|
||||||
|
{
|
||||||
|
if (length < (size_t) L64(251))
|
||||||
|
return 1;
|
||||||
|
if (length < (size_t) L64(65536))
|
||||||
|
return 2;
|
||||||
|
if (length < (size_t) L64(16777216))
|
||||||
|
return 3;
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
int STDCALL
|
int STDCALL
|
||||||
mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
|
mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
|
||||||
@@ -2797,12 +2880,127 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
|
|||||||
case MYSQL_OPT_SSL_CRLPATH:
|
case MYSQL_OPT_SSL_CRLPATH:
|
||||||
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crlpath, (char *)arg, 1);
|
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crlpath, (char *)arg, 1);
|
||||||
break;
|
break;
|
||||||
|
case MYSQL_OPT_CONNECT_ATTR_DELETE:
|
||||||
|
{
|
||||||
|
uchar *p;
|
||||||
|
CHECK_OPT_EXTENSION_SET(&mysql->options);
|
||||||
|
if (hash_inited(&mysql->options.extension->connect_attrs) &&
|
||||||
|
(p= hash_search(&mysql->options.extension->connect_attrs, arg,
|
||||||
|
arg ? strlen(arg) : 0)))
|
||||||
|
{
|
||||||
|
size_t key_len= *(size_t *)p;
|
||||||
|
mysql->options.extension->connect_attrs_len-= key_len;
|
||||||
|
mysql->options.extension->connect_attrs_len-= get_store_length(key_len);
|
||||||
|
key_len= *(size_t *)(p + sizeof(size_t) + key_len);
|
||||||
|
mysql->options.extension->connect_attrs_len-= key_len;
|
||||||
|
mysql->options.extension->connect_attrs_len-= get_store_length(key_len);
|
||||||
|
hash_delete(&mysql->options.extension->connect_attrs, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MYSQL_OPT_CONNECT_ATTR_RESET:
|
||||||
|
CHECK_OPT_EXTENSION_SET(&mysql->options);
|
||||||
|
if (hash_inited(&mysql->options.extension->connect_attrs))
|
||||||
|
{
|
||||||
|
hash_free(&mysql->options.extension->connect_attrs);
|
||||||
|
mysql->options.extension->connect_attrs_len= 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uchar *ma_get_hash_key(const uchar *hash_entry,
|
||||||
|
size_t *length,
|
||||||
|
my_bool not_used __attribute__((unused)))
|
||||||
|
{
|
||||||
|
/* Hash entry has the following format:
|
||||||
|
Offset: 0 key length
|
||||||
|
sizeof(size_t) key
|
||||||
|
key_length +
|
||||||
|
sizeof(size_t) value length
|
||||||
|
value
|
||||||
|
*/
|
||||||
|
uchar *p= (uchar *)hash_entry;
|
||||||
|
*length=*((size_t*)p);
|
||||||
|
p+= sizeof(size_t);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ma_hash_free(void *p)
|
||||||
|
{
|
||||||
|
my_free(p, MYF(MYF_ALLOW_ZERO_PTR));
|
||||||
|
}
|
||||||
|
|
||||||
|
int STDCALL mysql_options4(MYSQL *mysql,enum mysql_option option,
|
||||||
|
const void *arg1, const void *arg2)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("mysql_option4");
|
||||||
|
DBUG_PRINT("enter",("option: %d",(int) option));
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
case MYSQL_OPT_CONNECT_ATTR_ADD:
|
||||||
|
{
|
||||||
|
uchar *buffer;
|
||||||
|
size_t key_len= arg1 ? strlen(arg1) : 0,
|
||||||
|
value_len= arg2 ? strlen(arg2) : 0;
|
||||||
|
size_t storage_len= key_len + value_len +
|
||||||
|
get_store_length(key_len) +
|
||||||
|
get_store_length(value_len);
|
||||||
|
|
||||||
|
CHECK_OPT_EXTENSION_SET(&mysql->options);
|
||||||
|
if (!key_len ||
|
||||||
|
storage_len + mysql->options.extension->connect_attrs_len > 0xFFFF)
|
||||||
|
{
|
||||||
|
SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate, 0);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hash_inited(&mysql->options.extension->connect_attrs))
|
||||||
|
{
|
||||||
|
if (_hash_init(&mysql->options.extension->connect_attrs,
|
||||||
|
0, 0, 0, ma_get_hash_key, ma_hash_free, 0))
|
||||||
|
{
|
||||||
|
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((buffer= (uchar *)my_malloc(2 * sizeof(size_t) + key_len + value_len,
|
||||||
|
MYF(MY_WME | MY_ZEROFILL))))
|
||||||
|
{
|
||||||
|
uchar *p= buffer;
|
||||||
|
*((size_t *)p)= key_len;
|
||||||
|
p+= sizeof(size_t);
|
||||||
|
memcpy(p, arg1, key_len);
|
||||||
|
p+= key_len;
|
||||||
|
*((size_t *)p)= value_len;
|
||||||
|
p+= sizeof(size_t);
|
||||||
|
if (arg2)
|
||||||
|
memcpy(p, arg2, value_len);
|
||||||
|
|
||||||
|
if (hash_insert(&mysql->options.extension->connect_attrs, buffer))
|
||||||
|
{
|
||||||
|
my_free(buffer, MYF(0));
|
||||||
|
SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate, 0);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
mysql->options.extension->connect_attrs_len+= storage_len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
** Functions to get information from the MySQL structure
|
** Functions to get information from the MySQL structure
|
||||||
** These are functions to make shared libraries more usable.
|
** These are functions to make shared libraries more usable.
|
||||||
|
@@ -53,6 +53,7 @@ EXPORTS
|
|||||||
mysql_num_fields
|
mysql_num_fields
|
||||||
mysql_num_rows
|
mysql_num_rows
|
||||||
mysql_options
|
mysql_options
|
||||||
|
mysql_options4
|
||||||
mysql_stmt_param_count
|
mysql_stmt_param_count
|
||||||
mysql_stmt_param_metadata
|
mysql_stmt_param_metadata
|
||||||
mysql_ping
|
mysql_ping
|
||||||
|
@@ -14,6 +14,7 @@ static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t
|
|||||||
static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
|
static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
|
||||||
static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
|
static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
|
||||||
extern void read_user_name(char *name);
|
extern void read_user_name(char *name);
|
||||||
|
extern uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer);
|
||||||
|
|
||||||
#define compile_time_assert(A) \
|
#define compile_time_assert(A) \
|
||||||
do {\
|
do {\
|
||||||
@@ -61,7 +62,6 @@ struct st_mysql_client_plugin *mysql_client_builtins[]=
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
/* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int (*read_packet)(struct st_plugin_vio *vio, uchar **buf);
|
int (*read_packet)(struct st_plugin_vio *vio, uchar **buf);
|
||||||
int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, size_t pkt_len);
|
int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, size_t pkt_len);
|
||||||
@@ -170,34 +170,16 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
|
|||||||
return CR_OK;
|
return CR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
sends a COM_CHANGE_USER command with a caller provided payload
|
|
||||||
|
|
||||||
Packet format:
|
|
||||||
|
|
||||||
Bytes Content
|
|
||||||
----- ----
|
|
||||||
n user name - \0-terminated string
|
|
||||||
n password
|
|
||||||
3.23 scramble - \0-terminated string (9 bytes)
|
|
||||||
otherwise - length (1 byte) coded
|
|
||||||
n database name - \0-terminated string
|
|
||||||
2 character set number (if the server >= 4.1.x)
|
|
||||||
n client auth plugin name - \0-terminated string,
|
|
||||||
(if the server supports plugin auth)
|
|
||||||
|
|
||||||
@retval 0 ok
|
|
||||||
@retval 1 error
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int send_change_user_packet(MCPVIO_EXT *mpvio,
|
static int send_change_user_packet(MCPVIO_EXT *mpvio,
|
||||||
const uchar *data, int data_len)
|
const uchar *data, int data_len)
|
||||||
{
|
{
|
||||||
MYSQL *mysql= mpvio->mysql;
|
MYSQL *mysql= mpvio->mysql;
|
||||||
char *buff, *end;
|
char *buff, *end;
|
||||||
int res= 1;
|
int res= 1;
|
||||||
|
size_t conn_attr_len= (mysql->options.extension) ?
|
||||||
|
mysql->options.extension->connect_attrs_len : 0;
|
||||||
|
|
||||||
buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1);
|
buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1 + 9 + conn_attr_len);
|
||||||
|
|
||||||
end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
|
end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
|
||||||
|
|
||||||
@@ -234,6 +216,8 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio,
|
|||||||
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
|
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
|
||||||
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
|
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
|
||||||
|
|
||||||
|
end= ma_send_connect_attr(mysql, end);
|
||||||
|
|
||||||
res= simple_command(mysql, MYSQL_COM_CHANGE_USER,
|
res= simple_command(mysql, MYSQL_COM_CHANGE_USER,
|
||||||
buff, (ulong)(end-buff), 1);
|
buff, (ulong)(end-buff), 1);
|
||||||
|
|
||||||
@@ -243,36 +227,6 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
sends a client authentication packet (second packet in the 3-way handshake)
|
|
||||||
|
|
||||||
Packet format (when the server is 4.0 or earlier):
|
|
||||||
|
|
||||||
Bytes Content
|
|
||||||
----- ----
|
|
||||||
2 client capabilities
|
|
||||||
3 max packet size
|
|
||||||
n user name, \0-terminated
|
|
||||||
9 scramble_323, \0-terminated
|
|
||||||
|
|
||||||
Packet format (when the server is 4.1 or newer):
|
|
||||||
|
|
||||||
Bytes Content
|
|
||||||
----- ----
|
|
||||||
4 client capabilities
|
|
||||||
4 max packet size
|
|
||||||
1 charset number
|
|
||||||
23 reserved (always 0)
|
|
||||||
n user name, \0-terminated
|
|
||||||
n plugin auth data (e.g. scramble), length (1 byte) coded
|
|
||||||
n database name, \0-terminated
|
|
||||||
(if CLIENT_CONNECT_WITH_DB is set in the capabilities)
|
|
||||||
n client auth plugin name - \0-terminated string,
|
|
||||||
(if CLIENT_PLUGIN_AUTH is set in the capabilities)
|
|
||||||
|
|
||||||
@retval 0 ok
|
|
||||||
@retval 1 error
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||||
const uchar *data, int data_len)
|
const uchar *data, int data_len)
|
||||||
@@ -280,9 +234,11 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
|||||||
MYSQL *mysql= mpvio->mysql;
|
MYSQL *mysql= mpvio->mysql;
|
||||||
NET *net= &mysql->net;
|
NET *net= &mysql->net;
|
||||||
char *buff, *end;
|
char *buff, *end;
|
||||||
|
size_t conn_attr_len= (mysql->options.extension) ?
|
||||||
|
mysql->options.extension->connect_attrs_len : 0;
|
||||||
|
|
||||||
/* see end= buff+32 below, fixed size of the packet is 32 bytes */
|
/* see end= buff+32 below, fixed size of the packet is 32 bytes */
|
||||||
buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN);
|
buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN + conn_attr_len + 9);
|
||||||
|
|
||||||
mysql->client_flag|= mysql->options.client_flag;
|
mysql->client_flag|= mysql->options.client_flag;
|
||||||
mysql->client_flag|= CLIENT_CAPABILITIES;
|
mysql->client_flag|= CLIENT_CAPABILITIES;
|
||||||
@@ -416,6 +372,8 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
|||||||
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
|
if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
|
||||||
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
|
end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
|
||||||
|
|
||||||
|
end= ma_send_connect_attr(mysql, end);
|
||||||
|
|
||||||
/* Write authentication package */
|
/* Write authentication package */
|
||||||
if (my_net_write(net, buff, (size_t) (end-buff)) || net_flush(net))
|
if (my_net_write(net, buff, (size_t) (end-buff)) || net_flush(net))
|
||||||
{
|
{
|
||||||
|
@@ -356,7 +356,7 @@ MYSQL_RES *_mysql_stmt_use_result(MYSQL_STMT *stmt)
|
|||||||
DBUG_RETURN(NULL);
|
DBUG_RETURN(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *mysql_net_store_length(unsigned char *packet, my_ulonglong length)
|
unsigned char *mysql_net_store_length(unsigned char *packet, size_t length)
|
||||||
{
|
{
|
||||||
if (length < (my_ulonglong) L64(251)) {
|
if (length < (my_ulonglong) L64(251)) {
|
||||||
*packet = (unsigned char) length;
|
*packet = (unsigned char) length;
|
||||||
|
@@ -582,7 +582,7 @@ static int test_wl4166_3(MYSQL *mysql)
|
|||||||
rc= mysql_stmt_bind_param(stmt, my_bind);
|
rc= mysql_stmt_bind_param(stmt, my_bind);
|
||||||
check_stmt_rc(rc, stmt);
|
check_stmt_rc(rc, stmt);
|
||||||
|
|
||||||
tm[0].year= 10000;
|
tm[0].year= 2014;
|
||||||
tm[0].month= 1; tm[0].day= 1;
|
tm[0].month= 1; tm[0].day= 1;
|
||||||
tm[0].hour= 1; tm[0].minute= 1; tm[0].second= 1;
|
tm[0].hour= 1; tm[0].minute= 1; tm[0].second= 1;
|
||||||
tm[0].second_part= 0; tm[0].neg= 0;
|
tm[0].second_part= 0; tm[0].neg= 0;
|
||||||
@@ -592,15 +592,10 @@ static int test_wl4166_3(MYSQL *mysql)
|
|||||||
check_mysql_rc(rc, mysql);
|
check_mysql_rc(rc, mysql);
|
||||||
|
|
||||||
rc= mysql_stmt_execute(stmt);
|
rc= mysql_stmt_execute(stmt);
|
||||||
|
diag("rc=%d %s", rc, mysql_stmt_error(stmt));
|
||||||
check_stmt_rc(rc, stmt);
|
check_stmt_rc(rc, stmt);
|
||||||
/*
|
|
||||||
Sic: only one warning, instead of two. The warning
|
|
||||||
about data truncation when assigning a parameter is lost.
|
|
||||||
This is a bug.
|
|
||||||
*/
|
|
||||||
FAIL_IF(mysql_warning_count(mysql) != 1, "warning count != 1");
|
|
||||||
|
|
||||||
if (verify_col_data(mysql, "t1", "year", "0000-00-00 00:00:00")) {
|
if (verify_col_data(mysql, "t1", "year", "2014-01-01 01:01:01")) {
|
||||||
mysql_stmt_close(stmt);
|
mysql_stmt_close(stmt);
|
||||||
rc= mysql_query(mysql, "drop table t1");
|
rc= mysql_query(mysql, "drop table t1");
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -904,7 +899,55 @@ static int test_conc44(MYSQL *mysql)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int test_connect_attrs(MYSQL *my)
|
||||||
|
{
|
||||||
|
MYSQL *mysql;
|
||||||
|
MYSQL_RES *result;
|
||||||
|
int rc, len;
|
||||||
|
|
||||||
|
mysql= mysql_init(NULL);
|
||||||
|
|
||||||
|
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo0", "bar0");
|
||||||
|
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo1", "bar1");
|
||||||
|
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo2", "bar2");
|
||||||
|
|
||||||
|
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
|
||||||
|
port, socketname, 0), mysql_error(my));
|
||||||
|
|
||||||
|
if (!(mysql->server_capabilities & CLIENT_CONNECT_ATTRS))
|
||||||
|
{
|
||||||
|
mysql_close(mysql);
|
||||||
|
diag("Server doesn't support connection attributes");
|
||||||
|
return SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, "SELECT * FROM performance_schema.session_connect_attrs where attr_name like 'foo%'");
|
||||||
|
check_mysql_rc(rc, mysql);
|
||||||
|
result= mysql_store_result(mysql);
|
||||||
|
rc= mysql_num_rows(result);
|
||||||
|
mysql_free_result(result);
|
||||||
|
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, NULL);
|
||||||
|
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo0", "bar0");
|
||||||
|
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo1", "bar1");
|
||||||
|
mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "foo2", "bar2");
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "foo0");
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "foo1");
|
||||||
|
mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "foo2");
|
||||||
|
|
||||||
|
len= mysql->options.extension->connect_attrs_len;
|
||||||
|
|
||||||
|
|
||||||
|
mysql_close(mysql);
|
||||||
|
|
||||||
|
FAIL_IF(rc < 3, "Expected 3 or more rows");
|
||||||
|
FAIL_IF(len != 0, "Expected connection_attr_len=0");
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
struct my_tests_st my_tests[] = {
|
struct my_tests_st my_tests[] = {
|
||||||
|
{"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conc49", test_conc49, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_conc49", test_conc49, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_bug28075", test_bug28075, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_bug28075", test_bug28075, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_bug28505", test_bug28505, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_bug28505", test_bug28505, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
|
Reference in New Issue
Block a user