1
0
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:
Georg Richter
2013-09-28 10:38:56 +02:00
parent b7146e4f84
commit 8bdac9ac53
12 changed files with 1138 additions and 271 deletions

View File

@@ -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)))

View File

@@ -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@"

View File

@@ -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>

View File

@@ -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)

View File

@@ -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@"

View File

@@ -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
View 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

View File

@@ -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.

View File

@@ -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

View File

@@ -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))
{ {

View File

@@ -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;

View File

@@ -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},