aboutsummaryrefslogtreecommitdiff
path: root/mysql/libmariadb
diff options
context:
space:
mode:
Diffstat (limited to 'mysql/libmariadb')
-rw-r--r--mysql/libmariadb/.gitignore2
-rw-r--r--mysql/libmariadb/ma_alloc.c193
-rw-r--r--mysql/libmariadb/ma_array.c172
-rw-r--r--mysql/libmariadb/ma_charset.c1374
-rw-r--r--mysql/libmariadb/ma_client_plugin.c.in483
-rw-r--r--mysql/libmariadb/ma_compress.c90
-rw-r--r--mysql/libmariadb/ma_context.c726
-rw-r--r--mysql/libmariadb/ma_default.c326
-rw-r--r--mysql/libmariadb/ma_dtoa.c1925
-rw-r--r--mysql/libmariadb/ma_errmsg.c170
-rw-r--r--mysql/libmariadb/ma_hash.c583
-rw-r--r--mysql/libmariadb/ma_init.c115
-rw-r--r--mysql/libmariadb/ma_io.c223
-rw-r--r--mysql/libmariadb/ma_list.c114
-rw-r--r--mysql/libmariadb/ma_ll2str.c75
-rw-r--r--mysql/libmariadb/ma_loaddata.c262
-rw-r--r--mysql/libmariadb/ma_net.c588
-rw-r--r--mysql/libmariadb/ma_password.c169
-rw-r--r--mysql/libmariadb/ma_pvio.c582
-rw-r--r--mysql/libmariadb/ma_sha1.c326
-rw-r--r--mysql/libmariadb/ma_stmt_codec.c1081
-rw-r--r--mysql/libmariadb/ma_string.c133
-rw-r--r--mysql/libmariadb/ma_time.c65
-rw-r--r--mysql/libmariadb/ma_tls.c232
-rw-r--r--mysql/libmariadb/mariadb_async.c1946
-rw-r--r--mysql/libmariadb/mariadb_charset.c74
-rw-r--r--mysql/libmariadb/mariadb_dyncol.c4367
-rw-r--r--mysql/libmariadb/mariadb_lib.c4141
-rw-r--r--mysql/libmariadb/mariadb_stmt.c2419
-rw-r--r--mysql/libmariadb/mariadbclient_win32.def.in229
-rw-r--r--mysql/libmariadb/secure/ma_schannel.c1008
-rw-r--r--mysql/libmariadb/secure/ma_schannel.h87
-rw-r--r--mysql/libmariadb/secure/schannel.c586
33 files changed, 24866 insertions, 0 deletions
diff --git a/mysql/libmariadb/.gitignore b/mysql/libmariadb/.gitignore
new file mode 100644
index 0000000..8ac5982
--- /dev/null
+++ b/mysql/libmariadb/.gitignore
@@ -0,0 +1,2 @@
+ma_client_plugin.c
+mariadbclient.def
diff --git a/mysql/libmariadb/ma_alloc.c b/mysql/libmariadb/ma_alloc.c
new file mode 100644
index 0000000..300a107
--- /dev/null
+++ b/mysql/libmariadb/ma_alloc.c
@@ -0,0 +1,193 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Routines to handle mallocing of results which will be freed the same time */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+
+void ma_init_alloc_root(MA_MEM_ROOT *mem_root, size_t block_size, size_t pre_alloc_size)
+{
+ mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
+ mem_root->min_malloc=32;
+ mem_root->block_size= (block_size-MALLOC_OVERHEAD-sizeof(MA_USED_MEM)+8);
+ mem_root->error_handler=0;
+ mem_root->block_num= 4;
+ mem_root->first_block_usage= 0;
+#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
+ if (pre_alloc_size)
+ {
+ if ((mem_root->free = mem_root->pre_alloc=
+ (MA_USED_MEM*) malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(MA_USED_MEM)))))
+ {
+ mem_root->free->size=pre_alloc_size+ALIGN_SIZE(sizeof(MA_USED_MEM));
+ mem_root->free->left=pre_alloc_size;
+ mem_root->free->next=0;
+ }
+ }
+#endif
+}
+
+void * ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size)
+{
+#if defined(HAVE_purify) && defined(EXTRA_DEBUG)
+ reg1 MA_USED_MEM *next;
+ Size+=ALIGN_SIZE(sizeof(MA_USED_MEM));
+
+ if (!(next = (MA_USED_MEM*) malloc(Size)))
+ {
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ return((void *) 0); /* purecov: inspected */
+ }
+ next->next=mem_root->used;
+ mem_root->used=next;
+ return (void *) (((char*) next)+ALIGN_SIZE(sizeof(MA_USED_MEM)));
+#else
+ size_t get_size;
+ void * point;
+ reg1 MA_USED_MEM *next= 0;
+ reg2 MA_USED_MEM **prev;
+
+ Size= ALIGN_SIZE(Size);
+
+ if ((*(prev= &mem_root->free)))
+ {
+ if ((*prev)->left < Size &&
+ mem_root->first_block_usage++ >= 16 &&
+ (*prev)->left < 4096)
+ {
+ next= *prev;
+ *prev= next->next;
+ next->next= mem_root->used;
+ mem_root->used= next;
+ mem_root->first_block_usage= 0;
+ }
+ for (next= *prev; next && next->left < Size; next= next->next)
+ prev= &next->next;
+ }
+
+ if (! next)
+ { /* Time to alloc new block */
+ get_size= MAX(Size+ALIGN_SIZE(sizeof(MA_USED_MEM)),
+ (mem_root->block_size & ~1) * (mem_root->block_num >> 2));
+
+ if (!(next = (MA_USED_MEM*) malloc(get_size)))
+ {
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ return((void *) 0); /* purecov: inspected */
+ }
+ mem_root->block_num++;
+ next->next= *prev;
+ next->size= get_size;
+ next->left= get_size-ALIGN_SIZE(sizeof(MA_USED_MEM));
+ *prev=next;
+ }
+ point= (void *) ((char*) next+ (next->size-next->left));
+ if ((next->left-= Size) < mem_root->min_malloc)
+ { /* Full block */
+ *prev=next->next; /* Remove block from list */
+ next->next=mem_root->used;
+ mem_root->used=next;
+ mem_root->first_block_usage= 0;
+ }
+ return(point);
+#endif
+}
+
+ /* deallocate everything used by alloc_root */
+
+void ma_free_root(MA_MEM_ROOT *root, myf MyFlags)
+{
+ reg1 MA_USED_MEM *next,*old;
+
+ if (!root)
+ return; /* purecov: inspected */
+ if (!(MyFlags & MY_KEEP_PREALLOC))
+ root->pre_alloc=0;
+
+ for ( next=root->used; next ;)
+ {
+ old=next; next= next->next ;
+ if (old != root->pre_alloc)
+ free(old);
+ }
+ for (next= root->free ; next ; )
+ {
+ old=next; next= next->next ;
+ if (old != root->pre_alloc)
+ free(old);
+ }
+ root->used=root->free=0;
+ if (root->pre_alloc)
+ {
+ root->free=root->pre_alloc;
+ root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(MA_USED_MEM));
+ root->free->next=0;
+ }
+}
+
+
+char *ma_strdup_root(MA_MEM_ROOT *root,const char *str)
+{
+ size_t len= strlen(str)+1;
+ char *pos;
+ if ((pos=ma_alloc_root(root,len)))
+ memcpy(pos,str,len);
+ return pos;
+}
+
+
+char *ma_memdup_root(MA_MEM_ROOT *root, const char *str, size_t len)
+{
+ char *pos;
+ if ((pos= ma_alloc_root(root,len)))
+ memcpy(pos,str,len);
+ return pos;
+}
+
+void *ma_multi_malloc(myf myFlags, ...)
+{
+ va_list args;
+ char **ptr,*start,*res;
+ size_t tot_length,length;
+
+ va_start(args,myFlags);
+ tot_length=0;
+ while ((ptr=va_arg(args, char **)))
+ {
+ length=va_arg(args, size_t);
+ tot_length+=ALIGN_SIZE(length);
+ }
+ va_end(args);
+
+ if (!(start=(char *)malloc(tot_length)))
+ return 0;
+
+ va_start(args,myFlags);
+ res=start;
+ while ((ptr=va_arg(args, char **)))
+ {
+ *ptr=res;
+ length=va_arg(args,size_t);
+ res+=ALIGN_SIZE(length);
+ }
+ va_end(args);
+ return start;
+}
diff --git a/mysql/libmariadb/ma_array.c b/mysql/libmariadb/ma_array.c
new file mode 100644
index 0000000..a39e1a1
--- /dev/null
+++ b/mysql/libmariadb/ma_array.c
@@ -0,0 +1,172 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Handling of arrays that can grow dynamicly. */
+
+#undef SAFEMALLOC /* Problems with threads */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "ma_string.h"
+#include <memory.h>
+
+/*
+ Initiate array and alloc space for init_alloc elements. Array is usable
+ even if space allocation failed
+*/
+
+my_bool ma_init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
+ uint init_alloc, uint alloc_increment CALLER_INFO_PROTO)
+{
+ if (!alloc_increment)
+ {
+ alloc_increment=max((8192-MALLOC_OVERHEAD)/element_size,16);
+ if (init_alloc > 8 && alloc_increment > init_alloc * 2)
+ alloc_increment=init_alloc*2;
+ }
+
+ if (!init_alloc)
+ init_alloc=alloc_increment;
+ array->elements=0;
+ array->max_element=init_alloc;
+ array->alloc_increment=alloc_increment;
+ array->size_of_element=element_size;
+ if (!(array->buffer=(char*) malloc(element_size*init_alloc)))
+ {
+ array->max_element=0;
+ return(TRUE);
+ }
+ return(FALSE);
+}
+
+
+my_bool ma_insert_dynamic(DYNAMIC_ARRAY *array, void *element)
+{
+ void *buffer;
+ if (array->elements == array->max_element)
+ { /* Call only when nessesary */
+ if (!(buffer=ma_alloc_dynamic(array)))
+ return TRUE;
+ }
+ else
+ {
+ buffer=array->buffer+(array->elements * array->size_of_element);
+ array->elements++;
+ }
+ memcpy(buffer,element,(size_t) array->size_of_element);
+ return FALSE;
+}
+
+
+ /* Alloc room for one element */
+
+unsigned char *ma_alloc_dynamic(DYNAMIC_ARRAY *array)
+{
+ if (array->elements == array->max_element)
+ {
+ char *new_ptr;
+ if (!(new_ptr=(char*) realloc(array->buffer,(array->max_element+
+ array->alloc_increment)*
+ array->size_of_element)))
+ return 0;
+ array->buffer=new_ptr;
+ array->max_element+=array->alloc_increment;
+ }
+ return (unsigned char *)array->buffer+(array->elements++ * array->size_of_element);
+}
+
+
+ /* remove last element from array and return it */
+
+unsigned char *ma_pop_dynamic(DYNAMIC_ARRAY *array)
+{
+ if (array->elements)
+ return (unsigned char *)array->buffer+(--array->elements * array->size_of_element);
+ return 0;
+}
+
+
+my_bool ma_set_dynamic(DYNAMIC_ARRAY *array, void * element, uint idx)
+{
+ if (idx >= array->elements)
+ {
+ if (idx >= array->max_element)
+ {
+ uint size;
+ char *new_ptr;
+ size=(idx+array->alloc_increment)/array->alloc_increment;
+ size*= array->alloc_increment;
+ if (!(new_ptr=(char*) realloc(array->buffer,size*
+ array->size_of_element)))
+ return TRUE;
+ array->buffer=new_ptr;
+ array->max_element=size;
+ }
+ memset((array->buffer+array->elements*array->size_of_element), 0,
+ (idx - array->elements)*array->size_of_element);
+ array->elements=idx+1;
+ }
+ memcpy(array->buffer+(idx * array->size_of_element),element,
+ (size_t) array->size_of_element);
+ return FALSE;
+}
+
+
+void ma_get_dynamic(DYNAMIC_ARRAY *array, void * element, uint idx)
+{
+ if (idx >= array->elements)
+ {
+ memset(element, 0, array->size_of_element);
+ return;
+ }
+ memcpy(element,array->buffer+idx*array->size_of_element,
+ (size_t) array->size_of_element);
+}
+
+
+void ma_delete_dynamic(DYNAMIC_ARRAY *array)
+{
+ if (array->buffer)
+ {
+ free(array->buffer);
+ array->buffer=0;
+ array->elements=array->max_element=0;
+ }
+}
+
+
+void ma_delete_dynamic_element(DYNAMIC_ARRAY *array, uint idx)
+{
+ char *ptr=array->buffer+array->size_of_element*idx;
+ array->elements--;
+ memmove(ptr,ptr+array->size_of_element,
+ (array->elements-idx)*array->size_of_element);
+}
+
+
+void ma_freeze_size(DYNAMIC_ARRAY *array)
+{
+ uint elements=max(array->elements,1);
+
+ if (array->buffer && array->max_element != elements)
+ {
+ array->buffer=(char*) realloc(array->buffer,
+ elements*array->size_of_element);
+ array->max_element=elements;
+ }
+}
diff --git a/mysql/libmariadb/ma_charset.c b/mysql/libmariadb/ma_charset.c
new file mode 100644
index 0000000..9a8867c
--- /dev/null
+++ b/mysql/libmariadb/ma_charset.c
@@ -0,0 +1,1374 @@
+/****************************************************************************
+ Copyright (C) 2012 Monty Program AB
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not see <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ Part of this code includes code from the PHP project which
+ is freely available from http://www.php.net
+*****************************************************************************/
+
+/* The implementation for character set support was ported from PHP's mysqlnd
+ extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
+
+ Original file header:
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef _WIN32
+#include <strings.h>
+#include <string.h>
+#else
+#include <string.h>
+#endif
+#include <ma_global.h>
+#include <mariadb_ctype.h>
+#include <ma_string.h>
+
+#ifdef _WIN32
+#include "../win-iconv/iconv.h"
+#else
+#include <iconv.h>
+#endif
+
+
+#if defined(HAVE_NL_LANGINFO) && defined(HAVE_SETLOCALE)
+#include <locale.h>
+#include <langinfo.h>
+#endif
+
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* {{{ utf8 functions */
+static unsigned int check_mb_utf8mb3_sequence(const char *start, const char *end)
+{
+ uchar c;
+
+ if (start >= end) {
+ return 0;
+ }
+
+ c = (uchar) start[0];
+
+ if (c < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (c < 0xC2) {
+ return 0; /* invalid mb character */
+ }
+ if (c < 0xE0) {
+ if (start + 2 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40)) {
+ return 0;
+ }
+ return 2;
+ }
+ if (c < 0xF0) {
+ if (start + 3 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40 && ((uchar)start[2] ^ 0x80) < 0x40 &&
+ (c >= 0xE1 || (uchar)start[1] >= 0xA0))) {
+ return 0; /* invalid utf8 character */
+ }
+ return 3;
+ }
+ return 0;
+}
+
+
+static unsigned int check_mb_utf8_sequence(const char *start, const char *end)
+{
+ uchar c;
+
+ if (start >= end) {
+ return 0;
+ }
+
+ c = (uchar) start[0];
+
+ if (c < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (c < 0xC2) {
+ return 0; /* invalid mb character */
+ }
+ if (c < 0xE0) {
+ if (start + 2 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40)) {
+ return 0;
+ }
+ return 2;
+ }
+ if (c < 0xF0) {
+ if (start + 3 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40 && ((uchar)start[2] ^ 0x80) < 0x40 &&
+ (c >= 0xE1 || (uchar)start[1] >= 0xA0))) {
+ return 0; /* invalid utf8 character */
+ }
+ return 3;
+ }
+ if (c < 0xF5) {
+ if (start + 4 > end) { /* We need 4 characters */
+ return 0; /* too small */
+ }
+
+ /*
+ UTF-8 quick four-byte mask:
+ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ Encoding allows to encode U+00010000..U+001FFFFF
+
+ The maximum character defined in the Unicode standard is U+0010FFFF.
+ Higher characters U+00110000..U+001FFFFF are not used.
+
+ 11110000.10010000.10xxxxxx.10xxxxxx == F0.90.80.80 == U+00010000 (min)
+ 11110100.10001111.10111111.10111111 == F4.8F.BF.BF == U+0010FFFF (max)
+
+ Valid codes:
+ [F0][90..BF][80..BF][80..BF]
+ [F1][80..BF][80..BF][80..BF]
+ [F2][80..BF][80..BF][80..BF]
+ [F3][80..BF][80..BF][80..BF]
+ [F4][80..8F][80..BF][80..BF]
+ */
+
+ if (!(((uchar)start[1] ^ 0x80) < 0x40 &&
+ ((uchar)start[2] ^ 0x80) < 0x40 &&
+ ((uchar)start[3] ^ 0x80) < 0x40 &&
+ (c >= 0xf1 || (uchar)start[1] >= 0x90) &&
+ (c <= 0xf3 || (uchar)start[1] <= 0x8F)))
+ {
+ return 0; /* invalid utf8 character */
+ }
+ return 4;
+ }
+ return 0;
+}
+
+static unsigned int check_mb_utf8mb3_valid(const char *start, const char *end)
+{
+ unsigned int len = check_mb_utf8mb3_sequence(start, end);
+ return (len > 1)? len:0;
+}
+
+static unsigned int check_mb_utf8_valid(const char *start, const char *end)
+{
+ unsigned int len = check_mb_utf8_sequence(start, end);
+ return (len > 1)? len:0;
+}
+
+
+static unsigned int mysql_mbcharlen_utf8mb3(unsigned int utf8)
+{
+ if (utf8 < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (utf8 < 0xC2) {
+ return 0; /* invalid multibyte header */
+ }
+ if (utf8 < 0xE0) {
+ return 2; /* double byte character */
+ }
+ if (utf8 < 0xF0) {
+ return 3; /* triple byte character */
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_utf8(unsigned int utf8)
+{
+ if (utf8 < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (utf8 < 0xC2) {
+ return 0; /* invalid multibyte header */
+ }
+ if (utf8 < 0xE0) {
+ return 2; /* double byte character */
+ }
+ if (utf8 < 0xF0) {
+ return 3; /* triple byte character */
+ }
+ if (utf8 < 0xF8) {
+ return 4; /* four byte character */
+ }
+ return 0;
+}
+/* }}} */
+
+
+/* {{{ big5 functions */
+#define valid_big5head(c) (0xA1 <= (unsigned int)(c) && (unsigned int)(c) <= 0xF9)
+#define valid_big5tail(c) ((0x40 <= (unsigned int)(c) && (unsigned int)(c) <= 0x7E) || \
+ (0xA1 <= (unsigned int)(c) && (unsigned int)(c) <= 0xFE))
+
+#define isbig5code(c,d) (isbig5head(c) && isbig5tail(d))
+
+static unsigned int check_mb_big5(const char *start, const char *end)
+{
+ return (valid_big5head(*(start)) && (end - start) > 1 && valid_big5tail(*(start + 1)) ? 2 : 0);
+}
+
+
+static unsigned int mysql_mbcharlen_big5(unsigned int big5)
+{
+ return (valid_big5head(big5)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ cp932 functions */
+#define valid_cp932head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && c <= 0xFC))
+#define valid_cp932tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && c <= 0xFC))
+
+
+static unsigned int check_mb_cp932(const char *start, const char *end)
+{
+ return (valid_cp932head((uchar)start[0]) && (end - start > 1) &&
+ valid_cp932tail((uchar)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysql_mbcharlen_cp932(unsigned int cp932)
+{
+ return (valid_cp932head((uchar)cp932)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ euckr functions */
+#define valid_euckr(c) ((0xA1 <= (uchar)(c) && (uchar)(c) <= 0xFE))
+
+static unsigned int check_mb_euckr(const char *start, const char *end)
+{
+ if (end - start <= 1) {
+ return 0; /* invalid length */
+ }
+ if (*(uchar *)start < 0x80) {
+ return 0; /* invalid euckr character */
+ }
+ if (valid_euckr(start[1])) {
+ return 2;
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_euckr(unsigned int kr)
+{
+ return (valid_euckr(kr)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ eucjpms functions */
+#define valid_eucjpms(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xFE)
+#define valid_eucjpms_kata(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xDF)
+#define valid_eucjpms_ss2(c) (((c) & 0xFF) == 0x8E)
+#define valid_eucjpms_ss3(c) (((c) & 0xFF) == 0x8F)
+
+static unsigned int check_mb_eucjpms(const char *start, const char *end)
+{
+ if (*((uchar *)start) < 0x80) {
+ return 0; /* invalid eucjpms character */
+ }
+ if (valid_eucjpms(start[0]) && (end - start) > 1 && valid_eucjpms(start[1])) {
+ return 2;
+ }
+ if (valid_eucjpms_ss2(start[0]) && (end - start) > 1 && valid_eucjpms_kata(start[1])) {
+ return 2;
+ }
+ if (valid_eucjpms_ss3(start[0]) && (end - start) > 2 && valid_eucjpms(start[1]) &&
+ valid_eucjpms(start[2])) {
+ return 2;
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_eucjpms(unsigned int jpms)
+{
+ if (valid_eucjpms(jpms) || valid_eucjpms_ss2(jpms)) {
+ return 2;
+ }
+ if (valid_eucjpms_ss3(jpms)) {
+ return 3;
+ }
+ return 1;
+}
+/* }}} */
+
+
+/* {{{ gb2312 functions */
+#define valid_gb2312_head(c) (0xA1 <= (uchar)(c) && (uchar)(c) <= 0xF7)
+#define valid_gb2312_tail(c) (0xA1 <= (uchar)(c) && (uchar)(c) <= 0xFE)
+
+
+static unsigned int check_mb_gb2312(const char *start, const char *end)
+{
+ return (valid_gb2312_head((unsigned int)start[0]) && end - start > 1 &&
+ valid_gb2312_tail((unsigned int)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysql_mbcharlen_gb2312(unsigned int gb)
+{
+ return (valid_gb2312_head(gb)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ gbk functions */
+#define valid_gbk_head(c) (0x81<=(uchar)(c) && (uchar)(c)<=0xFE)
+#define valid_gbk_tail(c) ((0x40<=(uchar)(c) && (uchar)(c)<=0x7E) || (0x80<=(uchar)(c) && (uchar)(c)<=0xFE))
+
+static unsigned int check_mb_gbk(const char *start, const char *end)
+{
+ return (valid_gbk_head(start[0]) && (end) - (start) > 1 && valid_gbk_tail(start[1])) ? 2 : 0;
+}
+
+static unsigned int mysql_mbcharlen_gbk(unsigned int gbk)
+{
+ return (valid_gbk_head(gbk) ? 2 : 1);
+}
+/* }}} */
+
+
+/* {{{ sjis functions */
+#define valid_sjis_head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && (c) <= 0xFC))
+#define valid_sjis_tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && (c) <= 0xFC))
+
+
+static unsigned int check_mb_sjis(const char *start, const char *end)
+{
+ return (valid_sjis_head((uchar)start[0]) && (end - start) > 1 && valid_sjis_tail((uchar)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysql_mbcharlen_sjis(unsigned int sjis)
+{
+ return (valid_sjis_head((uchar)sjis)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ ucs2 functions */
+static unsigned int check_mb_ucs2(const char *start __attribute((unused)), const char *end __attribute((unused)))
+{
+ return 2; /* always 2 */
+}
+
+static unsigned int mysql_mbcharlen_ucs2(unsigned int ucs2 __attribute((unused)))
+{
+ return 2; /* always 2 */
+}
+/* }}} */
+
+
+/* {{{ ujis functions */
+#define valid_ujis(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xFE))
+#define valid_ujis_kata(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xDF))
+#define valid_ujis_ss2(c) (((c)&0xFF) == 0x8E)
+#define valid_ujis_ss3(c) (((c)&0xFF) == 0x8F)
+
+static unsigned int check_mb_ujis(const char *start, const char *end)
+{
+ if (*(uchar*)start < 0x80) {
+ return 0; /* invalid ujis character */
+ }
+ if (valid_ujis(*(start)) && valid_ujis(*((start)+1))) {
+ return 2;
+ }
+ if (valid_ujis_ss2(*(start)) && valid_ujis_kata(*((start)+1))) {
+ return 2;
+ }
+ if (valid_ujis_ss3(*(start)) && (end-start) > 2 && valid_ujis(*((start)+1)) && valid_ujis(*((start)+2))) {
+ return 3;
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_ujis(unsigned int ujis)
+{
+ return (valid_ujis(ujis)? 2: valid_ujis_ss2(ujis)? 2: valid_ujis_ss3(ujis)? 3: 1);
+}
+/* }}} */
+
+
+
+/* {{{ utf16 functions */
+#define UTF16_HIGH_HEAD(x) ((((uchar) (x)) & 0xFC) == 0xD8)
+#define UTF16_LOW_HEAD(x) ((((uchar) (x)) & 0xFC) == 0xDC)
+
+static unsigned int check_mb_utf16(const char *start, const char *end)
+{
+ if (start + 2 > end) {
+ return 0;
+ }
+
+ if (UTF16_HIGH_HEAD(*start)) {
+ return (start + 4 <= end) && UTF16_LOW_HEAD(start[2]) ? 4 : 0;
+ }
+
+ if (UTF16_LOW_HEAD(*start)) {
+ return 0;
+ }
+ return 2;
+}
+
+
+static uint mysql_mbcharlen_utf16(unsigned int utf16)
+{
+ return UTF16_HIGH_HEAD(utf16) ? 4 : 2;
+}
+/* }}} */
+
+
+/* {{{ utf32 functions */
+static uint
+check_mb_utf32(const char *start __attribute((unused)), const char *end __attribute((unused)))
+{
+ return 4;
+}
+
+
+static uint
+mysql_mbcharlen_utf32(unsigned int utf32 __attribute((unused)))
+{
+ return 4;
+}
+/* }}} */
+
+/*
+ The server compiles sometimes the full utf-8 (the mb4) as utf8m4, and the old as utf8,
+ for BC reasons. Sometimes, utf8mb4 is just utf8 but the old charsets are utf8mb3.
+ Change easily now, with a macro, could be made compilastion dependable.
+*/
+
+#define UTF8_MB4 "utf8mb4"
+#define UTF8_MB3 "utf8"
+
+/* {{{ mysql_charsets */
+const MARIADB_CHARSET_INFO mariadb_compiled_charsets[] =
+{
+ { 1, 1, "big5","big5_chinese_ci", "", 950, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ { 3, 1, "dec8", "dec8_swedisch_ci", "", 0, "DEC", 1, 1, NULL, NULL},
+ { 4, 1, "cp850", "cp850_general_ci", "", 850, "CP850", 1, 1, NULL, NULL},
+ { 6, 1, "hp8", "hp8_english_ci", "", 0, "HP-ROMAN8", 1, 1, NULL, NULL},
+ { 7, 1, "koi8r", "koi8r_general_ci", "", 878, "KOI8R", 1, 1, NULL, NULL},
+ { 8, 1, "latin1", "latin1_swedish_ci", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ { 9, 1, "latin2", "latin2_general_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 10, 1, "swe7", "swe7_swedish_ci", "", 20107, "", 1, 1, NULL, NULL},
+ { 11, 1, "ascii", "ascii_general_ci", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ { 12, 1, "ujis", "ujis_japanese_ci", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ { 13, 1, "sjis", "sjis_japanese_ci", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ { 16, 1, "hebrew", "hebrew_general_ci", "", 1255, "HEBREW", 1, 1, NULL, NULL},
+ { 18, 1, "tis620", "tis620_thai_ci", "", 874, "TIS620", 1, 1, NULL, NULL},
+ { 19, 1, "euckr", "euckr_korean_ci", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ { 22, 1, "koi8u", "koi8u_general_ci", "", 20866, "KOI8U", 1, 1, NULL, NULL},
+ { 24, 1, "gb2312", "gb2312_chinese_ci", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ { 25, 1, "greek", "greek_general_ci", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ { 26, 1, "cp1250", "cp1250_general_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 28, 1, "gbk", "gbk_chinese_ci", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ { 30, 1, "latin5", "latin5_turkish_ci", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ { 32, 1, "armscii8", "armscii8_general_ci", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ { 33, 1, UTF8_MB3, UTF8_MB3"_general_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 35, 1, "ucs2", "ucs2_general_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 36, 1, "cp866", "cp866_general_ci", "", 866, "CP866", 1, 1, NULL, NULL},
+ { 37, 1, "keybcs2", "keybcs2_general_ci", "", 0, "", 1, 1, NULL, NULL},
+ { 38, 1, "macce", "macce_general_ci", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ { 39, 1, "macroman", "macroman_general_ci", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ { 40, 1, "cp852", "cp852_general_ci", "", 852, "CP852", 1, 1, NULL, NULL},
+ { 41, 1, "latin7", "latin7_general_ci", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 51, 1, "cp1251", "cp1251_general_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 57, 1, "cp1256", "cp1256_general_ci", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ { 59, 1, "cp1257", "cp1257_general_ci", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ { 63, 1, "binary", "binary", "", 0, "ASCII", 1, 1, NULL, NULL},
+ { 64, 1, "armscii8", "armscii8_bin", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ { 92, 1, "geostd8", "geostd8_general_ci", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ { 95, 1, "cp932", "cp932_japanese_ci", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ { 97, 1, "eucjpms", "eucjpms_japanese_ci", "", 932, "EUC-JP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 2, 1, "latin2", "latin2_czech_cs", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 5, 1, "latin1", "latin1_german_ci", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ { 14, 1, "cp1251", "cp1251_bulgarian_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 15, 1, "latin1", "latin1_danish_ci", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ { 17, 1, "filename", "filename", "", 0, "", 1, 5, NULL, NULL},
+ { 20, 1, "latin7", "latin7_estonian_cs", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 21, 1, "latin2", "latin2_hungarian_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 23, 1, "cp1251", "cp1251_ukrainian_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 27, 1, "latin2", "latin2_croatian_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 29, 1, "cp1257", "cp1257_lithunian_ci", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ { 31, 1, "latin1", "latin1_german2_ci", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ { 34, 1, "cp1250", "cp1250_czech_cs", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 42, 1, "latin7", "latin7_general_cs", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 43, 1, "macce", "macce_bin", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ { 44, 1, "cp1250", "cp1250_croatian_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 45, 1, UTF8_MB4, UTF8_MB4"_general_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 46, 1, UTF8_MB4, UTF8_MB4"_bin", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 47, 1, "latin1", "latin1_bin", "", 1250, "LATIN1", 1, 1, NULL, NULL},
+ { 48, 1, "latin1", "latin1_general_ci", "", 1250, "LATIN1", 1, 1, NULL, NULL},
+ { 49, 1, "latin1", "latin1_general_cs", "", 1250, "LATIN1", 1, 1, NULL, NULL},
+ { 50, 1, "cp1251", "cp1251_bin", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 52, 1, "cp1251", "cp1251_general_cs", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 53, 1, "macroman", "macroman_bin", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ { 54, 1, "utf16", "utf16_general_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 55, 1, "utf16", "utf16_bin", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 56, 1, "utf16le", "utf16_general_ci", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 58, 1, "cp1257", "cp1257_bin", "", 1257, "CP1257", 1, 1, NULL, NULL},
+#ifdef USED_TO_BE_SO_BEFORE_MYSQL_5_5
+ { 60, 1, "armascii8", "armascii8_bin", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+#endif
+ { 60, 1, "utf32", "utf32_general_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 61, 1, "utf32", "utf32_bin", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 62, 1, "utf16le", "utf16_bin", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 65, 1, "ascii", "ascii_bin", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ { 66, 1, "cp1250", "cp1250_bin", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 67, 1, "cp1256", "cp1256_bin", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ { 68, 1, "cp866", "cp866_bin", "", 866, "CP866", 1, 1, NULL, NULL},
+ { 69, 1, "dec8", "dec8_bin", "", 0, "DEC", 1, 1, NULL, NULL},
+ { 70, 1, "greek", "greek_bin", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ { 71, 1, "hebrew", "hebrew_bin", "", 1255, "hebrew", 1, 1, NULL, NULL},
+ { 72, 1, "hp8", "hp8_bin", "", 0, "HPROMAN-8", 1, 1, NULL, NULL},
+ { 73, 1, "keybcs2", "keybcs2_bin", "", 0, "", 1, 1, NULL, NULL},
+ { 74, 1, "koi8r", "koi8r_bin", "", 20866, "KOI8R", 1, 1, NULL, NULL},
+ { 75, 1, "koi8u", "koi8u_bin", "", 21866, "KOI8U", 1, 1, NULL, NULL},
+ { 77, 1, "latin2", "latin2_bin", "", 28592, "LATIN2", 1, 1, NULL, NULL},
+ { 78, 1, "latin5", "latin5_bin", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ { 79, 1, "latin7", "latin7_bin", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 80, 1, "cp850", "cp850_bin", "", 850, "CP850", 1, 1, NULL, NULL},
+ { 81, 1, "cp852", "cp852_bin", "", 852, "CP852", 1, 1, NULL, NULL},
+ { 82, 1, "swe7", "swe7_bin", "", 0, "", 1, 1, NULL, NULL},
+ { 93, 1, "geostd8", "geostd8_bin", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ { 83, 1, UTF8_MB3, UTF8_MB3"_bin", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 84, 1, "big5", "big5_bin", "", 65000, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ { 85, 1, "euckr", "euckr_bin", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ { 86, 1, "gb2312", "gb2312_bin", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ { 87, 1, "gbk", "gbk_bin", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ { 88, 1, "sjis", "sjis_bin", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ { 89, 1, "tis620", "tis620_bin", "", 874, "TIS620", 1, 1, NULL, NULL},
+ { 90, 1, "ucs2", "ucs2_bin", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 91, 1, "ujis", "ujis_bin", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ { 94, 1, "latin1", "latin1_spanish_ci", "", 1252, "LATIN1", 1, 1, NULL, NULL},
+ { 96, 1, "cp932", "cp932_bin", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ { 99, 1, "cp1250", "cp1250_polish_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 98, 1, "eucjpms", "eucjpms_bin", "", 932, "EUCJP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 101, 1, "utf16", "utf16_unicode_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 102, 1, "utf16", "utf16_icelandic_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 103, 1, "utf16", "utf16_latvian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 104, 1, "utf16", "utf16_romanian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 105, 1, "utf16", "utf16_slovenian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 106, 1, "utf16", "utf16_polish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 107, 1, "utf16", "utf16_estonian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 108, 1, "utf16", "utf16_spanish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 109, 1, "utf16", "utf16_swedish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 110, 1, "utf16", "utf16_turkish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 111, 1, "utf16", "utf16_czech_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 112, 1, "utf16", "utf16_danish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 113, 1, "utf16", "utf16_lithunian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 114, 1, "utf16", "utf16_slovak_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 115, 1, "utf16", "utf16_spanish2_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 116, 1, "utf16", "utf16_roman_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 117, 1, "utf16", "utf16_persian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 118, 1, "utf16", "utf16_esperanto_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 120, 1, "utf16", "utf16_sinhala_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 121, 1, "utf16", "utf16_german2_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 122, 1, "utf16", "utf16_croatian_mysql561_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 123, 1, "utf16", "utf16_unicode_520_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 124, 1, "utf16", "utf16_vietnamese_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 128, 1, "ucs2", "ucs2_unicode_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 129, 1, "ucs2", "ucs2_icelandic_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 130, 1, "ucs2", "ucs2_latvian_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 131, 1, "ucs2", "ucs2_romanian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 132, 1, "ucs2", "ucs2_slovenian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 133, 1, "ucs2", "ucs2_polish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 134, 1, "ucs2", "ucs2_estonian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 135, 1, "ucs2", "ucs2_spanish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 136, 1, "ucs2", "ucs2_swedish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 137, 1, "ucs2", "ucs2_turkish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 138, 1, "ucs2", "ucs2_czech_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 139, 1, "ucs2", "ucs2_danish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 140, 1, "ucs2", "ucs2_lithunian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 141, 1, "ucs2", "ucs2_slovak_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 142, 1, "ucs2", "ucs2_spanish2_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 143, 1, "ucs2", "ucs2_roman_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 144, 1, "ucs2", "ucs2_persian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 145, 1, "ucs2", "ucs2_esperanto_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 146, 1, "ucs2", "ucs2_hungarian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 147, 1, "ucs2", "ucs2_sinhala_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 148, 1, "ucs2", "ucs2_german2_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 149, 1, "ucs2", "ucs2_croatian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 150, 1, "ucs2", "ucs2_unicode_520_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 151, 1, "ucs2", "ucs2_vietnamese_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 159, 1, "ucs2", "ucs2_general_mysql500_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 160, 1, "utf32", "utf32_unicode_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 161, 1, "utf32", "utf32_icelandic_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 162, 1, "utf32", "utf32_latvian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 163, 1, "utf32", "utf32_romanian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 164, 1, "utf32", "utf32_slovenian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 165, 1, "utf32", "utf32_polish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 166, 1, "utf32", "utf32_estonian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 167, 1, "utf32", "utf32_spanish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 168, 1, "utf32", "utf32_swedish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 169, 1, "utf32", "utf32_turkish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 170, 1, "utf32", "utf32_czech_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 171, 1, "utf32", "utf32_danish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 172, 1, "utf32", "utf32_lithunian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 173, 1, "utf32", "utf32_slovak_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 174, 1, "utf32", "utf32_spanish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 175, 1, "utf32", "utf32_roman_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 176, 1, "utf32", "utf32_persian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 177, 1, "utf32", "utf32_esperanto_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 178, 1, "utf32", "utf32_hungarian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 179, 1, "utf32", "utf32_sinhala_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 180, 1, "utf32", "utf32_german2_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 181, 1, "utf32", "utf32_croatian_mysql561_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 182, 1, "utf32", "utf32_unicode_520_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 183, 1, "utf32", "utf32_vietnamese_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+
+ { 192, 1, UTF8_MB3, UTF8_MB3"_general_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 193, 1, UTF8_MB3, UTF8_MB3"_icelandic_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 194, 1, UTF8_MB3, UTF8_MB3"_latvian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 195, 1, UTF8_MB3, UTF8_MB3"_romanian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 196, 1, UTF8_MB3, UTF8_MB3"_slovenian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 197, 1, UTF8_MB3, UTF8_MB3"_polish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 198, 1, UTF8_MB3, UTF8_MB3"_estonian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 199, 1, UTF8_MB3, UTF8_MB3"_spanish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 119, 1, UTF8_MB3, UTF8_MB3"_spanish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 200, 1, UTF8_MB3, UTF8_MB3"_swedish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 201, 1, UTF8_MB3, UTF8_MB3"_turkish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 202, 1, UTF8_MB3, UTF8_MB3"_czech_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 203, 1, UTF8_MB3, UTF8_MB3"_danish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid },
+ { 204, 1, UTF8_MB3, UTF8_MB3"_lithunian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid },
+ { 205, 1, UTF8_MB3, UTF8_MB3"_slovak_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 206, 1, UTF8_MB3, UTF8_MB3"_spanish2_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 207, 1, UTF8_MB3, UTF8_MB3"_roman_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 208, 1, UTF8_MB3, UTF8_MB3"_persian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 209, 1, UTF8_MB3, UTF8_MB3"_esperanto_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 210, 1, UTF8_MB3, UTF8_MB3"_hungarian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 211, 1, UTF8_MB3, UTF8_MB3"_sinhala_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 212, 1, UTF8_MB3, UTF8_MB3"_german_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 214, 1, UTF8_MB3, UTF8_MB3"_unicode_520_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 215, 1, UTF8_MB3, UTF8_MB3"_vietnamese_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 213, 1, UTF8_MB3, UTF8_MB3"_croatian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 223, 1, UTF8_MB3, UTF8_MB3"_general_mysql500_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+
+ { 224, 1, UTF8_MB4, UTF8_MB4"_unicode_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 225, 1, UTF8_MB4, UTF8_MB4"_icelandic_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 226, 1, UTF8_MB4, UTF8_MB4"_latvian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 227, 1, UTF8_MB4, UTF8_MB4"_romanian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 228, 1, UTF8_MB4, UTF8_MB4"_slovenian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 229, 1, UTF8_MB4, UTF8_MB4"_polish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 230, 1, UTF8_MB4, UTF8_MB4"_estonian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 231, 1, UTF8_MB4, UTF8_MB4"_spanish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 232, 1, UTF8_MB4, UTF8_MB4"_swedish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 233, 1, UTF8_MB4, UTF8_MB4"_turkish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 234, 1, UTF8_MB4, UTF8_MB4"_czech_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 235, 1, UTF8_MB4, UTF8_MB4"_danish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 236, 1, UTF8_MB4, UTF8_MB4"_lithuanian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 237, 1, UTF8_MB4, UTF8_MB4"_slovak_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 238, 1, UTF8_MB4, UTF8_MB4"_spanish2_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 239, 1, UTF8_MB4, UTF8_MB4"_roman_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 240, 1, UTF8_MB4, UTF8_MB4"_persian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 241, 1, UTF8_MB4, UTF8_MB4"_esperanto_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 242, 1, UTF8_MB4, UTF8_MB4"_hungarian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 243, 1, UTF8_MB4, UTF8_MB4"_sinhala_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 244, 1, UTF8_MB4, UTF8_MB4"_german2_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 245, 1, UTF8_MB4, UTF8_MB4"_croatian_mysql561_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 246, 1, UTF8_MB4, UTF8_MB4"_unicode_520_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 247, 1, UTF8_MB4, UTF8_MB4"_vietnamese_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+
+ { 254, 1, UTF8_MB3, UTF8_MB3"_general_cs", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 576, 1, UTF8_MB3, UTF8_MB3"_croatian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 577, 1, UTF8_MB3, UTF8_MB3"_myanmar_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 578, 1, UTF8_MB3, UTF8_MB3"_thai_520_w2", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 608, 1, UTF8_MB4, UTF8_MB4"_croatian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 609, 1, UTF8_MB4, UTF8_MB4"_myanmar_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 610, 1, UTF8_MB4, UTF8_MB4"_thai_520_w2", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 640, 1, "ucs2", "ucs2_croatian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 641, 1, "ucs2", "ucs2_myanmar_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 642, 1, "ucs2", "ucs2_thai_520_w2", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 672, 1, "utf16", "utf16_croatian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 673, 1, "utf16", "utf16_myanmar_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 674, 1, "utf16", "utf16_thai_520_w2", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 736, 1, "utf32", "utf32_croatian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 737, 1, "utf32", "utf32_myanmar_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 738, 1, "utf32", "utf32_thai_520_w2", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1025, 1, "big5","big5_chinese_nopad_ci", "", 950, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ {1027, 1, "dec8", "dec8_swedisch_nopad_ci", "", 0, "DEC", 1, 1, NULL, NULL},
+ {1028, 1, "cp850", "cp850_general_nopad_ci", "", 850, "CP850", 1, 1, NULL, NULL},
+ {1030, 1, "hp8", "hp8_english_nopad_ci", "", 0, "HP-ROMAN8", 1, 1, NULL, NULL},
+ {1031, 1, "koi8r", "koi8r_general_nopad_ci", "", 878, "KOI8R", 1, 1, NULL, NULL},
+ {1032, 1, "latin1", "latin1_swedish_nopad_ci", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ {1033, 1, "latin2", "latin2_general_nopad_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ {1034, 1, "swe7", "swe7_swedish_nopad_ci", "", 20107, "", 1, 1, NULL, NULL},
+ {1035, 1, "ascii", "ascii_general_nopad_ci", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ {1036, 1, "ujis", "ujis_japanese_nopad_ci", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ {1037, 1, "sjis", "sjis_japanese_nopad_ci", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ {1040, 1, "hebrew", "hebrew_general_nopad_ci", "", 1255, "HEBREW", 1, 1, NULL, NULL},
+ {1042, 1, "tis620", "tis620_thai_nopad_ci", "", 874, "TIS620", 1, 1, NULL, NULL},
+ {1043, 1, "euckr", "euckr_korean_nopad_ci", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ {1046, 1, "koi8u", "koi8u_general_nopad_ci", "", 20866, "KOI8U", 1, 1, NULL, NULL},
+ {1048, 1, "gb2312", "gb2312_chinese_nopad_ci", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ {1049, 1, "greek", "greek_general_nopad_ci", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ {1050, 1, "cp1250", "cp1250_general_nopad_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ {1052, 1, "gbk", "gbk_chinese_nopad_ci", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ {1054, 1, "latin5", "latin5_turkish_nopad_ci", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ {1056, 1, "armscii8", "armscii8_general_nopad_ci", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ {1057, 1, UTF8_MB3, UTF8_MB3"_general_nopad_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1059, 1, "ucs2", "ucs2_general_nopad_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1060, 1, "cp866", "cp866_general_nopad_ci", "", 866, "CP866", 1, 1, NULL, NULL},
+ {1061, 1, "keybcs2", "keybcs2_general_nopad_ci", "", 0, "", 1, 1, NULL, NULL},
+ {1062, 1, "macce", "macce_general_nopad_ci", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ {1063, 1, "macroman", "macroman_general_nopad_ci", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ {1064, 1, "cp852", "cp852_general_nopad_ci", "", 852, "CP852", 1, 1, NULL, NULL},
+ {1065, 1, "latin7", "latin7_general_nopad_ci", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ {1067, 1, "macce", "macce_nopad_bin", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ {1069, 1, UTF8_MB4, UTF8_MB4"_general_nopad_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ {1070, 1, UTF8_MB4, UTF8_MB4"_general_nopad_bin", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ {1071, 1, "latin1", "latin1_nopad_bin", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ {1074, 1, "cp1251", "cp1251_nopad_bin", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ {1075, 1, "cp1251", "cp1251_general_nopad_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ {1077, 1, "macroman", "macroman_nopad_bin", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ {1078, 1, "utf16", "utf16_general_nopad_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1079, 1, "utf16", "utf16_nopad_bin", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1080, 1, "utf16le", "utf16le_general_nopad_ci", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1081, 1, "cp1256", "cp1256_general_nopad_ci", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ {1082, 1, "cp1257", "cp1257_nopad_bin", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ {1083, 1, "cp1257", "cp1257_general_nopad_ci", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ {1084, 1, "utf32", "utf32_general_nopad_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1085, 1, "utf32", "utf32_nopad_bin", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1086, 1, "utf16le", "utf16le_nopad_bin", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1088, 1, "armscii8", "armscii8_nopad_bin", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ {1089, 1, "ascii", "ascii_nopad_bin", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ {1090, 1, "cp1250", "cp1250_nopad_bin", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ {1091, 1, "cp1256", "cp1256_nopad_bin", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ {1092, 1, "cp866", "cp866_nopad_bin", "", 866, "CP866", 1, 1, NULL, NULL},
+ {1093, 1, "dec8", "dec8_nopad_bin", "", 0, "DEC", 1, 1, NULL, NULL},
+ {1094, 1, "greek", "greek_nopad_bin", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ {1095, 1, "hebrew", "hebrew_nopad_bin", "", 1255, "HEBREW", 1, 1, NULL, NULL},
+ {1096, 1, "hp8", "hp8_nopad_bin", "", 0, "HP-ROMAN8", 1, 1, NULL, NULL},
+ {1097, 1, "keybcs2", "keybcs2_nopad_bin", "", 0, "", 1, 1, NULL, NULL},
+ {1098, 1, "koi8r", "koi8r_nopad_bin", "", 878, "KOI8R", 1, 1, NULL, NULL},
+ {1099, 1, "koi8u", "koi8u_nopad_bin", "", 20866, "KOI8U", 1, 1, NULL, NULL},
+ {1101, 1, "latin2", "latin2_nopad_bin", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ {1102, 1, "latin5", "latin5_nopad_bin", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ {1103, 1, "latin7", "latin7_nopad_bin", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ {1104, 1, "cp850", "cp850_nopad_bin", "", 850, "CP850", 1, 1, NULL, NULL},
+ {1105, 1, "cp852", "cp852_nopad_bin", "", 852, "CP852", 1, 1, NULL, NULL},
+ {1106, 1, "swe7", "swe7_nopad_bin", "", 20107, "", 1, 1, NULL, NULL},
+ {1107, 1, UTF8_MB3, UTF8_MB3"_nopad_bin", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1108, 1, "big5","big5_nopad_bin", "", 950, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ {1109, 1, "euckr", "euckr_nopad_bin", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ {1110, 1, "gb2312", "gb2312_nopad_bin", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ {1111, 1, "gbk", "gbk_nopad_bin", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ {1112, 1, "sjis", "sjis_nopad_bin", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ {1113, 1, "tis620", "tis620_nopad_bin", "", 874, "TIS620", 1, 1, NULL, NULL},
+ {1114, 1, "ucs2", "ucs2_nopad_bin", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1115, 1, "ujis", "ujis_nopad_bin", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ {1116, 1, "geostd8", "geostd8_general_nopad_ci", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ {1117, 1, "geostd8", "geostd8_nopad_bin", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ {1119, 1, "cp932", "cp932_japanese_nopad_ci", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ {1120, 1, "cp932", "cp932_nopad_bin", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ {1121, 1, "eucjpms", "eucjpms_japanese_nopad_ci", "", 932, "EUCJP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ {1122, 1, "eucjpms", "eucjpms_nopad_bin", "", 932, "EUCJP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ {1125, 1, "utf16", "utf16_unicode_nopad_ci", "", 1200, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1147, 1, "utf16", "utf16_unicode_520_nopad_ci", "", 1200, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1152, 1, "ucs2", "ucs2_unicode_nopad_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1174, 1, "ucs2", "ucs2_unicode_520_nopad_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1184, 1, "utf32", "utf32_unicode_nopad_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1206, 1, "utf32", "utf32_unicode_520_nopad_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1216, 1, UTF8_MB3, UTF8_MB3"_unicode_nopad_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1238, 1, UTF8_MB3, UTF8_MB3"_unicode_520_nopad_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1248, 1, UTF8_MB4, UTF8_MB4"_unicode_nopad_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ {1270, 1, UTF8_MB4, UTF8_MB4"_unicode_520_nopad_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 0, 0, NULL, NULL, NULL, 0, NULL, 0, 0, NULL, NULL}
+};
+/* }}} */
+
+
+/* {{{ mysql_find_charset_nr */
+const MARIADB_CHARSET_INFO * mysql_find_charset_nr(unsigned int charsetnr)
+{
+ const MARIADB_CHARSET_INFO * c = mariadb_compiled_charsets;
+
+ do {
+ if (c->nr == charsetnr) {
+ return(c);
+ }
+ ++c;
+ } while (c[0].nr != 0);
+ return(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysql_find_charset_name */
+MARIADB_CHARSET_INFO * mysql_find_charset_name(const char *name)
+{
+ MARIADB_CHARSET_INFO *c = (MARIADB_CHARSET_INFO *)mariadb_compiled_charsets;
+ const char *csname;
+
+ if (!strcasecmp(name, MADB_AUTODETECT_CHARSET_NAME))
+ csname= madb_get_os_character_set();
+ else
+ csname= (char *)name;
+
+ do {
+ if (!strcasecmp(c->csname, csname)) {
+ return(c);
+ }
+ ++c;
+ } while (c[0].nr != 0);
+ return(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysql_cset_escape_quotes */
+size_t mysql_cset_escape_quotes(const MARIADB_CHARSET_INFO *cset, char *newstr,
+ const char * escapestr, size_t escapestr_len )
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ my_bool escape_overflow = FALSE;
+
+ for (;escapestr < end; escapestr++) {
+ unsigned int len = 0;
+ /* check unicode characters */
+
+ if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) {
+
+ /* check possible overflow */
+ if ((newstr + len) > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy mb char without escaping it */
+ while (len--) {
+ *newstr++ = *escapestr++;
+ }
+ escapestr--;
+ continue;
+ }
+ if (*escapestr == '\'') {
+ if (newstr + 2 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ *newstr++ = '\'';
+ *newstr++ = '\'';
+ } else {
+ if (newstr + 1 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ *newstr++ = *escapestr;
+ }
+ }
+ *newstr = '\0';
+
+ if (escape_overflow) {
+ return((size_t)~0);
+ }
+ return((size_t)(newstr - newstr_s));
+}
+/* }}} */
+
+
+/* {{{ mysql_cset_escape_slashes */
+size_t mysql_cset_escape_slashes(const MARIADB_CHARSET_INFO * cset, char *newstr,
+ const char * escapestr, size_t escapestr_len )
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ my_bool escape_overflow = FALSE;
+
+ for (;escapestr < end; escapestr++) {
+ char esc = '\0';
+ unsigned int len = 0;
+
+ /* check unicode characters */
+ if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) {
+ /* check possible overflow */
+ if ((newstr + len) > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy mb char without escaping it */
+ while (len--) {
+ *newstr++ = *escapestr++;
+ }
+ escapestr--;
+ continue;
+ }
+ if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1) {
+ esc = *escapestr;
+ } else {
+ switch (*escapestr) {
+ case 0:
+ esc = '0';
+ break;
+ case '\n':
+ esc = 'n';
+ break;
+ case '\r':
+ esc = 'r';
+ break;
+ case '\\':
+ case '\'':
+ case '"':
+ esc = *escapestr;
+ break;
+ case '\032':
+ esc = 'Z';
+ break;
+ }
+ }
+ if (esc) {
+ if (newstr + 2 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy escaped character */
+ *newstr++ = '\\';
+ *newstr++ = esc;
+ } else {
+ if (newstr + 1 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy non escaped character */
+ *newstr++ = *escapestr;
+ }
+ }
+ *newstr = '\0';
+
+ if (escape_overflow) {
+ return((size_t)~0);
+ }
+ return((size_t)(newstr - newstr_s));
+}
+/* }}} */
+
+/* {{{ MADB_OS_CHARSET */
+struct st_madb_os_charset {
+ const char *identifier;
+ const char *description;
+ const char *charset;
+ const char *iconv_cs;
+ unsigned char supported;
+};
+
+#define MADB_CS_UNSUPPORTED 0
+#define MADB_CS_APPROX 1
+#define MADB_CS_EXACT 2
+
+/* Please add new character sets at the end. */
+struct st_madb_os_charset MADB_OS_CHARSET[]=
+{
+#ifdef _WIN32
+ /* Windows code pages */
+ {"037", "IBM EBCDIC US-Canada", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"437", "OEM United States", "cp850", NULL, MADB_CS_APPROX},
+ {"500", "IBM EBCDIC International", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"708", "Arabic (ASMO 708)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"709", "Arabic (ASMO-449+, BCON V4)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"710", "Transparent Arabic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"720", "Arabic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"737", "Greek (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"775", "Baltic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"850", "Western European (DOS)", "cp850", NULL, MADB_CS_EXACT},
+ {"852", "Central European (DOS)", "cp852", NULL, MADB_CS_EXACT},
+ {"855", "Cyrillic (primarily Russian)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"857", "Turkish (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"858", "OEM Multilingual Latin 1 + Euro symbol", "cp850", NULL, MADB_CS_EXACT},
+ {"860", "Portuguese (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"861", "Icelandic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"862", "Hebrew (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"863", "French Canadian (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"864", "Arabic (864)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"865", "Nordic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"866", "Cyrillic (DOS)", "cp866", NULL, MADB_CS_EXACT},
+ {"869", "Greek, Modern (DOS)", "greek", NULL, MADB_CS_EXACT},
+ {"870", "IBM EBCDIC Multilingual Latin 2", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"874", "Thai (Windows)", "tis620", NULL, MADB_CS_UNSUPPORTED},
+ {"875", "Greek Modern", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"932", "Japanese (Shift-JIS)", "cp932", NULL, MADB_CS_EXACT},
+ {"936", "Chinese Simplified (GB2312)", "gbk", NULL, MADB_CS_EXACT},
+ {"949", "ANSI/OEM Korean (Unified Hangul Code)", "euckr", NULL, MADB_CS_EXACT},
+ {"950", "Chinese Traditional (Big5)", "big5", NULL, MADB_CS_EXACT},
+ {"1026", "EBCDIC Turkish (Latin 5)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1047", "EBCDIC Latin 1/Open System", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1140", "IBM EBCDIC (US-Canada-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1141", "IBM EBCDIC (Germany-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1142", "IBM EBCDIC (Denmark-Norway-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1143", "IBM EBCDIC (Finland-Sweden-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1144", "IBM EBCDIC (Italy-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1145", "IBM EBCDIC (Spain-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1146", "IBM EBCDIC (UK-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1147", "IBM EBCDIC (France-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1148", "IBM EBCDIC (International-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1149", "IBM EBCDIC (Icelandic-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1200", "UTF-16, little endian byte order", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1201", "UTF-16, big endian byte order", "utf16", NULL, MADB_CS_UNSUPPORTED},
+ {"1250", "Central European (Windows)", "cp1250", NULL, MADB_CS_EXACT},
+ {"1251", "Cyrillic (Windows)", "cp1251", NULL, MADB_CS_EXACT},
+ {"1252", "Western European (Windows)", "latin1", NULL, MADB_CS_EXACT},
+ {"1253", "Greek (Windows)", "greek", NULL, MADB_CS_EXACT},
+ {"1254", "Turkish (Windows)", "latin5", NULL, MADB_CS_EXACT},
+ {"1255", "Hebrew (Windows)", "hewbrew", NULL, MADB_CS_EXACT},
+ {"1256", "Arabic (Windows)", "cp1256", NULL, MADB_CS_EXACT},
+ {"1257", "Baltic (Windows)","cp1257", NULL, MADB_CS_EXACT},
+ {"1258", "Vietnamese (Windows)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1361", "Korean (Johab)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10000", "Western European (Mac)", "macroman", NULL, MADB_CS_EXACT},
+ {"10001", "Japanese (Mac)", "sjis", NULL, MADB_CS_EXACT},
+ {"10002", "Chinese Traditional (Mac)", "big5", NULL, MADB_CS_EXACT},
+ {"10003", "Korean (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10004", "Arabic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10005", "Hebrew (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10006", "Greek (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10007", "Cyrillic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10008", "Chinese Simplified (Mac)", "gb2312", NULL, MADB_CS_EXACT},
+ {"10010", "Romanian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10017", "Ukrainian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10021", "Thai (Mac)", "tis620", NULL, MADB_CS_EXACT},
+ {"10029", "Central European (Mac)", "macce", NULL, MADB_CS_EXACT},
+ {"10079", "Icelandic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10081", "Turkish (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10082", "Croatian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"12000", "Unicode UTF-32, little endian byte order", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"12001", "Unicode UTF-32, big endian byte order", "utf32", NULL, MADB_CS_UNSUPPORTED},
+ {"20000", "Chinese Traditional (CNS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20001", "TCA Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20002", "Chinese Traditional (Eten)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20003", "IBM5550 Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20004", "TeleText Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20005", "Wang Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20105", "Western European (IA5)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20106", "IA5 German (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20107", "Swedish (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20108", "Norwegian (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20127", "US-ASCII (7-bit)", "ascii", NULL, MADB_CS_EXACT},
+ {"20261", "T.61", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20269", "Non-Spacing Accent", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20273", "EBCDIC Germany", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20277", "EBCDIC Denmark-Norway", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20278", "EBCDIC Finland-Sweden", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20280", "EBCDIC Italy", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20284", "EBCDIC Latin America-Spain", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20285", "EBCDIC United Kingdom", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20290", "EBCDIC Japanese Katakana Extended", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20297", "EBCDIC France", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20420", "EBCDIC Arabic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20423", "EBCDIC Greek", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20424", "EBCDIC Hebrew", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20833", "EBCDIC Korean Extended", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20838", "EBCDIC Thai", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20866", "Cyrillic (KOI8-R)", "koi8r", NULL, MADB_CS_EXACT},
+ {"20871", "EBCDIC Icelandic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20880", "EBCDIC Cyrillic Russian", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20905", "EBCDIC Turkish", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20924", "EBCDIC Latin 1/Open System (1047 + Euro symbol)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20932", "Japanese (JIS 0208-1990 and 0121-1990)", "ujis", NULL, MADB_CS_EXACT},
+ {"20936", "Chinese Simplified (GB2312-80)", "gb2312", NULL, MADB_CS_APPROX},
+ {"20949", "Korean Wansung", "euckr", NULL, MADB_CS_APPROX},
+ {"21025", "EBCDIC Cyrillic Serbian-Bulgarian", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"21866", "Cyrillic (KOI8-U)", "koi8u", NULL, MADB_CS_EXACT},
+ {"28591", "Western European (ISO)", "latin1", NULL, MADB_CS_APPROX},
+ {"28592", "Central European (ISO)", "latin2", NULL, MADB_CS_EXACT},
+ {"28593", "Latin 3", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28594", "Baltic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28595", "ISO 8859-5 Cyrillic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28596", "ISO 8859-6 Arabic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28597", "ISO 8859-7 Greek", "greek", NULL, MADB_CS_EXACT},
+ {"28598", "Hebrew (ISO-Visual)", "hebrew", NULL, MADB_CS_EXACT},
+ {"28599", "ISO 8859-9 Turkish", "latin5", NULL, MADB_CS_EXACT},
+ {"28603", "ISO 8859-13 Estonian", "latin7", NULL, MADB_CS_EXACT},
+ {"28605", "8859-15 Latin 9", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"29001", "Europa 3", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"38598", "ISO 8859-8 Hebrew; Hebrew (ISO-Logical)", "hebrew", NULL, MADB_CS_EXACT},
+ {"50220", "ISO 2022 Japanese with no halfwidth Katakana", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50221", "ISO 2022 Japanese with halfwidth Katakana", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50222", "ISO 2022 Japanese JIS X 0201-1989", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50225", "ISO 2022 Korean", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50227", "ISO 2022 Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50229", "ISO 2022 Traditional Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50930", "EBCDIC Japanese (Katakana) Extended", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50931", "EBCDIC US-Canada and Japanese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50933", "EBCDIC Korean Extended and Korean", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50935", "EBCDIC Simplified Chinese Extended and Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50936", "EBCDIC Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50937", "EBCDIC US-Canada and Traditional Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50939", "EBCDIC Japanese (Latin) Extended and Japanese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"51932", "EUC Japanese", "ujis", NULL, MADB_CS_EXACT},
+ {"51936", "EUC Simplified Chinese; Chinese Simplified (EUC)", "gb2312", NULL, MADB_CS_EXACT},
+ {"51949", "EUC Korean", "euckr", NULL, MADB_CS_EXACT},
+ {"51950", "EUC Traditional Chinese", "big5", NULL, MADB_CS_EXACT},
+ {"52936", "Chinese Simplified (HZ)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"54936", "Chinese Simplified (GB18030)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57002", "ISCII Devanagari", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57003", "ISCII Bengali", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57004", "ISCII Tamil", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57005", "ISCII Telugu", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57006", "ISCII Assamese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57007", "ISCII Oriya", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57008", "ISCII Kannada", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57009", "ISCII Malayalam", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57010", "ISCII Gujarati", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57011", "ISCII Punjabi", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"65000", "utf-7 Unicode (UTF-7)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"65001", "utf-8 Unicode (UTF-8)", "utf8", NULL, MADB_CS_EXACT},
+ /* non Windows */
+#else
+ /* iconv encodings */
+ {"ASCII", "US-ASCII", "ascii", "ASCII", MADB_CS_APPROX},
+ {"US-ASCII", "US-ASCII", "ascii", "ASCII", MADB_CS_APPROX},
+ {"Big5", "Chinese for Taiwan Multi-byte set", "big5", "BIG5", MADB_CS_EXACT},
+ {"CP866", "IBM 866", "cp866", "CP866", MADB_CS_EXACT},
+ {"IBM-1252", "Catalan Spain", "cp1252", "CP1252", MADB_CS_EXACT},
+ {"ISCII-DEV", "Hindi", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"ISO-8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO_8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO88591", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO-8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO_8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO885913", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO-8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO_8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO885915", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO-8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO_8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO88592", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO-8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO_8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO88597", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO-8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO_8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO88598", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO-8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO_8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO88599", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO-8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO_8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO88594", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO-8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"ISO8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"ISO_8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"ISO88595", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"KOI8-R", "KOI8-R", "koi8r", "KOI8R", MADB_CS_EXACT},
+ {"koi8r", "KOI8-R", "koi8r", "KOI8R", MADB_CS_EXACT},
+ {"KOI8-U", "KOI8-U", "koi8u", "KOI8U", MADB_CS_EXACT},
+ {"koi8u", "KOI8-U", "koi8u", "KOI8U", MADB_CS_EXACT},
+ {"koi8t", "KOI8-T", NULL, "KOI8-T", MADB_CS_UNSUPPORTED},
+ {"KOI8-T", "KOI8-T", NULL, "KOI8-T", MADB_CS_UNSUPPORTED},
+ {"SJIS", "SHIFT_JIS", "sjis", "SJIS", MADB_CS_EXACT},
+ {"Shift-JIS", "SHIFT_JIS", "sjis", "SJIS", MADB_CS_EXACT},
+ {"ansi1251", "Cyrillic", "cp1251", "CP1251", MADB_CS_EXACT},
+ {"cp1251", "Cyrillic", "cp1251", "CP1251", MADB_CS_EXACT},
+ {"armscii8", "Armenian", "armscii8", "ASMSCII-8", MADB_CS_EXACT},
+ {"armscii-8", "Armenian", "armscii8", "ASMSCII-8", MADB_CS_EXACT},
+ {"big5hkscs", "Big5-HKSCS", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"cp1255", "Hebrew", "cp1255", "CP1255", MADB_CS_EXACT},
+ {"eucCN", "GB-2312", "gb2312", "GB2312", MADB_CS_EXACT},
+ {"eucJP", "UJIS", "ujis", "UJIS", MADB_CS_EXACT},
+ {"eucKR", "EUC-KR", "euckr", "EUCKR", MADB_CS_EXACT},
+ {"euctw", "EUC-TW", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"gb18030", "GB 18030-2000", "gb18030", "GB18030", MADB_CS_UNSUPPORTED},
+ {"gb2312", "GB2312", "gb2312", "GB2312", MADB_CS_EXACT},
+ {"gbk", "GBK", "gbk", "GBK", MADB_CS_EXACT},
+ {"georgianps", "Georgian", "geostd8", "GEORGIAN-PS", MADB_CS_EXACT},
+ {"utf8", "UTF8", "utf8", "UTF-8", MADB_CS_EXACT},
+ {"utf-8", "UTF8", "utf8", "UTF-8", MADB_CS_EXACT},
+#endif
+ {NULL, NULL, NULL, NULL, 0}
+};
+/* }}} */
+
+/* {{{ madb_get_os_character_set */
+const char *madb_get_os_character_set()
+{
+ unsigned int i= 0;
+ char *p= NULL;
+#ifdef _WIN32
+ char codepage[FN_REFLEN];
+ snprintf(codepage, FN_REFLEN, "%u", GetConsoleWindow() ?
+ GetConsoleCP() : GetACP());
+ p= codepage;
+#elif defined(HAVE_NL_LANGINFO) && defined(HAVE_SETLOCALE)
+ if (setlocale(LC_CTYPE, ""))
+ p= nl_langinfo(CODESET);
+#endif
+ if (!p)
+ return MADB_DEFAULT_CHARSET_NAME;
+ while (MADB_OS_CHARSET[i].identifier)
+ {
+ if (MADB_OS_CHARSET[i].supported > MADB_CS_UNSUPPORTED &&
+ strcasecmp(MADB_OS_CHARSET[i].identifier, p) == 0)
+ return MADB_OS_CHARSET[i].charset;
+ i++;
+ }
+ return MADB_DEFAULT_CHARSET_NAME;
+}
+/* }}} */
+
+/* {{{ madb_get_code_page */
+#ifdef _WIN32
+int madb_get_windows_cp(const char *charset)
+{
+ unsigned int i= 0;
+ while (MADB_OS_CHARSET[i].identifier)
+ {
+ if (MADB_OS_CHARSET[i].supported > MADB_CS_UNSUPPORTED &&
+ strcmp(MADB_OS_CHARSET[i].charset, charset) == 0)
+ return atoi(MADB_OS_CHARSET[i].identifier);
+ i++;
+ }
+ return -1;
+}
+#endif
+/* }}} */
+
+
+/* {{{ map_charset_name
+ Changing charset name into something iconv understands, if necessary.
+ Another purpose it to avoid BOMs in result string, adding BE if necessary
+ e.g.UTF16 does not work form iconv, while UTF-16 does.
+ */
+static void map_charset_name(const char *cs_name, my_bool target_cs, char *buffer, size_t buff_len)
+{
+ char digits[3], endianness[3]= "BE";
+
+ if (sscanf(cs_name, "UTF%2[0-9]%2[LBE]", digits, endianness))
+ {
+ /* We should have at least digits. Endianness we write either default(BE), or what we found in the string */
+ snprintf(buffer, buff_len, "UTF-%s%s", digits, endianness);
+ }
+ else
+ {
+ /* Not our client - copy as is*/
+ strncpy(buffer, cs_name, buff_len);
+ }
+
+ if (target_cs)
+ {
+ strncat(buffer, "//TRANSLIT", buff_len);
+ }
+}
+/* }}} */
+
+/* {{{ mariadb_convert_string
+ Converts string from one charset to another, and writes converted string to given buffer
+ @param[in] from
+ @param[in/out] from_len
+ @param[in] from_cs
+ @param[out] to
+ @param[in/out] to_len
+ @param[in] to_cs
+ @param[out] errorcode
+
+ @return -1 in case of error, bytes used in the "to" buffer, otherwise
+ */
+size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, MARIADB_CHARSET_INFO *from_cs,
+ char *to, size_t *to_len, MARIADB_CHARSET_INFO *to_cs, int *errorcode)
+{
+ iconv_t conv= 0;
+ size_t rc= -1;
+ size_t save_len= *to_len;
+ char to_encoding[128], from_encoding[128];
+
+ *errorcode= 0;
+
+ /* check if conversion is supported */
+ if (!from_cs || !from_cs->encoding || !from_cs->encoding[0] ||
+ !to_cs || !to_cs->encoding || !to_cs->encoding[0])
+ {
+ *errorcode= EINVAL;
+ return rc;
+ }
+
+ map_charset_name(to_cs->encoding, 1, to_encoding, sizeof(to_encoding));
+ map_charset_name(from_cs->encoding, 0, from_encoding, sizeof(from_encoding));
+
+ if ((conv= iconv_open(to_encoding, from_encoding)) == (iconv_t)-1)
+ {
+ *errorcode= errno;
+ goto error;
+ }
+ if ((rc= iconv(conv, (char **)&from, from_len, &to, to_len)) == (size_t)-1)
+ {
+ *errorcode= errno;
+ goto error;
+ }
+ rc= save_len - *to_len;
+error:
+ if (conv != (iconv_t)-1)
+ iconv_close(conv);
+ return rc;
+}
+/* }}} */
+
diff --git a/mysql/libmariadb/ma_client_plugin.c.in b/mysql/libmariadb/ma_client_plugin.c.in
new file mode 100644
index 0000000..8e5b1af
--- /dev/null
+++ b/mysql/libmariadb/ma_client_plugin.c.in
@@ -0,0 +1,483 @@
+/* Copyright (C) 2010 - 2012 Sergei Golubchik and Monty Program Ab
+ 2015-2016 MariaDB Corporation 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 */
+
+/**
+ @file
+
+ Support code for the client side (libmariadb) plugins
+
+ Client plugins are somewhat different from server plugins, they are simpler.
+
+ They do not need to be installed or in any way explicitly loaded on the
+ client, they are loaded automatically on demand.
+ One client plugin per shared object, soname *must* match the plugin name.
+
+ There is no reference counting and no unloading either.
+*/
+
+#if _MSC_VER
+/* Silence warnings about variable 'unused' being used. */
+#define FORCE_INIT_OF_VARS 1
+#endif
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <ma_string.h>
+#include <ma_pthread.h>
+
+#include "errmsg.h"
+#include <mysql/client_plugin.h>
+
+struct st_client_plugin_int {
+ struct st_client_plugin_int *next;
+ void *dlhandle;
+ struct st_mysql_client_plugin *plugin;
+};
+
+static my_bool initialized= 0;
+static MA_MEM_ROOT mem_root;
+
+static uint valid_plugins[][2]= {
+ {MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_PVIO_PLUGIN, MARIADB_CLIENT_PVIO_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_TRACE_PLUGIN, MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_REMOTEIO_PLUGIN, MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_CONNECTION_PLUGIN, MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION},
+ {0, 0}
+};
+
+/*
+ Loaded plugins are stored in a linked list.
+ The list is append-only, the elements are added to the head (like in a stack).
+ The elements are added under a mutex, but the list can be read and traversed
+ without any mutex because once an element is added to the list, it stays
+ there. The main purpose of a mutex is to prevent two threads from
+ loading the same plugin twice in parallel.
+*/
+
+
+struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS + MARIADB_CLIENT_MAX_PLUGINS];
+#ifdef THREAD
+static pthread_mutex_t LOCK_load_client_plugin;
+#endif
+
+@EXTERNAL_PLUGINS@
+
+struct st_mysql_client_plugin *mysql_client_builtins[]=
+{
+ @BUILTIN_PLUGINS@
+ 0
+};
+
+
+static int is_not_initialized(MYSQL *mysql, const char *name)
+{
+ if (initialized)
+ return 0;
+
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+ SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+ name, "not initialized");
+ return 1;
+}
+
+static int get_plugin_nr(uint type)
+{
+ uint i= 0;
+ for(; valid_plugins[i][1]; i++)
+ if (valid_plugins[i][0] == type)
+ return i;
+ return -1;
+}
+
+static const char *check_plugin_version(struct st_mysql_client_plugin *plugin, unsigned int version)
+{
+ if (plugin->interface_version < version ||
+ (plugin->interface_version >> 8) > (version >> 8))
+ return "Incompatible client plugin interface";
+ return 0;
+}
+
+/**
+ finds a plugin in the list
+
+ @param name plugin name to search for
+ @param type plugin type
+
+ @note this does NOT necessarily need a mutex, take care!
+
+ @retval a pointer to a found plugin or 0
+*/
+static struct st_mysql_client_plugin *find_plugin(const char *name, int type)
+{
+ struct st_client_plugin_int *p;
+ int plugin_nr= get_plugin_nr(type);
+
+ DBUG_ASSERT(initialized);
+ if (plugin_nr == -1)
+ return 0;
+
+ if (!name)
+ return plugin_list[plugin_nr]->plugin;
+
+ for (p= plugin_list[plugin_nr]; p; p= p->next)
+ {
+ if (strcmp(p->plugin->name, name) == 0)
+ return p->plugin;
+ }
+ return NULL;
+}
+
+
+/**
+ verifies the plugin and adds it to the list
+
+ @param mysql MYSQL structure (for error reporting)
+ @param plugin plugin to install
+ @param dlhandle a handle to the shared object (returned by dlopen)
+ or 0 if the plugin was not dynamically loaded
+ @param argc number of arguments in the 'va_list args'
+ @param args arguments passed to the plugin initialization function
+
+ @retval a pointer to an installed plugin or 0
+*/
+
+static struct st_mysql_client_plugin *
+add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
+ int argc, va_list args)
+{
+ const char *errmsg;
+ struct st_client_plugin_int plugin_int, *p;
+ char errbuf[1024];
+ int plugin_nr;
+
+ DBUG_ASSERT(initialized);
+
+ plugin_int.plugin= plugin;
+ plugin_int.dlhandle= dlhandle;
+
+ if ((plugin_nr= get_plugin_nr(plugin->type)) == -1)
+ {
+ errmsg= "Unknown client plugin type";
+ goto err1;
+ }
+ if ((errmsg= check_plugin_version(plugin, valid_plugins[plugin_nr][1])))
+ goto err1;
+
+ /* Call the plugin initialization function, if any */
+ if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
+ {
+ errmsg= errbuf;
+ goto err1;
+ }
+
+ p= (struct st_client_plugin_int *)
+ ma_memdup_root(&mem_root, (char *)&plugin_int, sizeof(plugin_int));
+
+ if (!p)
+ {
+ errmsg= "Out of memory";
+ goto err2;
+ }
+
+#ifdef THREAD
+ safe_mutex_assert_owner(&LOCK_load_client_plugin);
+#endif
+
+ p->next= plugin_list[plugin_nr];
+ plugin_list[plugin_nr]= p;
+
+ return plugin;
+
+err2:
+ if (plugin->deinit)
+ plugin->deinit();
+err1:
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg);
+ if (dlhandle)
+ (void)dlclose(dlhandle);
+ return NULL;
+}
+
+
+/**
+ Loads plugins which are specified in the environment variable
+ LIBMYSQL_PLUGINS.
+
+ Multiple plugins must be separated by semicolon. This function doesn't
+ return or log an error.
+
+ The function is be called by mysql_client_plugin_init
+
+ @todo
+ Support extended syntax, passing parameters to plugins, for example
+ LIBMYSQL_PLUGINS="plugin1(param1,param2);plugin2;..."
+ or
+ LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..."
+*/
+
+static void load_env_plugins(MYSQL *mysql)
+{
+ char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
+
+ /* no plugins to load */
+ if (!s)
+ return;
+
+ free_env= plugs= strdup(s);
+
+ do {
+ if ((s= strchr(plugs, ';')))
+ *s= '\0';
+ mysql_load_plugin(mysql, plugs, -1, 0);
+ plugs= s + 1;
+ } while (s);
+
+ free(free_env);
+}
+
+/********** extern functions to be used by libmariadb *********************/
+
+/**
+ Initializes the client plugin layer.
+
+ This function must be called before any other client plugin function.
+
+ @retval 0 successful
+ @retval != 0 error occured
+*/
+
+int mysql_client_plugin_init()
+{
+ MYSQL mysql;
+ struct st_mysql_client_plugin **builtin;
+ va_list unused;
+ LINT_INIT_STRUCT(unused);
+
+ if (initialized)
+ return 0;
+
+ memset(&mysql, 0, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
+
+ pthread_mutex_init(&LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
+ ma_init_alloc_root(&mem_root, 128, 128);
+
+ memset(&plugin_list, 0, sizeof(plugin_list));
+
+ initialized= 1;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+ for (builtin= mysql_client_builtins; *builtin; builtin++)
+ add_plugin(&mysql, *builtin, 0, 0, unused);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ load_env_plugins(&mysql);
+
+ return 0;
+}
+
+
+/**
+ Deinitializes the client plugin layer.
+
+ Unloades all client plugins and frees any associated resources.
+*/
+
+void mysql_client_plugin_deinit()
+{
+ int i;
+ struct st_client_plugin_int *p;
+
+ if (!initialized)
+ return;
+
+ for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
+ for (p= plugin_list[i]; p; p= p->next)
+ {
+ if (p->plugin->deinit)
+ p->plugin->deinit();
+ if (p->dlhandle)
+ (void)dlclose(p->dlhandle);
+ }
+
+ memset(&plugin_list, 0, sizeof(plugin_list));
+ initialized= 0;
+ ma_free_root(&mem_root, MYF(0));
+ pthread_mutex_destroy(&LOCK_load_client_plugin);
+}
+
+/************* public facing functions, for client consumption *********/
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_client_register_plugin(MYSQL *mysql,
+ struct st_mysql_client_plugin *plugin)
+{
+ va_list unused;
+ LINT_INIT_STRUCT(unused);
+
+ if (is_not_initialized(mysql, plugin->name))
+ return NULL;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (find_plugin(plugin->name, plugin->type))
+ {
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+ SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+ plugin->name, "it is already loaded");
+ plugin= NULL;
+ }
+ else
+ plugin= add_plugin(mysql, plugin, 0, 0, unused);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ return plugin;
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
+ int argc, va_list args)
+{
+ const char *errmsg;
+#ifdef _WIN32
+ char errbuf[255];
+#endif
+ char dlpath[FN_REFLEN+1];
+ void *sym, *dlhandle;
+ struct st_mysql_client_plugin *plugin;
+ char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR");
+
+ CLEAR_CLIENT_ERROR(mysql);
+ if (is_not_initialized(mysql, name))
+ return NULL;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (type >= 0 && find_plugin(name, type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ /* Compile dll path */
+ snprintf(dlpath, sizeof(dlpath) - 1, "%s/%s%s",
+ mysql->options.extension && mysql->options.extension->plugin_dir ?
+ mysql->options.extension->plugin_dir : (env_plugin_dir) ? env_plugin_dir :
+ MARIADB_PLUGINDIR, name, SO_EXT);
+
+ /* Open new dll handle */
+ if (!(dlhandle= dlopen((const char *)dlpath, RTLD_NOW)))
+ {
+#ifdef _WIN32
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&errbuf, 255, NULL);
+ errmsg= errbuf;
+#else
+ errmsg= dlerror();
+#endif
+ goto err;
+ }
+
+
+ if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
+ {
+ errmsg= "not a plugin";
+ (void)dlclose(dlhandle);
+ goto err;
+ }
+
+ plugin= (struct st_mysql_client_plugin*)sym;
+
+ if (type >=0 && type != plugin->type)
+ {
+ errmsg= "type mismatch";
+ goto err;
+ }
+
+ if (strcmp(name, plugin->name))
+ {
+ errmsg= "name mismatch";
+ goto err;
+ }
+
+ if (type < 0 && find_plugin(name, plugin->type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ return plugin;
+
+err:
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
+ return NULL;
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
+{
+ struct st_mysql_client_plugin *p;
+ va_list args;
+ va_start(args, argc);
+ p= mysql_load_plugin_v(mysql, name, type, argc, args);
+ va_end(args);
+ return p;
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
+{
+ struct st_mysql_client_plugin *p;
+ int plugin_nr= get_plugin_nr(type);
+
+ if (is_not_initialized(mysql, name))
+ return NULL;
+
+ if (plugin_nr == -1)
+ {
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "invalid type");
+ }
+
+ if ((p= find_plugin(name, type)))
+ return p;
+
+ /* not found, load it */
+ return mysql_load_plugin(mysql, name, type, 0);
+}
+
diff --git a/mysql/libmariadb/ma_compress.c b/mysql/libmariadb/ma_compress.c
new file mode 100644
index 0000000..55184a0
--- /dev/null
+++ b/mysql/libmariadb/ma_compress.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Written by Sinisa Milivojevic <sinisa@coresinc.com> */
+
+#include <ma_global.h>
+#ifdef HAVE_COMPRESS
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <zlib.h>
+
+/*
+** This replaces the packet with a compressed packet
+** Returns 1 on error
+** *complen is 0 if the packet wasn't compressed
+*/
+
+my_bool _mariadb_compress(unsigned char *packet, size_t *len, size_t *complen)
+{
+ if (*len < MIN_COMPRESS_LENGTH)
+ *complen=0;
+ else
+ {
+ unsigned char *compbuf=_mariadb_compress_alloc(packet,len,complen);
+ if (!compbuf)
+ return *complen ? 0 : 1;
+ memcpy(packet,compbuf,*len);
+ free(compbuf);
+ }
+ return 0;
+}
+
+
+unsigned char *_mariadb_compress_alloc(const unsigned char *packet, size_t *len, size_t *complen)
+{
+ unsigned char *compbuf;
+ *complen = *len * 120 / 100 + 12;
+ if (!(compbuf = (unsigned char *) malloc(*complen)))
+ return 0; /* Not enough memory */
+ if (compress((Bytef*) compbuf,(ulong *) complen, (Bytef*) packet,
+ (uLong) *len ) != Z_OK)
+ {
+ free(compbuf);
+ return 0;
+ }
+ if (*complen >= *len)
+ {
+ *complen=0;
+ free(compbuf);
+ return 0;
+ }
+ swap(size_t,*len,*complen); /* *len is now packet length */
+ return compbuf;
+}
+
+my_bool _mariadb_uncompress (unsigned char *packet, size_t *len, size_t *complen)
+{
+ if (*complen) /* If compressed */
+ {
+ unsigned char *compbuf = (unsigned char *) malloc (*complen);
+ if (!compbuf)
+ return 1; /* Not enough memory */
+ if (uncompress((Bytef*) compbuf, (uLongf *)complen, (Bytef*) packet, (uLongf)*len) != Z_OK)
+ { /* Probably wrong packet */
+ free(compbuf);
+ return 1;
+ }
+ *len = *complen;
+ memcpy(packet,compbuf,*len);
+ free(compbuf);
+ }
+ else *complen= *len;
+ return 0;
+}
+#endif /* HAVE_COMPRESS */
diff --git a/mysql/libmariadb/ma_context.c b/mysql/libmariadb/ma_context.c
new file mode 100644
index 0000000..68b3560
--- /dev/null
+++ b/mysql/libmariadb/ma_context.c
@@ -0,0 +1,726 @@
+/*
+ Copyright 2011, 2012 Kristian Nielsen and Monty Program Ab
+ 2016 MariaDB Corporation AB
+
+ This file is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Implementation of async context spawning using Posix ucontext and
+ swapcontext().
+*/
+
+#include "ma_global.h"
+#include "ma_string.h"
+#include "ma_context.h"
+
+#ifdef HAVE_VALGRIND
+#include <valgrind/valgrind.h>
+#endif
+
+#ifdef MY_CONTEXT_USE_UCONTEXT
+/*
+ The makecontext() only allows to pass integers into the created context :-(
+ We want to pass pointers, so we do it this kinda hackish way.
+ Anyway, it should work everywhere, and at least it does not break strict
+ aliasing.
+*/
+union pass_void_ptr_as_2_int {
+ int a[2];
+ void *p;
+};
+
+/*
+ We use old-style function definition here, as this is passed to
+ makecontext(). And the type of the makecontext() argument does not match
+ the actual type (as the actual type can differ from call to call).
+*/
+static void
+my_context_spawn_internal(i0, i1)
+int i0, i1;
+{
+ int err;
+ struct my_context *c;
+ union pass_void_ptr_as_2_int u;
+
+ u.a[0]= i0;
+ u.a[1]= i1;
+ c= (struct my_context *)u.p;
+
+ (*c->user_func)(c->user_data);
+ c->active= 0;
+ err= setcontext(&c->base_context);
+ fprintf(stderr, "Aieie, setcontext() failed: %d (errno=%d)\n", err, errno);
+}
+
+
+int
+my_context_continue(struct my_context *c)
+{
+ int err;
+
+ if (!c->active)
+ return 0;
+
+ err= swapcontext(&c->base_context, &c->spawned_context);
+ if (err)
+ {
+ fprintf(stderr, "Aieie, swapcontext() failed: %d (errno=%d)\n",
+ err, errno);
+ return -1;
+ }
+
+ return c->active;
+}
+
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ int err;
+ union pass_void_ptr_as_2_int u;
+
+ err= getcontext(&c->spawned_context);
+ if (err)
+ return -1;
+ c->spawned_context.uc_stack.ss_sp= c->stack;
+ c->spawned_context.uc_stack.ss_size= c->stack_size;
+ c->spawned_context.uc_link= NULL;
+ c->user_func= f;
+ c->user_data= d;
+ c->active= 1;
+ u.p= c;
+ makecontext(&c->spawned_context, my_context_spawn_internal, 2,
+ u.a[0], u.a[1]);
+
+ return my_context_continue(c);
+}
+
+
+int
+my_context_yield(struct my_context *c)
+{
+ int err;
+
+ if (!c->active)
+ return -1;
+
+ err= swapcontext(&c->spawned_context, &c->base_context);
+ if (err)
+ return -1;
+ return 0;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+#if SIZEOF_CHARP > SIZEOF_INT*2
+#error Error: Unable to store pointer in 2 ints on this architecture
+#endif
+
+ memset(c, 0, sizeof(*c));
+ if (!(c->stack= malloc(stack_size)))
+ return -1; /* Out of memory */
+ c->stack_size= stack_size;
+#ifdef HAVE_VALGRIND
+ c->valgrind_stack_id=
+ VALGRIND_STACK_REGISTER(c->stack, ((unsigned char *)(c->stack))+stack_size);
+#endif
+ return 0;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->stack)
+ {
+#ifdef HAVE_VALGRIND
+ VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
+#endif
+ free(c->stack);
+ }
+}
+
+#endif /* MY_CONTEXT_USE_UCONTEXT */
+
+
+#ifdef MY_CONTEXT_USE_X86_64_GCC_ASM
+/*
+ GCC-amd64 implementation of my_context.
+
+ This is slightly optimized in the common case where we never yield
+ (eg. fetch next row and it is already fully received in buffer). In this
+ case we do not need to restore registers at return (though we still need to
+ save them as we cannot know if we will yield or not in advance).
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ Layout of saved registers etc.
+ Since this is accessed through gcc inline assembler, it is simpler to just
+ use numbers than to try to define nice constants or structs.
+
+ 0 0 %rsp
+ 1 8 %rbp
+ 2 16 %rbx
+ 3 24 %r12
+ 4 32 %r13
+ 5 40 %r14
+ 6 48 %r15
+ 7 56 %rip for done
+ 8 64 %rip for yield/continue
+*/
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ int ret;
+
+ /*
+ There are 6 callee-save registers we need to save and restore when
+ suspending and continuing, plus stack pointer %rsp and instruction pointer
+ %rip.
+
+ However, if we never suspend, the user-supplied function will in any case
+ restore the 6 callee-save registers, so we can avoid restoring them in
+ this case.
+ */
+ __asm__ __volatile__
+ (
+ "movq %%rsp, (%[save])\n\t"
+ "movq %[stack], %%rsp\n\t"
+#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
+ /*
+ This emits a DWARF DW_CFA_undefined directive to make the return address
+ undefined. This indicates that this is the top of the stack frame, and
+ helps tools that use DWARF stack unwinding to obtain stack traces.
+ (I use numeric constant to avoid a dependency on libdwarf includes).
+ */
+ ".cfi_escape 0x07, 16\n\t"
+#endif
+ "movq %%rbp, 8(%[save])\n\t"
+ "movq %%rbx, 16(%[save])\n\t"
+ "movq %%r12, 24(%[save])\n\t"
+ "movq %%r13, 32(%[save])\n\t"
+ "movq %%r14, 40(%[save])\n\t"
+ "movq %%r15, 48(%[save])\n\t"
+ "leaq 1f(%%rip), %%rax\n\t"
+ "leaq 2f(%%rip), %%rcx\n\t"
+ "movq %%rax, 56(%[save])\n\t"
+ "movq %%rcx, 64(%[save])\n\t"
+ /*
+ Constraint below puts the argument to the user function into %rdi, as
+ needed for the calling convention.
+ */
+ "callq *%[f]\n\t"
+ "jmpq *56(%[save])\n"
+ /*
+ Come here when operation is done.
+ We do not need to restore callee-save registers, as the called function
+ will do this for us if needed.
+ */
+ "1:\n\t"
+ "movq (%[save]), %%rsp\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 3f\n"
+ /* Come here when operation was suspended. */
+ "2:\n\t"
+ "movl $1, %[ret]\n"
+ "3:\n"
+ : [ret] "=a" (ret),
+ [f] "+S" (f),
+ /* Need this in %rdi to follow calling convention. */
+ [d] "+D" (d)
+ : [stack] "a" (c->stack_top),
+ /* Need this in callee-save register to preserve in function call. */
+ [save] "b" (&c->save[0])
+ : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_continue(struct my_context *c)
+{
+ int ret;
+
+ __asm__ __volatile__
+ (
+ "movq (%[save]), %%rax\n\t"
+ "movq %%rsp, (%[save])\n\t"
+ "movq %%rax, %%rsp\n\t"
+ "movq 8(%[save]), %%rax\n\t"
+ "movq %%rbp, 8(%[save])\n\t"
+ "movq %%rax, %%rbp\n\t"
+ "movq 24(%[save]), %%rax\n\t"
+ "movq %%r12, 24(%[save])\n\t"
+ "movq %%rax, %%r12\n\t"
+ "movq 32(%[save]), %%rax\n\t"
+ "movq %%r13, 32(%[save])\n\t"
+ "movq %%rax, %%r13\n\t"
+ "movq 40(%[save]), %%rax\n\t"
+ "movq %%r14, 40(%[save])\n\t"
+ "movq %%rax, %%r14\n\t"
+ "movq 48(%[save]), %%rax\n\t"
+ "movq %%r15, 48(%[save])\n\t"
+ "movq %%rax, %%r15\n\t"
+
+ "leaq 1f(%%rip), %%rax\n\t"
+ "leaq 2f(%%rip), %%rcx\n\t"
+ "movq %%rax, 56(%[save])\n\t"
+ "movq 64(%[save]), %%rax\n\t"
+ "movq %%rcx, 64(%[save])\n\t"
+
+ "movq 16(%[save]), %%rcx\n\t"
+ "movq %%rbx, 16(%[save])\n\t"
+ "movq %%rcx, %%rbx\n\t"
+
+ "jmpq *%%rax\n"
+ /*
+ Come here when operation is done.
+ Be sure to use the same callee-save register for %[save] here and in
+ my_context_spawn(), so we preserve the value correctly at this point.
+ */
+ "1:\n\t"
+ "movq (%[save]), %%rsp\n\t"
+ "movq 8(%[save]), %%rbp\n\t"
+ /* %rbx is preserved from my_context_spawn() in this case. */
+ "movq 24(%[save]), %%r12\n\t"
+ "movq 32(%[save]), %%r13\n\t"
+ "movq 40(%[save]), %%r14\n\t"
+ "movq 48(%[save]), %%r15\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 3f\n"
+ /* Come here when operation is suspended. */
+ "2:\n\t"
+ "movl $1, %[ret]\n"
+ "3:\n"
+ : [ret] "=a" (ret)
+ : /* Need this in callee-save register to preserve in function call. */
+ [save] "b" (&c->save[0])
+ : "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_yield(struct my_context *c)
+{
+ uint64_t *save= &c->save[0];
+ __asm__ __volatile__
+ (
+ "movq (%[save]), %%rax\n\t"
+ "movq %%rsp, (%[save])\n\t"
+ "movq %%rax, %%rsp\n\t"
+ "movq 8(%[save]), %%rax\n\t"
+ "movq %%rbp, 8(%[save])\n\t"
+ "movq %%rax, %%rbp\n\t"
+ "movq 16(%[save]), %%rax\n\t"
+ "movq %%rbx, 16(%[save])\n\t"
+ "movq %%rax, %%rbx\n\t"
+ "movq 24(%[save]), %%rax\n\t"
+ "movq %%r12, 24(%[save])\n\t"
+ "movq %%rax, %%r12\n\t"
+ "movq 32(%[save]), %%rax\n\t"
+ "movq %%r13, 32(%[save])\n\t"
+ "movq %%rax, %%r13\n\t"
+ "movq 40(%[save]), %%rax\n\t"
+ "movq %%r14, 40(%[save])\n\t"
+ "movq %%rax, %%r14\n\t"
+ "movq 48(%[save]), %%rax\n\t"
+ "movq %%r15, 48(%[save])\n\t"
+ "movq %%rax, %%r15\n\t"
+ "movq 64(%[save]), %%rax\n\t"
+ "leaq 1f(%%rip), %%rcx\n\t"
+ "movq %%rcx, 64(%[save])\n\t"
+
+ "jmpq *%%rax\n"
+
+ "1:\n"
+ : [save] "+D" (save)
+ :
+ : "rax", "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"
+ );
+ return 0;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ memset(c, 0, sizeof(*c));
+
+ if (!(c->stack_bot= malloc(stack_size)))
+ return -1; /* Out of memory */
+ /*
+ The x86_64 ABI specifies 16-byte stack alignment.
+ Also put two zero words at the top of the stack.
+ */
+ c->stack_top= (void *)
+ (( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
+ memset(c->stack_top, 0, 16);
+
+#ifdef HAVE_VALGRIND
+ c->valgrind_stack_id=
+ VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
+#endif
+ return 0;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->stack_bot)
+ {
+ free(c->stack_bot);
+#ifdef HAVE_VALGRIND
+ VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
+#endif
+ }
+}
+
+#endif /* MY_CONTEXT_USE_X86_64_GCC_ASM */
+
+
+#ifdef MY_CONTEXT_USE_I386_GCC_ASM
+/*
+ GCC-i386 implementation of my_context.
+
+ This is slightly optimized in the common case where we never yield
+ (eg. fetch next row and it is already fully received in buffer). In this
+ case we do not need to restore registers at return (though we still need to
+ save them as we cannot know if we will yield or not in advance).
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ Layout of saved registers etc.
+ Since this is accessed through gcc inline assembler, it is simpler to just
+ use numbers than to try to define nice constants or structs.
+
+ 0 0 %esp
+ 1 4 %ebp
+ 2 8 %ebx
+ 3 12 %esi
+ 4 16 %edi
+ 5 20 %eip for done
+ 6 24 %eip for yield/continue
+*/
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ int ret;
+
+ /*
+ There are 4 callee-save registers we need to save and restore when
+ suspending and continuing, plus stack pointer %esp and instruction pointer
+ %eip.
+
+ However, if we never suspend, the user-supplied function will in any case
+ restore the 4 callee-save registers, so we can avoid restoring them in
+ this case.
+ */
+ __asm__ __volatile__
+ (
+ "movl %%esp, (%[save])\n\t"
+ "movl %[stack], %%esp\n\t"
+#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
+ /*
+ This emits a DWARF DW_CFA_undefined directive to make the return address
+ undefined. This indicates that this is the top of the stack frame, and
+ helps tools that use DWARF stack unwinding to obtain stack traces.
+ (I use numeric constant to avoid a dependency on libdwarf includes).
+ */
+ ".cfi_escape 0x07, 8\n\t"
+#endif
+ /* Push the parameter on the stack. */
+ "pushl %[d]\n\t"
+ "movl %%ebp, 4(%[save])\n\t"
+ "movl %%ebx, 8(%[save])\n\t"
+ "movl %%esi, 12(%[save])\n\t"
+ "movl %%edi, 16(%[save])\n\t"
+ /* Get label addresses in -fPIC-compatible way (no pc-relative on 32bit) */
+ "call 1f\n"
+ "1:\n\t"
+ "popl %%eax\n\t"
+ "addl $(2f-1b), %%eax\n\t"
+ "movl %%eax, 20(%[save])\n\t"
+ "addl $(3f-2f), %%eax\n\t"
+ "movl %%eax, 24(%[save])\n\t"
+ "call *%[f]\n\t"
+ "jmp *20(%[save])\n"
+ /*
+ Come here when operation is done.
+ We do not need to restore callee-save registers, as the called function
+ will do this for us if needed.
+ */
+ "2:\n\t"
+ "movl (%[save]), %%esp\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 4f\n"
+ /* Come here when operation was suspended. */
+ "3:\n\t"
+ "movl $1, %[ret]\n"
+ "4:\n"
+ : [ret] "=a" (ret),
+ [f] "+c" (f),
+ [d] "+d" (d)
+ : [stack] "a" (c->stack_top),
+ /* Need this in callee-save register to preserve across function call. */
+ [save] "D" (&c->save[0])
+ : "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_continue(struct my_context *c)
+{
+ int ret;
+
+ __asm__ __volatile__
+ (
+ "movl (%[save]), %%eax\n\t"
+ "movl %%esp, (%[save])\n\t"
+ "movl %%eax, %%esp\n\t"
+ "movl 4(%[save]), %%eax\n\t"
+ "movl %%ebp, 4(%[save])\n\t"
+ "movl %%eax, %%ebp\n\t"
+ "movl 8(%[save]), %%eax\n\t"
+ "movl %%ebx, 8(%[save])\n\t"
+ "movl %%eax, %%ebx\n\t"
+ "movl 12(%[save]), %%eax\n\t"
+ "movl %%esi, 12(%[save])\n\t"
+ "movl %%eax, %%esi\n\t"
+
+ "movl 24(%[save]), %%eax\n\t"
+ "call 1f\n"
+ "1:\n\t"
+ "popl %%ecx\n\t"
+ "addl $(2f-1b), %%ecx\n\t"
+ "movl %%ecx, 20(%[save])\n\t"
+ "addl $(3f-2f), %%ecx\n\t"
+ "movl %%ecx, 24(%[save])\n\t"
+
+ /* Must restore %edi last as it is also our %[save] register. */
+ "movl 16(%[save]), %%ecx\n\t"
+ "movl %%edi, 16(%[save])\n\t"
+ "movl %%ecx, %%edi\n\t"
+
+ "jmp *%%eax\n"
+ /*
+ Come here when operation is done.
+ Be sure to use the same callee-save register for %[save] here and in
+ my_context_spawn(), so we preserve the value correctly at this point.
+ */
+ "2:\n\t"
+ "movl (%[save]), %%esp\n\t"
+ "movl 4(%[save]), %%ebp\n\t"
+ "movl 8(%[save]), %%ebx\n\t"
+ "movl 12(%[save]), %%esi\n\t"
+ "movl 16(%[save]), %%edi\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 4f\n"
+ /* Come here when operation is suspended. */
+ "3:\n\t"
+ "movl $1, %[ret]\n"
+ "4:\n"
+ : [ret] "=a" (ret)
+ : /* Need this in callee-save register to preserve in function call. */
+ [save] "D" (&c->save[0])
+ : "ecx", "edx", "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_yield(struct my_context *c)
+{
+ uint64_t *save= &c->save[0];
+ __asm__ __volatile__
+ (
+ "movl (%[save]), %%eax\n\t"
+ "movl %%esp, (%[save])\n\t"
+ "movl %%eax, %%esp\n\t"
+ "movl 4(%[save]), %%eax\n\t"
+ "movl %%ebp, 4(%[save])\n\t"
+ "movl %%eax, %%ebp\n\t"
+ "movl 8(%[save]), %%eax\n\t"
+ "movl %%ebx, 8(%[save])\n\t"
+ "movl %%eax, %%ebx\n\t"
+ "movl 12(%[save]), %%eax\n\t"
+ "movl %%esi, 12(%[save])\n\t"
+ "movl %%eax, %%esi\n\t"
+ "movl 16(%[save]), %%eax\n\t"
+ "movl %%edi, 16(%[save])\n\t"
+ "movl %%eax, %%edi\n\t"
+
+ "movl 24(%[save]), %%eax\n\t"
+ "call 1f\n"
+ "1:\n\t"
+ "popl %%ecx\n\t"
+ "addl $(2f-1b), %%ecx\n\t"
+ "movl %%ecx, 24(%[save])\n\t"
+
+ "jmp *%%eax\n"
+
+ "2:\n"
+ : [save] "+d" (save)
+ :
+ : "eax", "ecx", "memory", "cc"
+ );
+ return 0;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ memset(c, 0, sizeof(*c));
+ if (!(c->stack_bot= malloc(stack_size)))
+ return -1; /* Out of memory */
+ c->stack_top= (void *)
+ (( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
+ memset(c->stack_top, 0, 16);
+
+#ifdef HAVE_VALGRIND
+ c->valgrind_stack_id=
+ VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
+#endif
+ return 0;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->stack_bot)
+ {
+ free(c->stack_bot);
+#ifdef HAVE_VALGRIND
+ VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
+#endif
+ }
+}
+
+#endif /* MY_CONTEXT_USE_I386_GCC_ASM */
+
+
+#ifdef MY_CONTEXT_USE_WIN32_FIBERS
+int
+my_context_yield(struct my_context *c)
+{
+ c->return_value= 1;
+ SwitchToFiber(c->app_fiber);
+ return 0;
+}
+
+
+static void WINAPI
+my_context_trampoline(void *p)
+{
+ struct my_context *c= (struct my_context *)p;
+ /*
+ Reuse the Fiber by looping infinitely, each time we are scheduled we
+ spawn the appropriate function and switch back when it is done.
+
+ This way we avoid the overhead of CreateFiber() for every asynchroneous
+ operation.
+ */
+ for(;;)
+ {
+ (*(c->user_func))(c->user_arg);
+ c->return_value= 0;
+ SwitchToFiber(c->app_fiber);
+ }
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ memset(c, 0, sizeof(*c));
+ c->lib_fiber= CreateFiber(stack_size, my_context_trampoline, c);
+ if (c->lib_fiber)
+ return 0;
+ return -1;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->lib_fiber)
+ {
+ DeleteFiber(c->lib_fiber);
+ c->lib_fiber= NULL;
+ }
+}
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ c->user_func= f;
+ c->user_arg= d;
+ return my_context_continue(c);
+}
+
+int
+my_context_continue(struct my_context *c)
+{
+ void *current_fiber= IsThreadAFiber() ? GetCurrentFiber() : ConvertThreadToFiber(c);
+ c->app_fiber= current_fiber;
+ SwitchToFiber(c->lib_fiber);
+ return c->return_value;
+}
+
+#endif /* MY_CONTEXT_USE_WIN32_FIBERS */
+
+#ifdef MY_CONTEXT_DISABLE
+int
+my_context_continue(struct my_context *c)
+{
+ return -1;
+}
+
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ return -1;
+}
+
+
+int
+my_context_yield(struct my_context *c)
+{
+ return -1;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ return -1; /* Out of memory */
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+}
+
+#endif
diff --git a/mysql/libmariadb/ma_default.c b/mysql/libmariadb/ma_default.c
new file mode 100644
index 0000000..518819d
--- /dev/null
+++ b/mysql/libmariadb/ma_default.c
@@ -0,0 +1,326 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "ma_string.h"
+#include <ctype.h>
+#include "mariadb_ctype.h"
+#include <mysql.h>
+#include <ma_common.h>
+#include <mariadb/ma_io.h>
+
+#ifdef _WIN32
+#include <io.h>
+#include "Shlwapi.h"
+
+static const char *ini_exts[]= {"ini", "cnf", 0};
+#define R_OK 4
+#else
+#include <unistd.h>
+static const char *ini_exts[]= {"cnf", 0};
+#endif
+
+char **configuration_dirs= NULL;
+#define MAX_CONFIG_DIRS 6
+
+static int add_cfg_dir(char **cfg_dirs, const char *directory)
+{
+ int i;
+
+ for (i=0; i < MAX_CONFIG_DIRS && cfg_dirs[i]; i++);
+
+ if (i < MAX_CONFIG_DIRS) {
+ cfg_dirs[i]= strdup(directory);
+ return 0;
+ }
+ return 1;
+}
+
+void release_configuration_dirs()
+{
+ if (configuration_dirs)
+ {
+ int i= 0;
+ while (configuration_dirs[i])
+ free(configuration_dirs[i++]);
+ free(configuration_dirs);
+ }
+}
+
+char **get_default_configuration_dirs()
+{
+#ifdef _WIN32
+ char dirname[FN_REFLEN];
+#endif
+ char *env;
+
+ configuration_dirs= (char **)calloc(1, (MAX_CONFIG_DIRS + 1) * sizeof(char *));
+ if (!configuration_dirs)
+ goto end;
+
+#ifdef _WIN32
+ /* On Windows operating systems configuration files are stored in
+ 1. System directory
+ 2. Windows directory
+ 3. C:\
+ */
+ if (!GetSystemDirectory(dirname, FN_REFLEN) ||
+ add_cfg_dir(configuration_dirs, dirname))
+ goto error;
+
+ if (!GetWindowsDirectory(dirname, FN_REFLEN) ||
+ add_cfg_dir(configuration_dirs, dirname))
+ goto error;
+
+ if (add_cfg_dir(configuration_dirs, "C:"))
+ goto error;
+
+ if (GetModuleFileName(NULL, dirname, FN_REFLEN))
+ {
+ PathRemoveFileSpec(dirname);
+ if (add_cfg_dir(configuration_dirs, dirname))
+ goto error;
+ }
+#else
+ /* on *nix platforms configuration files are stored in
+ 1. SYSCONFDIR (if build happens inside server package, or
+ -DDEFAULT_SYSCONFDIR was specified
+ 2. /etc
+ 3. /etc/mysql
+ */
+#ifdef DEFAULT_SYSCONFDIR
+ if (add_cfg_dir(configuration_dirs, DEFAULT_SYSCONFDIR))
+ goto error;
+#else
+ if (add_cfg_dir(configuration_dirs, "/etc"))
+ goto error;
+ if (add_cfg_dir(configuration_dirs, "/etc/mysql"))
+ goto error;
+#endif
+#endif
+/* This differs from https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/ where MYSQL_HOME is not specified for Windows */
+ if ((env= getenv("MYSQL_HOME")) &&
+ add_cfg_dir(configuration_dirs, env))
+ goto error;
+end:
+ return configuration_dirs;
+error:
+ return NULL;
+}
+
+extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value);
+
+static my_bool is_group(char *ptr, const char **groups)
+{
+ while (*groups)
+ {
+ if (!strcmp(ptr, *groups))
+ return 1;
+ groups++;
+ }
+ return 0;
+}
+
+static my_bool _mariadb_read_options_from_file(MYSQL *mysql,
+ const char *config_file,
+ const char *group)
+{
+ uint line=0;
+ my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0;
+ char buff[4096],*ptr,*end,*value, *key= 0, *optval;
+ MA_FILE *file= NULL;
+ my_bool rc= 1;
+ const char *groups[5]= {"client",
+ "client-server",
+ "client-mariadb",
+ group,
+ NULL};
+ my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value);
+
+
+ /* if a plugin registered a hook we will call this hook, otherwise
+ * default (_mariadb_set_conf_option) will be called */
+ if (mysql->options.extension && mysql->options.extension->set_option)
+ set_option= mysql->options.extension->set_option;
+ else
+ set_option= _mariadb_set_conf_option;
+
+ if (!(file = ma_open(config_file, "r", NULL)))
+ goto err;
+
+ while (ma_gets(buff,sizeof(buff)-1,file))
+ {
+ line++;
+ key= 0;
+ /* Ignore comment and empty lines */
+ for (ptr=buff ; isspace(*ptr) ; ptr++ );
+ if (!is_escaped && (*ptr == '\"' || *ptr== '\''))
+ {
+ is_quoted= !is_quoted;
+ continue;
+ }
+ if (*ptr == '#' || *ptr == ';' || !*ptr)
+ continue;
+ is_escaped= (*ptr == '\\');
+ if (*ptr == '[') /* Group name */
+ {
+ found_group=1;
+ if (!(end=(char *) strchr(++ptr,']')))
+ {
+ /* todo: set error */
+ goto err;
+ }
+ for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
+ end[0]=0;
+ read_values= is_group(ptr, groups);
+ continue;
+ }
+ if (!found_group)
+ {
+ /* todo: set error */
+ goto err;
+ }
+ if (!read_values)
+ continue;
+ if (!(end=value=strchr(ptr,'=')))
+ {
+ end=strchr(ptr, '\0'); /* Option without argument */
+ set_option(mysql, ptr, NULL);
+ }
+ if (!key)
+ key= ptr;
+ for ( ; isspace(end[-1]) ; end--) ;
+
+ if (!value)
+ {
+ if (!key)
+ key= ptr;
+ }
+ else
+ {
+ /* Remove pre- and end space */
+ char *value_end;
+ *value= 0;
+ value++;
+ ptr= value;
+ for ( ; isspace(*value); value++) ;
+ optval= value;
+ value_end=strchr(value, '\0');
+ for ( ; isspace(value_end[-1]) ; value_end--) ;
+ /* remove possible quotes */
+ if (*value == '\'' || *value == '\"')
+ {
+ value++;
+ if (value_end[-1] == '\'' || value_end[-1] == '\"')
+ value_end--;
+ }
+ if (value_end < value) /* Empty string */
+ value_end=value;
+ for ( ; value != value_end; value++)
+ {
+ if (*value == '\\' && value != value_end-1)
+ {
+ switch(*++value) {
+ case 'n':
+ *ptr++='\n';
+ break;
+ case 't':
+ *ptr++= '\t';
+ break;
+ case 'r':
+ *ptr++ = '\r';
+ break;
+ case 'b':
+ *ptr++ = '\b';
+ break;
+ case 's':
+ *ptr++= ' '; /* space */
+ break;
+ case '\"':
+ *ptr++= '\"';
+ break;
+ case '\'':
+ *ptr++= '\'';
+ break;
+ case '\\':
+ *ptr++= '\\';
+ break;
+ default: /* Unknown; Keep '\' */
+ *ptr++= '\\';
+ *ptr++= *value;
+ break;
+ }
+ }
+ else
+ *ptr++= *value;
+ }
+ *ptr=0;
+ set_option(mysql, key, optval);
+ key= optval= 0;
+ }
+ }
+ if (file)
+ ma_close(file);
+ rc= 0;
+
+err:
+ return rc;
+}
+
+
+my_bool _mariadb_read_options(MYSQL *mysql,
+ const char *config_file,
+ const char *group)
+{
+ int i= 0,
+ exts,
+ errors= 0;
+ char filename[FN_REFLEN + 1];
+#ifndef _WIN32
+ char *env;
+#endif
+
+ if (config_file)
+ return _mariadb_read_options_from_file(mysql, config_file, group);
+
+ for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++)
+ {
+ for (exts= 0; ini_exts[exts]; exts++)
+ {
+ snprintf(filename, FN_REFLEN,
+ "%s%cmy.%s", configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]);
+ if (!access(filename, R_OK))
+ errors+= _mariadb_read_options_from_file(mysql, filename, group);
+ }
+ }
+#ifndef _WIN32
+ /* special case: .my.cnf in Home directory */
+ if ((env= getenv("HOME")))
+ {
+ for (exts= 0; ini_exts[exts]; exts++)
+ {
+ snprintf(filename, FN_REFLEN,
+ "%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]);
+ if (!access(filename, R_OK))
+ errors+= _mariadb_read_options_from_file(mysql, filename, group);
+ }
+ }
+#endif
+ return errors;
+}
diff --git a/mysql/libmariadb/ma_dtoa.c b/mysql/libmariadb/ma_dtoa.c
new file mode 100644
index 0000000..e45f792
--- /dev/null
+++ b/mysql/libmariadb/ma_dtoa.c
@@ -0,0 +1,1925 @@
+/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ 2016 MariaDB Corporation 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; version 2
+ of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/****************************************************************
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+
+ The author of this software is David M. Gay.
+
+ Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose without fee is hereby granted, provided that this entire notice
+ is included in all copies of any software which is or includes a copy
+ or modification of this software and in all copies of the supporting
+ documentation for such software.
+
+ THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+
+ ***************************************************************/
+
+//#include "strings_def.h"
+//#include <my_base.h> /* for EOVERFLOW on Windows */
+#include <ma_global.h>
+#include <memory.h>
+#include "ma_string.h"
+
+/**
+ Appears to suffice to not call malloc() in most cases.
+ @todo
+ see if it is possible to get rid of malloc().
+ this constant is sufficient to avoid malloc() on all inputs I have tried.
+*/
+#define DTOA_BUFF_SIZE (460 * sizeof(void *))
+
+/* Magic value returned by dtoa() to indicate overflow */
+#define DTOA_OVERFLOW 9999
+
+static char *dtoa(double, int, int, int *, int *, char **, char *, size_t);
+static void dtoa_free(char *, char *, size_t);
+
+/**
+ @brief
+ Converts a given floating point number to a zero-terminated string
+ representation using the 'f' format.
+
+ @details
+ This function is a wrapper around dtoa() to do the same as
+ sprintf(to, "%-.*f", precision, x), though the conversion is usually more
+ precise. The only difference is in handling [-,+]infinity and nan values,
+ in which case we print '0\0' to the output string and indicate an overflow.
+
+ @param x the input floating point number.
+ @param precision the number of digits after the decimal point.
+ All properties of sprintf() apply:
+ - if the number of significant digits after the decimal
+ point is less than precision, the resulting string is
+ right-padded with zeros
+ - if the precision is 0, no decimal point appears
+ - if a decimal point appears, at least one digit appears
+ before it
+ @param to pointer to the output buffer. The longest string which
+ my_fcvt() can return is FLOATING_POINT_BUFFER bytes
+ (including the terminating '\0').
+ @param error if not NULL, points to a location where the status of
+ conversion is stored upon return.
+ FALSE successful conversion
+ TRUE the input number is [-,+]infinity or nan.
+ The output string in this case is always '0'.
+ @return number of written characters (excluding terminating '\0')
+*/
+
+size_t ma_fcvt(double x, int precision, char *to, my_bool *error)
+{
+ int decpt, sign, len, i;
+ char *res, *src, *end, *dst= to;
+ char buf[DTOA_BUFF_SIZE];
+ DBUG_ASSERT(precision >= 0 && precision < NOT_FIXED_DEC && to != NULL);
+
+ res= dtoa(x, 5, precision, &decpt, &sign, &end, buf, sizeof(buf));
+
+ if (decpt == DTOA_OVERFLOW)
+ {
+ dtoa_free(res, buf, sizeof(buf));
+ *to++= '0';
+ *to= '\0';
+ if (error != NULL)
+ *error= TRUE;
+ return 1;
+ }
+
+ src= res;
+ len= (int)(end - src);
+
+ if (sign)
+ *dst++= '-';
+
+ if (decpt <= 0)
+ {
+ *dst++= '0';
+ *dst++= '.';
+ for (i= decpt; i < 0; i++)
+ *dst++= '0';
+ }
+
+ for (i= 1; i <= len; i++)
+ {
+ *dst++= *src++;
+ if (i == decpt && i < len)
+ *dst++= '.';
+ }
+ while (i++ <= decpt)
+ *dst++= '0';
+
+ if (precision > 0)
+ {
+ if (len <= decpt)
+ *dst++= '.';
+
+ for (i= precision - MAX(0, (len - decpt)); i > 0; i--)
+ *dst++= '0';
+ }
+
+ *dst= '\0';
+ if (error != NULL)
+ *error= FALSE;
+
+ dtoa_free(res, buf, sizeof(buf));
+
+ return dst - to;
+}
+
+/**
+ @brief
+ Converts a given floating point number to a zero-terminated string
+ representation with a given field width using the 'e' format
+ (aka scientific notation) or the 'f' one.
+
+ @details
+ The format is chosen automatically to provide the most number of significant
+ digits (and thus, precision) with a given field width. In many cases, the
+ result is similar to that of sprintf(to, "%g", x) with a few notable
+ differences:
+ - the conversion is usually more precise than C library functions.
+ - there is no 'precision' argument. instead, we specify the number of
+ characters available for conversion (i.e. a field width).
+ - the result never exceeds the specified field width. If the field is too
+ short to contain even a rounded decimal representation, ma_gcvt()
+ indicates overflow and truncates the output string to the specified width.
+ - float-type arguments are handled differently than double ones. For a
+ float input number (i.e. when the 'type' argument is MY_GCVT_ARG_FLOAT)
+ we deliberately limit the precision of conversion by FLT_DIG digits to
+ avoid garbage past the significant digits.
+ - unlike sprintf(), in cases where the 'e' format is preferred, we don't
+ zero-pad the exponent to save space for significant digits. The '+' sign
+ for a positive exponent does not appear for the same reason.
+
+ @param x the input floating point number.
+ @param type is either MY_GCVT_ARG_FLOAT or MY_GCVT_ARG_DOUBLE.
+ Specifies the type of the input number (see notes above).
+ @param width field width in characters. The minimal field width to
+ hold any number representation (albeit rounded) is 7
+ characters ("-Ne-NNN").
+ @param to pointer to the output buffer. The result is always
+ zero-terminated, and the longest returned string is thus
+ 'width + 1' bytes.
+ @param error if not NULL, points to a location where the status of
+ conversion is stored upon return.
+ FALSE successful conversion
+ TRUE the input number is [-,+]infinity or nan.
+ The output string in this case is always '0'.
+ @return number of written characters (excluding terminating '\0')
+
+ @todo
+ Check if it is possible and makes sense to do our own rounding on top of
+ dtoa() instead of calling dtoa() twice in (rare) cases when the resulting
+ string representation does not fit in the specified field width and we want
+ to re-round the input number with fewer significant digits. Examples:
+
+ ma_gcvt(-9e-3, ..., 4, ...);
+ ma_gcvt(-9e-3, ..., 2, ...);
+ ma_gcvt(1.87e-3, ..., 4, ...);
+ ma_gcvt(55, ..., 1, ...);
+
+ We do our best to minimize such cases by:
+
+ - passing to dtoa() the field width as the number of significant digits
+
+ - removing the sign of the number early (and decreasing the width before
+ passing it to dtoa())
+
+ - choosing the proper format to preserve the most number of significant
+ digits.
+*/
+
+size_t ma_gcvt(double x, my_gcvt_arg_type type, int width, char *to,
+ my_bool *error)
+{
+ int decpt, sign, len, exp_len;
+ char *res, *src, *end, *dst= to, *dend= dst + width;
+ char buf[DTOA_BUFF_SIZE];
+ my_bool have_space, force_e_format;
+ DBUG_ASSERT(width > 0 && to != NULL);
+
+ /* We want to remove '-' from equations early */
+ if (x < 0.)
+ width--;
+
+ res= dtoa(x, 4, type == MY_GCVT_ARG_DOUBLE ? width : MIN(width, FLT_DIG),
+ &decpt, &sign, &end, buf, sizeof(buf));
+ if (decpt == DTOA_OVERFLOW)
+ {
+ dtoa_free(res, buf, sizeof(buf));
+ *to++= '0';
+ *to= '\0';
+ if (error != NULL)
+ *error= TRUE;
+ return 1;
+ }
+
+ if (error != NULL)
+ *error= FALSE;
+
+ src= res;
+ len= (int)(end - res);
+
+ /*
+ Number of digits in the exponent from the 'e' conversion.
+ The sign of the exponent is taken into account separetely, we don't need
+ to count it here.
+ */
+ exp_len= 1 + (decpt >= 101 || decpt <= -99) + (decpt >= 11 || decpt <= -9);
+
+ /*
+ Do we have enough space for all digits in the 'f' format?
+ Let 'len' be the number of significant digits returned by dtoa,
+ and F be the length of the resulting decimal representation.
+ Consider the following cases:
+ 1. decpt <= 0, i.e. we have "0.NNN" => F = len - decpt + 2
+ 2. 0 < decpt < len, i.e. we have "NNN.NNN" => F = len + 1
+ 3. len <= decpt, i.e. we have "NNN00" => F = decpt
+ */
+ have_space= (decpt <= 0 ? len - decpt + 2 :
+ decpt > 0 && decpt < len ? len + 1 :
+ decpt) <= width;
+ /*
+ The following is true when no significant digits can be placed with the
+ specified field width using the 'f' format, and the 'e' format
+ will not be truncated.
+ */
+ force_e_format= (decpt <= 0 && width <= 2 - decpt && width >= 3 + exp_len);
+ /*
+ Assume that we don't have enough space to place all significant digits in
+ the 'f' format. We have to choose between the 'e' format and the 'f' one
+ to keep as many significant digits as possible.
+ Let E and F be the lengths of decimal representaion in the 'e' and 'f'
+ formats, respectively. We want to use the 'f' format if, and only if F <= E.
+ Consider the following cases:
+ 1. decpt <= 0.
+ F = len - decpt + 2 (see above)
+ E = len + (len > 1) + 1 + 1 (decpt <= -99) + (decpt <= -9) + 1
+ ("N.NNe-MMM")
+ (F <= E) <=> (len == 1 && decpt >= -1) || (len > 1 && decpt >= -2)
+ We also need to ensure that if the 'f' format is chosen,
+ the field width allows us to place at least one significant digit
+ (i.e. width > 2 - decpt). If not, we prefer the 'e' format.
+ 2. 0 < decpt < len
+ F = len + 1 (see above)
+ E = len + 1 + 1 + ... ("N.NNeMMM")
+ F is always less than E.
+ 3. len <= decpt <= width
+ In this case we have enough space to represent the number in the 'f'
+ format, so we prefer it with some exceptions.
+ 4. width < decpt
+ The number cannot be represented in the 'f' format at all, always use
+ the 'e' 'one.
+ */
+ if ((have_space ||
+ /*
+ Not enough space, let's see if the 'f' format provides the most number
+ of significant digits.
+ */
+ ((decpt <= width && (decpt >= -1 || (decpt == -2 &&
+ (len > 1 || !force_e_format)))) &&
+ !force_e_format)) &&
+
+ /*
+ Use the 'e' format in some cases even if we have enough space for the
+ 'f' one. See comment for DBL_DIG.
+ */
+ (!have_space || (decpt >= -DBL_DIG + 1 &&
+ (decpt <= DBL_DIG || len > decpt))))
+ {
+ /* 'f' format */
+ int i;
+
+ width-= (decpt < len) + (decpt <= 0 ? 1 - decpt : 0);
+
+ /* Do we have to truncate any digits? */
+ if (width < len)
+ {
+ if (width < decpt)
+ {
+ if (error != NULL)
+ *error= TRUE;
+ width= decpt;
+ }
+
+ /*
+ We want to truncate (len - width) least significant digits after the
+ decimal point. For this we are calling dtoa with mode=5, passing the
+ number of significant digits = (len-decpt) - (len-width) = width-decpt
+ */
+ dtoa_free(res, buf, sizeof(buf));
+ res= dtoa(x, 5, width - decpt, &decpt, &sign, &end, buf, sizeof(buf));
+ src= res;
+ len= (int)(end - res);
+ }
+
+ if (len == 0)
+ {
+ /* Underflow. Just print '0' and exit */
+ *dst++= '0';
+ goto end;
+ }
+
+ /*
+ At this point we are sure we have enough space to put all digits
+ returned by dtoa
+ */
+ if (sign && dst < dend)
+ *dst++= '-';
+ if (decpt <= 0)
+ {
+ if (dst < dend)
+ *dst++= '0';
+ if (len > 0 && dst < dend)
+ *dst++= '.';
+ for (; decpt < 0 && dst < dend; decpt++)
+ *dst++= '0';
+ }
+
+ for (i= 1; i <= len && dst < dend; i++)
+ {
+ *dst++= *src++;
+ if (i == decpt && i < len && dst < dend)
+ *dst++= '.';
+ }
+ while (i++ <= decpt && dst < dend)
+ *dst++= '0';
+ }
+ else
+ {
+ /* 'e' format */
+ int decpt_sign= 0;
+
+ if (--decpt < 0)
+ {
+ decpt= -decpt;
+ width--;
+ decpt_sign= 1;
+ }
+ width-= 1 + exp_len; /* eNNN */
+
+ if (len > 1)
+ width--;
+
+ if (width <= 0)
+ {
+ /* Overflow */
+ if (error != NULL)
+ *error= TRUE;
+ width= 0;
+ }
+
+ /* Do we have to truncate any digits? */
+ if (width < len)
+ {
+ /* Yes, re-convert with a smaller width */
+ dtoa_free(res, buf, sizeof(buf));
+ res= dtoa(x, 4, width, &decpt, &sign, &end, buf, sizeof(buf));
+ src= res;
+ len= (int)(end - res);
+ if (--decpt < 0)
+ decpt= -decpt;
+ }
+ /*
+ At this point we are sure we have enough space to put all digits
+ returned by dtoa
+ */
+ if (sign && dst < dend)
+ *dst++= '-';
+ if (dst < dend)
+ *dst++= *src++;
+ if (len > 1 && dst < dend)
+ {
+ *dst++= '.';
+ while (src < end && dst < dend)
+ *dst++= *src++;
+ }
+ if (dst < dend)
+ *dst++= 'e';
+ if (decpt_sign && dst < dend)
+ *dst++= '-';
+
+ if (decpt >= 100 && dst < dend)
+ {
+ *dst++= decpt / 100 + '0';
+ decpt%= 100;
+ if (dst < dend)
+ *dst++= decpt / 10 + '0';
+ }
+ else if (decpt >= 10 && dst < dend)
+ *dst++= decpt / 10 + '0';
+ if (dst < dend)
+ *dst++= decpt % 10 + '0';
+
+ }
+
+end:
+ dtoa_free(res, buf, sizeof(buf));
+ *dst= '\0';
+
+ return dst - to;
+}
+
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/*
+ Original copy of the software is located at http://www.netlib.org/fp/dtoa.c
+ It was adjusted to serve MySQL server needs:
+ * strtod() was modified to not expect a zero-terminated string.
+ It now honors 'se' (end of string) argument as the input parameter,
+ not just as the output one.
+ * in dtoa(), in case of overflow/underflow/NaN result string now contains "0";
+ decpt is set to DTOA_OVERFLOW to indicate overflow.
+ * support for VAX, IBM mainframe and 16-bit hardware removed
+ * we always assume that 64-bit integer type is available
+ * support for Kernigan-Ritchie style headers (pre-ANSI compilers)
+ removed
+ * all gcc warnings ironed out
+ * we always assume multithreaded environment, so we had to change
+ memory allocation procedures to use stack in most cases;
+ malloc is used as the last resort.
+ * pow5mult rewritten to use pre-calculated pow5 list instead of
+ the one generated on the fly.
+*/
+
+
+/*
+ On a machine with IEEE extended-precision registers, it is
+ necessary to specify double-precision (53-bit) rounding precision
+ before invoking strtod or dtoa. If the machine uses (the equivalent
+ of) Intel 80x87 arithmetic, the call
+ _control87(PC_53, MCW_PC);
+ does this with many compilers. Whether this or another call is
+ appropriate depends on the compiler; for this to work, it may be
+ necessary to #include "float.h" or another system-dependent header
+ file.
+*/
+
+/*
+ #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ and dtoa should round accordingly.
+ #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ and Honor_FLT_ROUNDS is not #defined.
+
+ TODO: check if we can get rid of the above two
+*/
+
+typedef int32 Long;
+typedef uint32 ULong;
+typedef int64 LLong;
+typedef uint64 ULLong;
+
+typedef union { double d; ULong L[2]; } U;
+
+#if defined(WORDS_BIGENDIAN) || (defined(__FLOAT_WORD_ORDER) && \
+ (__FLOAT_WORD_ORDER == __BIG_ENDIAN))
+#define word0(x) (x)->L[0]
+#define word1(x) (x)->L[1]
+#else
+#define word0(x) (x)->L[1]
+#define word1(x) (x)->L[0]
+#endif
+
+#define dval(x) (x)->d
+
+/* #define P DBL_MANT_DIG */
+/* Ten_pmax= floor(P*log(2)/log(5)) */
+/* Bletch= (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max= floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max= floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Bias 1023
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#define Rounding rounding
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#define rounded_product(a,b) a*= b
+#define rounded_quotient(a,b) a/= b
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+#define FFFFFFFF 0xffffffffUL
+
+/* This is tested to be enough for dtoa */
+
+#define Kmax 15
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+ 2*sizeof(int) + y->wds*sizeof(ULong))
+
+/* Arbitrary-length integer */
+
+typedef struct Bigint
+{
+ union {
+ ULong *x; /* points right after this Bigint object */
+ struct Bigint *next; /* to maintain free lists */
+ } p;
+ int k; /* 2^k = maxwds */
+ int maxwds; /* maximum length in 32-bit words */
+ int sign; /* not zero if number is negative */
+ int wds; /* current length in 32-bit words */
+} Bigint;
+
+
+/* A simple stack-memory based allocator for Bigints */
+
+typedef struct Stack_alloc
+{
+ char *begin;
+ char *free;
+ char *end;
+ /*
+ Having list of free blocks lets us reduce maximum required amount
+ of memory from ~4000 bytes to < 1680 (tested on x86).
+ */
+ Bigint *freelist[Kmax+1];
+} Stack_alloc;
+
+
+/*
+ Try to allocate object on stack, and resort to malloc if all
+ stack memory is used. Ensure allocated objects to be aligned by the pointer
+ size in order to not break the alignment rules when storing a pointer to a
+ Bigint.
+*/
+
+static Bigint *Balloc(int k, Stack_alloc *alloc)
+{
+ Bigint *rv;
+ DBUG_ASSERT(k <= Kmax);
+ if (k <= Kmax && alloc->freelist[k])
+ {
+ rv= alloc->freelist[k];
+ alloc->freelist[k]= rv->p.next;
+ }
+ else
+ {
+ int x, len;
+
+ x= 1 << k;
+ len= MY_ALIGN(sizeof(Bigint) + x * sizeof(ULong), SIZEOF_CHARP);
+
+ if (alloc->free + len <= alloc->end)
+ {
+ rv= (Bigint*) alloc->free;
+ alloc->free+= len;
+ }
+ else
+ rv= (Bigint*) malloc(len);
+
+ rv->k= k;
+ rv->maxwds= x;
+ }
+ rv->sign= rv->wds= 0;
+ rv->p.x= (ULong*) (rv + 1);
+ return rv;
+}
+
+
+/*
+ If object was allocated on stack, try putting it to the free
+ list. Otherwise call free().
+*/
+
+static void Bfree(Bigint *v, Stack_alloc *alloc)
+{
+ char *gptr= (char*) v; /* generic pointer */
+ if (gptr < alloc->begin || gptr >= alloc->end)
+ free(gptr);
+ else if (v->k <= Kmax)
+ {
+ /*
+ Maintain free lists only for stack objects: this way we don't
+ have to bother with freeing lists in the end of dtoa;
+ heap should not be used normally anyway.
+ */
+ v->p.next= alloc->freelist[v->k];
+ alloc->freelist[v->k]= v;
+ }
+}
+
+
+/*
+ This is to place return value of dtoa in: tries to use stack
+ as well, but passes by free lists management and just aligns len by
+ the pointer size in order to not break the alignment rules when storing a
+ pointer to a Bigint.
+*/
+
+static char *dtoa_alloc(int i, Stack_alloc *alloc)
+{
+ char *rv;
+ int aligned_size= MY_ALIGN(i, SIZEOF_CHARP);
+ if (alloc->free + aligned_size <= alloc->end)
+ {
+ rv= alloc->free;
+ alloc->free+= aligned_size;
+ }
+ else
+ rv= malloc(i);
+ return rv;
+}
+
+
+/*
+ dtoa_free() must be used to free values s returned by dtoa()
+ This is the counterpart of dtoa_alloc()
+*/
+
+static void dtoa_free(char *gptr, char *buf, size_t buf_size)
+{
+ if (gptr < buf || gptr >= buf + buf_size)
+ free(gptr);
+}
+
+
+/* Bigint arithmetic functions */
+
+/* Multiply by m and add a */
+
+static Bigint *multadd(Bigint *b, int m, int a, Stack_alloc *alloc)
+{
+ int i, wds;
+ ULong *x;
+ ULLong carry, y;
+ Bigint *b1;
+
+ wds= b->wds;
+ x= b->p.x;
+ i= 0;
+ carry= a;
+ do
+ {
+ y= *x * (ULLong)m + carry;
+ carry= y >> 32;
+ *x++= (ULong)(y & FFFFFFFF);
+ }
+ while (++i < wds);
+ if (carry)
+ {
+ if (wds >= b->maxwds)
+ {
+ b1= Balloc(b->k+1, alloc);
+ Bcopy(b1, b);
+ Bfree(b, alloc);
+ b= b1;
+ }
+ b->p.x[wds++]= (ULong) carry;
+ b->wds= wds;
+ }
+ return b;
+}
+
+
+static int hi0bits(register ULong x)
+{
+ register int k= 0;
+
+ if (!(x & 0xffff0000))
+ {
+ k= 16;
+ x<<= 16;
+ }
+ if (!(x & 0xff000000))
+ {
+ k+= 8;
+ x<<= 8;
+ }
+ if (!(x & 0xf0000000))
+ {
+ k+= 4;
+ x<<= 4;
+ }
+ if (!(x & 0xc0000000))
+ {
+ k+= 2;
+ x<<= 2;
+ }
+ if (!(x & 0x80000000))
+ {
+ k++;
+ if (!(x & 0x40000000))
+ return 32;
+ }
+ return k;
+}
+
+
+static int lo0bits(ULong *y)
+{
+ register int k;
+ register ULong x= *y;
+
+ if (x & 7)
+ {
+ if (x & 1)
+ return 0;
+ if (x & 2)
+ {
+ *y= x >> 1;
+ return 1;
+ }
+ *y= x >> 2;
+ return 2;
+ }
+ k= 0;
+ if (!(x & 0xffff))
+ {
+ k= 16;
+ x>>= 16;
+ }
+ if (!(x & 0xff))
+ {
+ k+= 8;
+ x>>= 8;
+ }
+ if (!(x & 0xf))
+ {
+ k+= 4;
+ x>>= 4;
+ }
+ if (!(x & 0x3))
+ {
+ k+= 2;
+ x>>= 2;
+ }
+ if (!(x & 1))
+ {
+ k++;
+ x>>= 1;
+ if (!x)
+ return 32;
+ }
+ *y= x;
+ return k;
+}
+
+
+/* Convert integer to Bigint number */
+
+static Bigint *i2b(int i, Stack_alloc *alloc)
+{
+ Bigint *b;
+
+ b= Balloc(1, alloc);
+ b->p.x[0]= i;
+ b->wds= 1;
+ return b;
+}
+
+
+/* Multiply two Bigint numbers */
+
+static Bigint *mult(Bigint *a, Bigint *b, Stack_alloc *alloc)
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+ ULLong carry, z;
+
+ if (a->wds < b->wds)
+ {
+ c= a;
+ a= b;
+ b= c;
+ }
+ k= a->k;
+ wa= a->wds;
+ wb= b->wds;
+ wc= wa + wb;
+ if (wc > a->maxwds)
+ k++;
+ c= Balloc(k, alloc);
+ for (x= c->p.x, xa= x + wc; x < xa; x++)
+ *x= 0;
+ xa= a->p.x;
+ xae= xa + wa;
+ xb= b->p.x;
+ xbe= xb + wb;
+ xc0= c->p.x;
+ for (; xb < xbe; xc0++)
+ {
+ if ((y= *xb++))
+ {
+ x= xa;
+ xc= xc0;
+ carry= 0;
+ do
+ {
+ z= *x++ * (ULLong)y + *xc + carry;
+ carry= z >> 32;
+ *xc++= (ULong) (z & FFFFFFFF);
+ }
+ while (x < xae);
+ *xc= (ULong) carry;
+ }
+ }
+ for (xc0= c->p.x, xc= xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds= wc;
+ return c;
+}
+
+
+/*
+ Precalculated array of powers of 5: tested to be enough for
+ vasting majority of dtoa_r cases.
+*/
+
+static ULong powers5[]=
+{
+ 625UL,
+
+ 390625UL,
+
+ 2264035265UL, 35UL,
+
+ 2242703233UL, 762134875UL, 1262UL,
+
+ 3211403009UL, 1849224548UL, 3668416493UL, 3913284084UL, 1593091UL,
+
+ 781532673UL, 64985353UL, 253049085UL, 594863151UL, 3553621484UL,
+ 3288652808UL, 3167596762UL, 2788392729UL, 3911132675UL, 590UL,
+
+ 2553183233UL, 3201533787UL, 3638140786UL, 303378311UL, 1809731782UL,
+ 3477761648UL, 3583367183UL, 649228654UL, 2915460784UL, 487929380UL,
+ 1011012442UL, 1677677582UL, 3428152256UL, 1710878487UL, 1438394610UL,
+ 2161952759UL, 4100910556UL, 1608314830UL, 349175UL
+};
+
+
+static Bigint p5_a[]=
+{
+ /* { x } - k - maxwds - sign - wds */
+ { { powers5 }, 1, 1, 0, 1 },
+ { { powers5 + 1 }, 1, 1, 0, 1 },
+ { { powers5 + 2 }, 1, 2, 0, 2 },
+ { { powers5 + 4 }, 2, 3, 0, 3 },
+ { { powers5 + 7 }, 3, 5, 0, 5 },
+ { { powers5 + 12 }, 4, 10, 0, 10 },
+ { { powers5 + 22 }, 5, 19, 0, 19 }
+};
+
+#define P5A_MAX (sizeof(p5_a)/sizeof(*p5_a) - 1)
+
+static Bigint *pow5mult(Bigint *b, int k, Stack_alloc *alloc)
+{
+ Bigint *b1, *p5, *p51=NULL;
+ int i;
+ static int p05[3]= { 5, 25, 125 };
+ my_bool overflow= FALSE;
+
+ if ((i= k & 3))
+ b= multadd(b, p05[i-1], 0, alloc);
+
+ if (!(k>>= 2))
+ return b;
+ p5= p5_a;
+ for (;;)
+ {
+ if (k & 1)
+ {
+ b1= mult(b, p5, alloc);
+ Bfree(b, alloc);
+ b= b1;
+ }
+ if (!(k>>= 1))
+ break;
+ /* Calculate next power of 5 */
+ if (overflow)
+ {
+ p51= mult(p5, p5, alloc);
+ Bfree(p5, alloc);
+ p5= p51;
+ }
+ else if (p5 < p5_a + P5A_MAX)
+ ++p5;
+ else if (p5 == p5_a + P5A_MAX)
+ {
+ p5= mult(p5, p5, alloc);
+ overflow= TRUE;
+ }
+ }
+ if (p51)
+ Bfree(p51, alloc);
+ return b;
+}
+
+
+static Bigint *lshift(Bigint *b, int k, Stack_alloc *alloc)
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *x, *x1, *xe, z;
+
+ n= k >> 5;
+ k1= b->k;
+ n1= n + b->wds + 1;
+ for (i= b->maxwds; n1 > i; i<<= 1)
+ k1++;
+ b1= Balloc(k1, alloc);
+ x1= b1->p.x;
+ for (i= 0; i < n; i++)
+ *x1++= 0;
+ x= b->p.x;
+ xe= x + b->wds;
+ if (k&= 0x1f)
+ {
+ k1= 32 - k;
+ z= 0;
+ do
+ {
+ *x1++= *x << k | z;
+ z= *x++ >> k1;
+ }
+ while (x < xe);
+ if ((*x1= z))
+ ++n1;
+ }
+ else
+ do
+ *x1++= *x++;
+ while (x < xe);
+ b1->wds= n1 - 1;
+ Bfree(b, alloc);
+ return b1;
+}
+
+
+static int cmp(Bigint *a, Bigint *b)
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i= a->wds;
+ j= b->wds;
+ if (i-= j)
+ return i;
+ xa0= a->p.x;
+ xa= xa0 + j;
+ xb0= b->p.x;
+ xb= xb0 + j;
+ for (;;)
+ {
+ if (*--xa != *--xb)
+ return *xa < *xb ? -1 : 1;
+ if (xa <= xa0)
+ break;
+ }
+ return 0;
+}
+
+
+static Bigint *diff(Bigint *a, Bigint *b, Stack_alloc *alloc)
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+ ULLong borrow, y;
+
+ i= cmp(a,b);
+ if (!i)
+ {
+ c= Balloc(0, alloc);
+ c->wds= 1;
+ c->p.x[0]= 0;
+ return c;
+ }
+ if (i < 0)
+ {
+ c= a;
+ a= b;
+ b= c;
+ i= 1;
+ }
+ else
+ i= 0;
+ c= Balloc(a->k, alloc);
+ c->sign= i;
+ wa= a->wds;
+ xa= a->p.x;
+ xae= xa + wa;
+ wb= b->wds;
+ xb= b->p.x;
+ xbe= xb + wb;
+ xc= c->p.x;
+ borrow= 0;
+ do
+ {
+ y= (ULLong)*xa++ - *xb++ - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *xc++= (ULong) (y & FFFFFFFF);
+ }
+ while (xb < xbe);
+ while (xa < xae)
+ {
+ y= *xa++ - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *xc++= (ULong) (y & FFFFFFFF);
+ }
+ while (!*--xc)
+ wa--;
+ c->wds= wa;
+ return c;
+}
+
+
+static Bigint *d2b(U *d, int *e, int *bits, Stack_alloc *alloc)
+{
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+ int i;
+#define d0 word0(d)
+#define d1 word1(d)
+
+ b= Balloc(1, alloc);
+ x= b->p.x;
+
+ z= d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+ if ((de= (int)(d0 >> Exp_shift)))
+ z|= Exp_msk1;
+ if ((y= d1))
+ {
+ if ((k= lo0bits(&y)))
+ {
+ x[0]= y | z << (32 - k);
+ z>>= k;
+ }
+ else
+ x[0]= y;
+ i= b->wds= (x[1]= z) ? 2 : 1;
+ }
+ else
+ {
+ k= lo0bits(&z);
+ x[0]= z;
+ i= b->wds= 1;
+ k+= 32;
+ }
+ if (de)
+ {
+ *e= de - Bias - (P-1) + k;
+ *bits= P - k;
+ }
+ else
+ {
+ *e= de - Bias - (P-1) + 1 + k;
+ *bits= 32*i - hi0bits(x[i-1]);
+ }
+ return b;
+#undef d0
+#undef d1
+}
+
+
+static const double tens[] =
+{
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+};
+
+static const double bigtens[]= { 1e16, 1e32, 1e64, 1e128, 1e256 };
+static const double tinytens[]=
+{ 1e-16, 1e-32, 1e-64, 1e-128,
+ 9007199254740992.*9007199254740992.e-256 /* = 2^106 * 1e-53 */
+};
+/*
+ The factor of 2^53 in tinytens[4] helps us avoid setting the underflow
+ flag unnecessarily. It leads to a song and dance at the end of strtod.
+*/
+#define Scale_Bit 0x10
+#define n_bigtens 5
+
+
+static int quorem(Bigint *b, Bigint *S)
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+ ULLong borrow, carry, y, ys;
+
+ n= S->wds;
+ if (b->wds < n)
+ return 0;
+ sx= S->p.x;
+ sxe= sx + --n;
+ bx= b->p.x;
+ bxe= bx + n;
+ q= *bxe / (*sxe + 1); /* ensure q <= true quotient */
+ if (q)
+ {
+ borrow= 0;
+ carry= 0;
+ do
+ {
+ ys= *sx++ * (ULLong)q + carry;
+ carry= ys >> 32;
+ y= *bx - (ys & FFFFFFFF) - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *bx++= (ULong) (y & FFFFFFFF);
+ }
+ while (sx <= sxe);
+ if (!*bxe)
+ {
+ bx= b->p.x;
+ while (--bxe > bx && !*bxe)
+ --n;
+ b->wds= n;
+ }
+ }
+ if (cmp(b, S) >= 0)
+ {
+ q++;
+ borrow= 0;
+ carry= 0;
+ bx= b->p.x;
+ sx= S->p.x;
+ do
+ {
+ ys= *sx++ + carry;
+ carry= ys >> 32;
+ y= *bx - (ys & FFFFFFFF) - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *bx++= (ULong) (y & FFFFFFFF);
+ }
+ while (sx <= sxe);
+ bx= b->p.x;
+ bxe= bx + n;
+ if (!*bxe)
+ {
+ while (--bxe > bx && !*bxe)
+ --n;
+ b->wds= n;
+ }
+ }
+ return q;
+}
+
+
+/*
+ dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+
+ Inspired by "How to Print Floating-Point Numbers Accurately" by
+ Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].
+
+ Modifications:
+ 1. Rather than iterating, we use a simple numeric overestimate
+ to determine k= floor(log10(d)). We scale relevant
+ quantities using O(log2(k)) rather than O(k) multiplications.
+ 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ try to generate digits strictly left to right. Instead, we
+ compute with fewer bits and propagate the carry if necessary
+ when rounding the final digit up. This is often faster.
+ 3. Under the assumption that input will be rounded nearest,
+ mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ That is, we allow equality in stopping tests when the
+ round-nearest rule will give the same floating-point value
+ as would satisfaction of the stopping test with strict
+ inequality.
+ 4. We remove common factors of powers of 2 from relevant
+ quantities.
+ 5. When converting floating-point integers less than 1e16,
+ we use floating-point arithmetic rather than resorting
+ to multiple-precision integers.
+ 6. When asked to produce fewer than 15 digits, we first try
+ to get by with floating-point arithmetic; we resort to
+ multiple-precision integer arithmetic only if we cannot
+ guarantee that the floating-point calculation has given
+ the correctly rounded result. For k requested digits and
+ "uniformly" distributed input, the probability is
+ something like 10^(k-15) that we must resort to the Long
+ calculation.
+ */
+
+static char *dtoa(double dd, int mode, int ndigits, int *decpt, int *sign,
+ char **rve, char *buf, size_t buf_size)
+{
+ /*
+ Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to DTOA_OVERFLOW.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> MAX(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: don't try
+ fast floating-point estimate (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int bbits, b2, b5, be, dig, i, ieps, UNINIT_VAR(ilim), ilim0,
+ UNINIT_VAR(ilim1), j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+ int denorm;
+ ULong x;
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ U d2, eps, u;
+ double ds;
+ char *s, *s0;
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+ Stack_alloc alloc;
+
+ alloc.begin= alloc.free= buf;
+ alloc.end= buf + buf_size;
+ memset(alloc.freelist, 0, sizeof(alloc.freelist));
+
+ u.d= dd;
+ if (word0(&u) & Sign_bit)
+ {
+ /* set sign for everything, including 0's and NaNs */
+ *sign= 1;
+ word0(&u) &= ~Sign_bit; /* clear sign bit */
+ }
+ else
+ *sign= 0;
+
+ /* If infinity, set decpt to DTOA_OVERFLOW, if 0 set it to 1 */
+ if (((word0(&u) & Exp_mask) == Exp_mask && (*decpt= DTOA_OVERFLOW)) ||
+ (!dval(&u) && (*decpt= 1)))
+ {
+ /* Infinity, NaN, 0 */
+ char *res= (char*) dtoa_alloc(2, &alloc);
+ res[0]= '0';
+ res[1]= '\0';
+ if (rve)
+ *rve= res + 1;
+ return res;
+ }
+
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding= Flt_Rounds) >= 2)
+ {
+ if (*sign)
+ rounding= rounding == 2 ? 0 : 2;
+ else
+ if (rounding != 2)
+ rounding= 0;
+ }
+#endif
+
+ b= d2b(&u, &be, &bbits, &alloc);
+ if ((i= (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1))))
+ {
+ dval(&d2)= dval(&u);
+ word0(&d2) &= Frac_mask1;
+ word0(&d2) |= Exp_11;
+
+ /*
+ log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ log10(x) = log(x) / log(10)
+ ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ log10(d)= (i-Bias)*log(2)/log(10) + log10(d2)
+
+ This suggests computing an approximation k to log10(d) by
+
+ k= (i - Bias)*0.301029995663981
+ + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+
+ We want k to be too large rather than too small.
+ The error in the first-order Taylor series approximation
+ is in our favor, so we just round up the constant enough
+ to compensate for any error in the multiplication of
+ (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ adding 1e-13 to the constant term more than suffices.
+ Hence we adjust the constant term to 0.1760912590558.
+ (We could get a more accurate k by invoking log10,
+ but this is probably not worthwhile.)
+ */
+
+ i-= Bias;
+ denorm= 0;
+ }
+ else
+ {
+ /* d is denormalized */
+
+ i= bbits + be + (Bias + (P-1) - 1);
+ x= i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32)
+ : word1(&u) << (32 - i);
+ dval(&d2)= x;
+ word0(&d2)-= 31*Exp_msk1; /* adjust exponent */
+ i-= (Bias + (P-1) - 1) + 1;
+ denorm= 1;
+ }
+ ds= (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+ k= (int)ds;
+ if (ds < 0. && ds != k)
+ k--; /* want k= floor(ds) */
+ k_check= 1;
+ if (k >= 0 && k <= Ten_pmax)
+ {
+ if (dval(&u) < tens[k])
+ k--;
+ k_check= 0;
+ }
+ j= bbits - i - 1;
+ if (j >= 0)
+ {
+ b2= 0;
+ s2= j;
+ }
+ else
+ {
+ b2= -j;
+ s2= 0;
+ }
+ if (k >= 0)
+ {
+ b5= 0;
+ s5= k;
+ s2+= k;
+ }
+ else
+ {
+ b2-= k;
+ b5= -k;
+ s5= 0;
+ }
+ if (mode < 0 || mode > 9)
+ mode= 0;
+
+#ifdef Check_FLT_ROUNDS
+ try_quick= Rounding == 1;
+#else
+ try_quick= 1;
+#endif
+
+ if (mode > 5)
+ {
+ mode-= 4;
+ try_quick= 0;
+ }
+ leftright= 1;
+ switch (mode) {
+ case 0:
+ case 1:
+ ilim= ilim1= -1;
+ i= 18;
+ ndigits= 0;
+ break;
+ case 2:
+ leftright= 0;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits= 1;
+ ilim= ilim1= i= ndigits;
+ break;
+ case 3:
+ leftright= 0;
+ /* no break */
+ case 5:
+ i= ndigits + k + 1;
+ ilim= i;
+ ilim1= i - 1;
+ if (i <= 0)
+ i= 1;
+ }
+ s= s0= dtoa_alloc(i, &alloc);
+
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1 && rounding != 1)
+ leftright= 0;
+#endif
+
+ if (ilim >= 0 && ilim <= Quick_max && try_quick)
+ {
+ /* Try to get by with floating-point arithmetic. */
+ i= 0;
+ dval(&d2)= dval(&u);
+ k0= k;
+ ilim0= ilim;
+ ieps= 2; /* conservative */
+ if (k > 0)
+ {
+ ds= tens[k&0xf];
+ j= k >> 4;
+ if (j & Bletch)
+ {
+ /* prevent overflows */
+ j&= Bletch - 1;
+ dval(&u)/= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for (; j; j>>= 1, i++)
+ {
+ if (j & 1)
+ {
+ ieps++;
+ ds*= bigtens[i];
+ }
+ }
+ dval(&u)/= ds;
+ }
+ else if ((j1= -k))
+ {
+ dval(&u)*= tens[j1 & 0xf];
+ for (j= j1 >> 4; j; j>>= 1, i++)
+ {
+ if (j & 1)
+ {
+ ieps++;
+ dval(&u)*= bigtens[i];
+ }
+ }
+ }
+ if (k_check && dval(&u) < 1. && ilim > 0)
+ {
+ if (ilim1 <= 0)
+ goto fast_failed;
+ ilim= ilim1;
+ k--;
+ dval(&u)*= 10.;
+ ieps++;
+ }
+ dval(&eps)= ieps*dval(&u) + 7.;
+ word0(&eps)-= (P-1)*Exp_msk1;
+ if (ilim == 0)
+ {
+ S= mhi= 0;
+ dval(&u)-= 5.;
+ if (dval(&u) > dval(&eps))
+ goto one_digit;
+ if (dval(&u) < -dval(&eps))
+ goto no_digits;
+ goto fast_failed;
+ }
+ if (leftright)
+ {
+ /* Use Steele & White method of only generating digits needed. */
+ dval(&eps)= 0.5/tens[ilim-1] - dval(&eps);
+ for (i= 0;;)
+ {
+ L= (Long) dval(&u);
+ dval(&u)-= L;
+ *s++= '0' + (int)L;
+ if (dval(&u) < dval(&eps))
+ goto ret1;
+ if (1. - dval(&u) < dval(&eps))
+ goto bump_up;
+ if (++i >= ilim)
+ break;
+ dval(&eps)*= 10.;
+ dval(&u)*= 10.;
+ }
+ }
+ else
+ {
+ /* Generate ilim digits, then fix them up. */
+ dval(&eps)*= tens[ilim-1];
+ for (i= 1;; i++, dval(&u)*= 10.)
+ {
+ L= (Long)(dval(&u));
+ if (!(dval(&u)-= L))
+ ilim= i;
+ *s++= '0' + (int)L;
+ if (i == ilim)
+ {
+ if (dval(&u) > 0.5 + dval(&eps))
+ goto bump_up;
+ else if (dval(&u) < 0.5 - dval(&eps))
+ {
+ while (*--s == '0');
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+ }
+ fast_failed:
+ s= s0;
+ dval(&u)= dval(&d2);
+ k= k0;
+ ilim= ilim0;
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be >= 0 && k <= Int_max)
+ {
+ /* Yes. */
+ ds= tens[k];
+ if (ndigits < 0 && ilim <= 0)
+ {
+ S= mhi= 0;
+ if (ilim < 0 || dval(&u) <= 5*ds)
+ goto no_digits;
+ goto one_digit;
+ }
+ for (i= 1;; i++, dval(&u)*= 10.)
+ {
+ L= (Long)(dval(&u) / ds);
+ dval(&u)-= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(&u) < 0)
+ {
+ L--;
+ dval(&u)+= ds;
+ }
+#endif
+ *s++= '0' + (int)L;
+ if (!dval(&u))
+ {
+ break;
+ }
+ if (i == ilim)
+ {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ {
+ switch (rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+ }
+#endif
+ dval(&u)+= dval(&u);
+ if (dval(&u) > ds || (dval(&u) == ds && L & 1))
+ {
+bump_up:
+ while (*--s == '9')
+ if (s == s0)
+ {
+ k++;
+ *s= '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+ }
+
+ m2= b2;
+ m5= b5;
+ mhi= mlo= 0;
+ if (leftright)
+ {
+ i = denorm ? be + (Bias + (P-1) - 1 + 1) : 1 + P - bbits;
+ b2+= i;
+ s2+= i;
+ mhi= i2b(1, &alloc);
+ }
+ if (m2 > 0 && s2 > 0)
+ {
+ i= m2 < s2 ? m2 : s2;
+ b2-= i;
+ m2-= i;
+ s2-= i;
+ }
+ if (b5 > 0)
+ {
+ if (leftright)
+ {
+ if (m5 > 0)
+ {
+ mhi= pow5mult(mhi, m5, &alloc);
+ b1= mult(mhi, b, &alloc);
+ Bfree(b, &alloc);
+ b= b1;
+ }
+ if ((j= b5 - m5))
+ b= pow5mult(b, j, &alloc);
+ }
+ else
+ b= pow5mult(b, b5, &alloc);
+ }
+ S= i2b(1, &alloc);
+ if (s5 > 0)
+ S= pow5mult(S, s5, &alloc);
+
+ /* Check for special case that d is a normalized power of 2. */
+
+ spec_case= 0;
+ if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && rounding == 1
+#endif
+ )
+ {
+ if (!word1(&u) && !(word0(&u) & Bndry_mask) &&
+ word0(&u) & (Exp_mask & ~Exp_msk1)
+ )
+ {
+ /* The special case */
+ b2+= Log2P;
+ s2+= Log2P;
+ spec_case= 1;
+ }
+ }
+
+ /*
+ Arrange for convenient computation of quotients:
+ shift left if necessary so divisor has 4 leading 0 bits.
+
+ Perhaps we should just compute leading 28 bits of S once
+ a nd for all and pass them and a shift to quorem, so it
+ can do shifts and ors to compute the numerator for q.
+ */
+ if ((i= ((s5 ? 32 - hi0bits(S->p.x[S->wds-1]) : 1) + s2) & 0x1f))
+ i= 32 - i;
+ if (i > 4)
+ {
+ i-= 4;
+ b2+= i;
+ m2+= i;
+ s2+= i;
+ }
+ else if (i < 4)
+ {
+ i+= 28;
+ b2+= i;
+ m2+= i;
+ s2+= i;
+ }
+ if (b2 > 0)
+ b= lshift(b, b2, &alloc);
+ if (s2 > 0)
+ S= lshift(S, s2, &alloc);
+ if (k_check)
+ {
+ if (cmp(b,S) < 0)
+ {
+ k--;
+ /* we botched the k estimate */
+ b= multadd(b, 10, 0, &alloc);
+ if (leftright)
+ mhi= multadd(mhi, 10, 0, &alloc);
+ ilim= ilim1;
+ }
+ }
+ if (ilim <= 0 && (mode == 3 || mode == 5))
+ {
+ if (ilim < 0 || cmp(b,S= multadd(S,5,0, &alloc)) <= 0)
+ {
+ /* no digits, fcvt style */
+no_digits:
+ k= -1 - ndigits;
+ goto ret;
+ }
+one_digit:
+ *s++= '1';
+ k++;
+ goto ret;
+ }
+ if (leftright)
+ {
+ if (m2 > 0)
+ mhi= lshift(mhi, m2, &alloc);
+
+ /*
+ Compute mlo -- check for special case that d is a normalized power of 2.
+ */
+
+ mlo= mhi;
+ if (spec_case)
+ {
+ mhi= Balloc(mhi->k, &alloc);
+ Bcopy(mhi, mlo);
+ mhi= lshift(mhi, Log2P, &alloc);
+ }
+
+ for (i= 1;;i++)
+ {
+ dig= quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string that will round to d? */
+ j= cmp(b, mlo);
+ delta= diff(S, mhi, &alloc);
+ j1= delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta, &alloc);
+ if (j1 == 0 && mode != 1 && !(word1(&u) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && rounding >= 1
+#endif
+ )
+ {
+ if (dig == '9')
+ goto round_9_up;
+ if (j > 0)
+ dig++;
+ *s++= dig;
+ goto ret;
+ }
+ if (j < 0 || (j == 0 && mode != 1 && !(word1(&u) & 1)))
+ {
+ if (!b->p.x[0] && b->wds <= 1)
+ {
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch (rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0)
+ {
+ b= lshift(b, 1, &alloc);
+ j1= cmp(b, S);
+ if ((j1 > 0 || (j1 == 0 && dig & 1))
+ && dig++ == '9')
+ goto round_9_up;
+ }
+accept_dig:
+ *s++= dig;
+ goto ret;
+ }
+ if (j1 > 0)
+ {
+#ifdef Honor_FLT_ROUNDS
+ if (!rounding)
+ goto accept_dig;
+#endif
+ if (dig == '9')
+ { /* possible if i == 1 */
+round_9_up:
+ *s++= '9';
+ goto roundoff;
+ }
+ *s++= dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+keep_dig:
+#endif
+ *s++= dig;
+ if (i == ilim)
+ break;
+ b= multadd(b, 10, 0, &alloc);
+ if (mlo == mhi)
+ mlo= mhi= multadd(mhi, 10, 0, &alloc);
+ else
+ {
+ mlo= multadd(mlo, 10, 0, &alloc);
+ mhi= multadd(mhi, 10, 0, &alloc);
+ }
+ }
+ }
+ else
+ for (i= 1;; i++)
+ {
+ *s++= dig= quorem(b,S) + '0';
+ if (!b->p.x[0] && b->wds <= 1)
+ {
+ goto ret;
+ }
+ if (i >= ilim)
+ break;
+ b= multadd(b, 10, 0, &alloc);
+ }
+
+ /* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+ switch (rounding) {
+ case 0: goto trimzeros;
+ case 2: goto roundoff;
+ }
+#endif
+ b= lshift(b, 1, &alloc);
+ j= cmp(b, S);
+ if (j > 0 || (j == 0 && dig & 1))
+ {
+roundoff:
+ while (*--s == '9')
+ if (s == s0)
+ {
+ k++;
+ *s++= '1';
+ goto ret;
+ }
+ ++*s++;
+ }
+ else
+ {
+#ifdef Honor_FLT_ROUNDS
+trimzeros:
+#endif
+ while (*--s == '0');
+ s++;
+ }
+ret:
+ Bfree(S, &alloc);
+ if (mhi)
+ {
+ if (mlo && mlo != mhi)
+ Bfree(mlo, &alloc);
+ Bfree(mhi, &alloc);
+ }
+ret1:
+ Bfree(b, &alloc);
+ *s= 0;
+ *decpt= k + 1;
+ if (rve)
+ *rve= s;
+ return s0;
+}
diff --git a/mysql/libmariadb/ma_errmsg.c b/mysql/libmariadb/ma_errmsg.c
new file mode 100644
index 0000000..5c7884b
--- /dev/null
+++ b/mysql/libmariadb/ma_errmsg.c
@@ -0,0 +1,170 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Error messages for MySQL clients */
+/* error messages for the demon is in share/language/errmsg.sys */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "errmsg.h"
+#include <stdarg.h>
+
+const char *SQLSTATE_UNKNOWN= "HY000";
+
+#ifdef GERMAN
+const char *client_errors[]=
+{
+ "Unbekannter MySQL Fehler",
+ "Kann UNIX-Socket nicht anlegen (%d)",
+ "Keine Verbindung zu lokalem MySQL Server, socket: '%-.64s' (%d)",
+ "Keine Verbindung zu MySQL Server auf %-.64s (%d)",
+ "Kann TCP/IP-Socket nicht anlegen (%d)",
+ "Unbekannter MySQL Server Host (%-.64s) (%d)",
+ "MySQL Server nicht vorhanden",
+ "Protokolle ungleich. Server Version = % d Client Version = %d",
+ "MySQL client got out of memory",
+ "Wrong host info",
+ "Localhost via UNIX socket",
+ "%-.64s via TCP/IP",
+ "Error in server handshake",
+ "Lost connection to MySQL server during query",
+ "Commands out of sync; you can't run this command now",
+ "Verbindung ueber Named Pipe; Host: %-.64s",
+ "Kann nicht auf Named Pipe warten. Host: %-.64s pipe: %-.32s (%lu)",
+ "Kann Named Pipe nicht oeffnen. Host: %-.64s pipe: %-.32s (%lu)",
+ "Kann den Status der Named Pipe nicht setzen. Host: %-.64s pipe: %-.32s (%lu)",
+ "Can't initialize character set %-.64s (path: %-.64s)",
+ "Got packet bigger than 'max_allowed_packet'"
+};
+
+/* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */
+
+#elif defined PORTUGUESE
+const char *client_errors[]=
+{
+ "Erro desconhecido do MySQL",
+ "N�o pode criar 'UNIX socket' (%d)",
+ "N�o pode se conectar ao servidor MySQL local atrav�s do 'socket' '%-.64s' (%d)",
+ "N�o pode se conectar ao servidor MySQL em '%-.64s' (%d)",
+ "N�o pode criar 'socket TCP/IP' (%d)",
+ "'Host' servidor MySQL '%-.64s' (%d) desconhecido",
+ "Servidor MySQL desapareceu",
+ "Incompatibilidade de protocolos. Vers�o do Servidor: %d - Vers�o do Cliente: %d",
+ "Cliente do MySQL com falta de mem�ria",
+ "Informa��o inv�lida de 'host'",
+ "Localhost via 'UNIX socket'",
+ "%-.64s via 'TCP/IP'",
+ "Erro na negocia��o de acesso ao servidor",
+ "Conex�o perdida com servidor MySQL durante 'query'",
+ "Comandos fora de sincronismo. Voc� n�o pode executar este comando agora",
+ "%-.64s via 'named pipe'",
+ "N�o pode esperar pelo 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
+ "N�o pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
+ "N�o pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
+ "N�o pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)",
+ "Obteve pacote maior do que 'max_allowed_packet'"
+};
+
+#else /* ENGLISH */
+const char *client_errors[]=
+{
+/* 2000 */ "Unknown MySQL error",
+/* 2001 */ "Can't create UNIX socket (%d)",
+/* 2002 */ "Can't connect to local MySQL server through socket '%-.64s' (%d)",
+/* 2003 */ "Can't connect to MySQL server on '%-.64s' (%d)",
+/* 2004 */ "Can't create TCP/IP socket (%d)",
+/* 2005 */ "Unknown MySQL server host '%-.100s' (%d)",
+/* 2006 */ "MySQL server has gone away",
+/* 2007 */ "Protocol mismatch. Server Version = %d Client Version = %d",
+/* 2008 */ "MySQL client run out of memory",
+/* 2009 */ "Wrong host info",
+/* 2010 */ "Localhost via UNIX socket",
+/* 2011 */ "%-.64s via TCP/IP",
+/* 2012 */ "Error in server handshake",
+/* 2013 */ "Lost connection to MySQL server during query",
+/* 2014 */ "Commands out of sync; you can't run this command now",
+/* 2015 */ "%-.64s via named pipe",
+/* 2016 */ "Can't wait for named pipe to host: %-.64s pipe: %-.32s (%lu)",
+/* 2017 */ "Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)",
+/* 2018 */ "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)",
+/* 2019 */ "Can't initialize character set %-.64s (path: %-.64s)",
+/* 2020 */ "Got packet bigger than 'max_allowed_packet'",
+/* 2021 */ "",
+/* 2022 */ "",
+/* 2023 */ "",
+/* 2024 */ "",
+/* 2025 */ "",
+/* 2026 */ "SSL connection error: %-.100s",
+/* 2027 */ "received malformed packet",
+/* 2028 */ "",
+/* 2029 */ "",
+/* 2030 */ "Statement is not prepared",
+/* 2031 */ "No data supplied for parameters in prepared statement",
+/* 2032 */ "Data truncated",
+/* 2033 */ "",
+/* 2034 */ "Invalid parameter number",
+/* 2035 */ "Invalid buffer type: %d (paraneter: %d)",
+/* 2036 */ "Buffer type is not supported",
+/* 2037 */ "Shared memory: %-.64s",
+/* 2038 */ "Shared memory connection failed during %s. (%lu)",
+/* 2039 */ "",
+/* 2040 */ "",
+/* 2041 */ "",
+/* 2042 */ "",
+/* 2043 */ "",
+/* 2044 */ "",
+/* 2045 */ "",
+/* 2046 */ "",
+/* 2047 */ "Wrong or unknown protocol",
+/* 2048 */ "",
+/* 2049 */ "Connection with old authentication protocol refused.",
+/* 2050 */ "",
+/* 2051 */ "",
+/* 2052 */ "Prepared statement contains no metadata",
+/* 2053 */ "",
+/* 2054 */ "This feature is not implemented or disabled",
+/* 2055 */ "Lost connection to MySQL server at '%s', system error: %d",
+/* 2056 */ "Server closed statement due to a prior %s function call",
+/* 2057 */ "The number of parameters in bound buffers differs from number of columns in resultset",
+/* 2059 */ "Can't connect twice. Already connected",
+/* 2058 */ "Plugin %s could not be loaded: %s",
+/* 2059 */ "An attribute with same name already exists"
+/* 2060 */ "Plugin doesn't support this function",
+ ""
+};
+#endif
+
+const char *mariadb_client_errors[] =
+{
+ /* 5000 */ "Creating an event failed (Errorcode: %d)",
+ /* 5001 */ "Bind to local interface '-.%64s' failed (Errorcode: %d)",
+ /* 5002 */ "Connection type doesn't support asynchronous IO operations",
+ /* 5003 */ "Server doesn't support function '%s'",
+ /* 5004 */ "File '%s' not found (Errcode: %d)",
+ /* 5005 */ "Error reading file '%s' (Errcode: %d)",
+ /* 5006 */ "Bulk operation without parameters is not supported",
+ ""
+};
+
+const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0};
+char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
+
+void init_client_errs(void)
+{
+ my_errmsg[CLIENT_ERRMAP] = &client_errors[0];
+}
+
diff --git a/mysql/libmariadb/ma_hash.c b/mysql/libmariadb/ma_hash.c
new file mode 100644
index 0000000..8100171
--- /dev/null
+++ b/mysql/libmariadb/ma_hash.c
@@ -0,0 +1,583 @@
+/************************************************************************************
+ Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
+ Monty Program AB, 2016 MariaDB Corporation 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 <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include "ma_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)
+{
+ hash->records=0;
+ if (ma_init_dynamic_array_ci(&hash->array,sizeof(HASH_LINK),size,0))
+ {
+ hash->free=0; /* Allow call to hash_free */
+ 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;
+ return(0);
+}
+
+
+void hash_free(HASH *hash)
+{
+ 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;
+ }
+ ma_delete_dynamic(&hash->array);
+ hash->records=0;
+ 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
+*/
+
+static inline char*
+hash_key(HASH *hash,const uchar *record,uint *length,my_bool first)
+{
+ if (hash->get_key)
+ return (char *)(*hash->get_key)(record,(uint *)length,first);
+ *length=hash->key_length;
+ return (char*) 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 */
+static 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 */
+
+void* hash_search(HASH *hash,const uchar *key,uint length)
+{
+ HASH_LINK *pos;
+ uint flag,idx;
+
+ 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))
+ {
+ hash->current_record= idx;
+ 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;
+ return(0);
+}
+
+ /* Get next record with identical key */
+ /* Can only be called if previous calls was hash_search */
+
+void *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= NULL,*ptr_to_rec2= NULL;
+ HASH_LINK *data,*empty,*gpos= NULL,*gpos2 = NULL,*pos;
+
+ LINT_INIT(gpos); LINT_INIT(gpos2);
+ LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
+
+ flag=0;
+ if (!(empty=(HASH_LINK*) ma_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;
+ if (!hash->records)
+ 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)
+ 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:
+ ma_pop_dynamic(&hash->array);
+ if (hash->free)
+ (*hash->free)((uchar*) record);
+ 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;
+
+ 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)
+ 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)
+ 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;
+ }
+ return(0);
+}
+
+
+uchar *hash_element(HASH *hash,uint idx)
+{
+ if (idx < hash->records)
+ return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
+ return 0;
+}
+
+
+
diff --git a/mysql/libmariadb/ma_init.c b/mysql/libmariadb/ma_init.c
new file mode 100644
index 0000000..077bb87
--- /dev/null
+++ b/mysql/libmariadb/ma_init.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "mariadb_ctype.h"
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#ifdef HAVE_GETRUSAGE
+#include <sys/resource.h>
+/* extern int getrusage(int, struct rusage *); */
+#endif
+#include <signal.h>
+#ifdef _WIN32
+#ifdef _MSC_VER
+#include <locale.h>
+#include <crtdbg.h>
+#endif
+static my_bool my_win_init(void);
+#else
+#define my_win_init()
+#endif
+
+my_bool ma_init_done=0;
+
+
+
+/* Init ma_sys functions and ma_sys variabels */
+
+void ma_init(void)
+{
+ if (ma_init_done)
+ return;
+ ma_init_done=1;
+ {
+#ifdef _WIN32
+ my_win_init();
+#endif
+ return;
+ }
+} /* ma_init */
+
+
+
+void ma_end(int infoflag __attribute__((unused)))
+{
+#ifdef _WIN32
+ WSACleanup( );
+#endif /* _WIN32 */
+ ma_init_done=0;
+} /* ma_end */
+
+#ifdef _WIN32
+
+/*
+ This code is specially for running MySQL, but it should work in
+ other cases too.
+
+ Inizializzazione delle variabili d'ambiente per Win a 32 bit.
+
+ Vengono inserite nelle variabili d'ambiente (utilizzando cosi'
+ le funzioni getenv e putenv) i valori presenti nelle chiavi
+ del file di registro:
+
+ HKEY_LOCAL_MACHINE\software\MySQL
+
+ Se la kiave non esiste nonn inserisce nessun valore
+*/
+
+/* Crea la stringa d'ambiente */
+
+void setEnvString(char *ret, const char *name, const char *value)
+{
+ sprintf(ret, "%s=%s", name, value);
+ return ;
+}
+
+static my_bool my_win_init()
+{
+ WORD VersionRequested;
+ int err;
+ WSADATA WsaData;
+ const unsigned int MajorVersion=2,
+ MinorVersion=2;
+ VersionRequested= MAKEWORD(MajorVersion, MinorVersion);
+ /* Load WinSock library */
+ if ((err= WSAStartup(VersionRequested, &WsaData)))
+ {
+ return 0;
+ }
+ /* make sure 2.2 or higher is supported */
+ if ((LOBYTE(WsaData.wVersion) * 10 + HIBYTE(WsaData.wVersion)) < 22)
+ {
+ WSACleanup();
+ return 1;
+ }
+ return 0;
+}
+#endif
+
diff --git a/mysql/libmariadb/ma_io.c b/mysql/libmariadb/ma_io.c
new file mode 100644
index 0000000..7ce34ad
--- /dev/null
+++ b/mysql/libmariadb/ma_io.c
@@ -0,0 +1,223 @@
+/*
+ Copyright (C) 2015 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <errmsg.h>
+#include <mysql.h>
+#include <mysql/client_plugin.h>
+#include <mariadb/ma_io.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_REMOTEIO
+struct st_mysql_client_plugin_REMOTEIO *rio_plugin= NULL;
+#endif
+
+/* {{{ ma_open */
+MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql)
+{
+ int CodePage= -1;
+ FILE *fp= NULL;
+ MA_FILE *ma_file= NULL;
+
+ if (!location || !location[0])
+ return NULL;
+#ifdef HAVE_REMOTEIO
+ if (strstr(location, "://"))
+ goto remote;
+#endif
+
+#ifdef _WIN32
+ if (mysql && mysql->charset)
+ CodePage= madb_get_windows_cp(mysql->charset->csname);
+#endif
+ if (CodePage == -1)
+ {
+ if (!(fp= fopen(location, mode)))
+ {
+ return NULL;
+ }
+ }
+#ifdef _WIN32
+ /* See CONC-44: we need to support non ascii filenames too, so we convert
+ current character set to wchar_t and try to open the file via _wsopen */
+ else
+ {
+ wchar_t *w_filename= NULL;
+ wchar_t *w_mode= NULL;
+ int len;
+ DWORD Length;
+
+ len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), NULL, 0);
+ if (!len)
+ return NULL;
+ if (!(w_filename= (wchar_t *)calloc(1, (len + 1) * sizeof(wchar_t))))
+ {
+ my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return NULL;
+ }
+ Length= len;
+ len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), w_filename, (int)Length);
+ if (!len)
+ {
+ /* todo: error handling */
+ free(w_filename);
+ return NULL;
+ }
+ len= (int)strlen(mode);
+ if (!(w_mode= (wchar_t *)calloc(1, (len + 1) * sizeof(wchar_t))))
+ {
+ my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ free(w_filename);
+ return NULL;
+ }
+ Length= len;
+ len= MultiByteToWideChar(CodePage, 0, mode, (int)strlen(mode), w_mode, (int)Length);
+ if (!len)
+ {
+ /* todo: error handling */
+ free(w_filename);
+ free(w_mode);
+ return NULL;
+ }
+ fp= _wfopen(w_filename, w_mode);
+ free(w_filename);
+ free(w_mode);
+ }
+
+#endif
+ if (fp)
+ {
+ ma_file= (MA_FILE *)malloc(sizeof(MA_FILE));
+ if (!ma_file)
+ {
+ my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return NULL;
+ }
+ ma_file->type= MA_FILE_LOCAL;
+ ma_file->ptr= (void *)fp;
+ }
+ return ma_file;
+#ifdef HAVE_REMOTEIO
+remote:
+ /* check if plugin for remote io is available and try
+ * to open location */
+ {
+ MYSQL mysql;
+ if (rio_plugin ||(rio_plugin= (struct st_mysql_client_plugin_REMOTEIO *)
+ mysql_client_find_plugin(&mysql, NULL, MARIADB_CLIENT_REMOTEIO_PLUGIN)))
+ return rio_plugin->methods->mopen(location, mode);
+ return NULL;
+ }
+#endif
+}
+/* }}} */
+
+/* {{{ ma_close */
+int ma_close(MA_FILE *file)
+{
+ int rc;
+ if (!file)
+ return -1;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ rc= fclose((FILE *)file->ptr);
+ free(file);
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ rc= rio_plugin->methods->mclose(file);
+ break;
+#endif
+ default:
+ return -1;
+ }
+ return rc;
+}
+/* }}} */
+
+
+/* {{{ ma_feof */
+int ma_feof(MA_FILE *file)
+{
+ if (!file)
+ return -1;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ return feof((FILE *)file->ptr);
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ return rio_plugin->methods->mfeof(file);
+ break;
+#endif
+ default:
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ ma_read */
+size_t ma_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file)
+{
+ size_t s= 0;
+ if (!file)
+ return -1;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ s= fread(ptr, size, nmemb, (FILE *)file->ptr);
+ return s;
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ return rio_plugin->methods->mread(ptr, size, nmemb, file);
+ break;
+#endif
+ default:
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ ma_gets */
+char *ma_gets(char *ptr, size_t size, MA_FILE *file)
+{
+ if (!file)
+ return NULL;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ return fgets(ptr, (int)size, (FILE *)file->ptr);
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ return rio_plugin->methods->mgets(ptr, size, file);
+ break;
+#endif
+ default:
+ return NULL;
+ }
+}
+/* }}} */
+
+
diff --git a/mysql/libmariadb/ma_list.c b/mysql/libmariadb/ma_list.c
new file mode 100644
index 0000000..63c526f
--- /dev/null
+++ b/mysql/libmariadb/ma_list.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/*
+ Code for handling dubble-linked lists in C
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_list.h>
+
+ /* Add a element to start of list */
+
+LIST *list_add(LIST *root, LIST *element)
+{
+ if (root)
+ {
+ if (root->prev) /* If add in mid of list */
+ root->prev->next= element;
+ element->prev=root->prev;
+ root->prev=element;
+ }
+ else
+ element->prev=0;
+ element->next=root;
+ return(element); /* New root */
+}
+
+
+LIST *list_delete(LIST *root, LIST *element)
+{
+ if (element->prev)
+ element->prev->next=element->next;
+ else
+ root=element->next;
+ if (element->next)
+ element->next->prev=element->prev;
+ return root;
+}
+
+
+void list_free(LIST *root, unsigned int free_data)
+{
+ LIST *next;
+ while (root)
+ {
+ next=root->next;
+ if (free_data)
+ free(root->data);
+ free(root);
+ root=next;
+ }
+}
+
+
+LIST *list_cons(void *data, LIST *list)
+{
+ LIST *new_charset=(LIST*) malloc(sizeof(LIST));
+ if (!new_charset)
+ return 0;
+ new_charset->data=data;
+ return list_add(list,new_charset);
+}
+
+
+LIST *list_reverse(LIST *root)
+{
+ LIST *last;
+
+ last=root;
+ while (root)
+ {
+ last=root;
+ root=root->next;
+ last->next=last->prev;
+ last->prev=root;
+ }
+ return last;
+}
+
+uint list_length(LIST *list)
+{
+ uint count;
+ for (count=0 ; list ; list=list->next, count++) ;
+ return count;
+}
+
+
+int list_walk(LIST *list, list_walk_action action, gptr argument)
+{
+ int error=0;
+ while (list)
+ {
+ if ((error = (*action)(list->data,argument)))
+ return error;
+ list= list_rest(list);
+ }
+ return 0;
+}
diff --git a/mysql/libmariadb/ma_ll2str.c b/mysql/libmariadb/ma_ll2str.c
new file mode 100644
index 0000000..b96c736
--- /dev/null
+++ b/mysql/libmariadb/ma_ll2str.c
@@ -0,0 +1,75 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include "ma_string.h"
+#include <ctype.h>
+
+char NEAR _dig_vec[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+#define char_val(X) (X >= '0' && X <= '9' ? X-'0' :\
+ X >= 'A' && X <= 'Z' ? X-'A'+10 :\
+ X >= 'a' && X <= 'z' ? X-'a'+10 :\
+ '\177')
+
+char *ma_ll2str(long long val,char *dst,int radix)
+{
+ char buffer[65];
+ register char *p;
+ long long_val;
+
+ if (radix < 0)
+ {
+ if (radix < -36 || radix > -2) return (char*) 0;
+ if (val < 0) {
+ *dst++ = '-';
+ val = 0ULL - val;
+ }
+ radix = -radix;
+ }
+ else
+ {
+ if (radix > 36 || radix < 2) return (char*) 0;
+ }
+ if (val == 0)
+ {
+ *dst++='0';
+ *dst='\0';
+ return dst;
+ }
+ p = &buffer[sizeof(buffer)-1];
+ *p = '\0';
+
+ while ((ulonglong) val > (ulonglong) LONG_MAX)
+ {
+ ulonglong quo=(ulonglong) val/(uint) radix;
+ uint rem= (uint) (val- quo* (uint) radix);
+ *--p = _dig_vec[rem];
+ val= quo;
+ }
+ long_val= (long) val;
+ while (long_val != 0)
+ {
+ long quo= long_val/radix;
+ *--p = _dig_vec[(uchar) (long_val - quo*radix)];
+ long_val= quo;
+ }
+ while ((*dst++ = *p++) != 0) ;
+ return dst-1;
+}
diff --git a/mysql/libmariadb/ma_loaddata.c b/mysql/libmariadb/ma_loaddata.c
new file mode 100644
index 0000000..0dd30dc
--- /dev/null
+++ b/mysql/libmariadb/ma_loaddata.c
@@ -0,0 +1,262 @@
+/************************************************************************************
+ Copyright (C) 2000, 2011 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
+*************************************************************************************/
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "ma_global.h"
+#include <ma_sys.h>
+#include <ma_string.h>
+#include "errmsg.h"
+#include "mysql.h"
+#include <mariadb/ma_io.h>
+#include <string.h>
+#ifdef _WIN32
+#include <share.h>
+#endif
+
+typedef struct st_mysql_infile_info
+{
+ MA_FILE *fp;
+ int error_no;
+ char error_msg[MYSQL_ERRMSG_SIZE + 1];
+ const char *filename;
+} MYSQL_INFILE_INFO;
+
+/* {{{ mysql_local_infile_init */
+static
+int mysql_local_infile_init(void **ptr, const char *filename, void *userdata)
+{
+ MYSQL_INFILE_INFO *info;
+ MYSQL *mysql= (MYSQL *)userdata;
+
+ info = (MYSQL_INFILE_INFO *)malloc(sizeof(MYSQL_INFILE_INFO));
+ if (!info) {
+ return(1);
+ }
+ memset(info, 0, sizeof(MYSQL_INFILE_INFO));
+ *ptr = info;
+
+ info->filename = filename;
+
+ info->fp= ma_open(filename, "rb", mysql);
+
+ if (!info->fp)
+ {
+ /* error handling is done via mysql_local_infile_error function, so we
+ need to copy error to info */
+ if (mysql_errno(mysql) && !info->error_no)
+ {
+ info->error_no= mysql_errno(mysql);
+ ma_strmake(info->error_msg, mysql_error(mysql), MYSQL_ERRMSG_SIZE);
+ }
+ else
+ {
+ info->error_no = errno;
+ snprintf((char *)info->error_msg, sizeof(info->error_msg),
+ CER(CR_FILE_NOT_FOUND), filename, info->error_no);
+ }
+ return(1);
+ }
+
+ return(0);
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_read */
+static
+int mysql_local_infile_read(void *ptr, char * buf, unsigned int buf_len)
+{
+ MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
+ size_t count;
+
+ count= ma_read((void *)buf, 1, (size_t)buf_len, info->fp);
+
+ if (count == (size_t)-1)
+ {
+ info->error_no = errno;
+ snprintf((char *)info->error_msg, sizeof(info->error_msg),
+ CER(CR_FILE_READ), info->filename, info->error_no);
+ }
+ return((int)count);
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_error */
+static
+int mysql_local_infile_error(void *ptr, char *error_buf, unsigned int error_buf_len)
+{
+ MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
+
+ if (info) {
+ ma_strmake(error_buf, info->error_msg, error_buf_len);
+ return(info->error_no);
+ }
+
+ ma_strmake(error_buf, "Unknown error", error_buf_len);
+ return(CR_UNKNOWN_ERROR);
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_end */
+static
+void mysql_local_infile_end(void *ptr)
+{
+ MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
+
+ if (info)
+ {
+ if (info->fp)
+ ma_close(info->fp);
+ free(ptr);
+ }
+ return;
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_default */
+void mysql_set_local_infile_default(MYSQL *conn)
+{
+ conn->options.local_infile_init = mysql_local_infile_init;
+ conn->options.local_infile_read = mysql_local_infile_read;
+ conn->options.local_infile_error = mysql_local_infile_error;
+ conn->options.local_infile_end = mysql_local_infile_end;
+ return;
+}
+/* }}} */
+
+/* {{{ mysql_set_local_infile_handler */
+void STDCALL mysql_set_local_infile_handler(MYSQL *conn,
+ int (*local_infile_init)(void **, const char *, void *),
+ int (*local_infile_read)(void *, char *, uint),
+ void (*local_infile_end)(void *),
+ int (*local_infile_error)(void *, char *, uint),
+ void *userdata)
+{
+ conn->options.local_infile_init= local_infile_init;
+ conn->options.local_infile_read= local_infile_read;
+ conn->options.local_infile_end= local_infile_end;
+ conn->options.local_infile_error= local_infile_error;
+ conn->options.local_infile_userdata = userdata;
+ return;
+}
+/* }}} */
+
+/* {{{ mysql_handle_local_infile */
+my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename)
+{
+ unsigned int buflen= 4096;
+ int bufread;
+ unsigned char *buf= NULL;
+ void *info= NULL;
+ my_bool result= 1;
+
+ /* check if all callback functions exist */
+ if (!conn->options.local_infile_init || !conn->options.local_infile_end ||
+ !conn->options.local_infile_read || !conn->options.local_infile_error)
+ {
+ conn->options.local_infile_userdata= conn;
+ mysql_set_local_infile_default(conn);
+ }
+
+ if (!(conn->options.client_flag & CLIENT_LOCAL_FILES)) {
+ my_set_error(conn, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, "Load data local infile forbidden");
+ /* write empty packet to server */
+ ma_net_write(&conn->net, (unsigned char *)"", 0);
+ ma_net_flush(&conn->net);
+ goto infile_error;
+ }
+
+ /* allocate buffer for reading data */
+ buf = (uchar *)malloc(buflen);
+
+ /* init handler: allocate read buffer and open file */
+ if (conn->options.local_infile_init(&info, filename,
+ conn->options.local_infile_userdata))
+ {
+ char tmp_buf[MYSQL_ERRMSG_SIZE];
+ int tmp_errno;
+
+ tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
+ my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
+ ma_net_write(&conn->net, (unsigned char *)"", 0);
+ ma_net_flush(&conn->net);
+ goto infile_error;
+ }
+
+ /* read data */
+ while ((bufread= conn->options.local_infile_read(info, (char *)buf, buflen)) > 0)
+ {
+ if (ma_net_write(&conn->net, (unsigned char *)buf, bufread))
+ {
+ my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
+ goto infile_error;
+ }
+ }
+
+ /* send empty packet for eof */
+ if (ma_net_write(&conn->net, (unsigned char *)"", 0) ||
+ ma_net_flush(&conn->net))
+ {
+ my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
+ goto infile_error;
+ }
+
+ /* error during read occured */
+ if (bufread < 0)
+ {
+ char tmp_buf[MYSQL_ERRMSG_SIZE];
+ int tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
+ my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
+ goto infile_error;
+ }
+
+ result = 0;
+
+infile_error:
+ conn->options.local_infile_end(info);
+ free(buf);
+ return(result);
+}
+/* }}} */
+
diff --git a/mysql/libmariadb/ma_net.c b/mysql/libmariadb/ma_net.c
new file mode 100644
index 0000000..19601d8
--- /dev/null
+++ b/mysql/libmariadb/ma_net.c
@@ -0,0 +1,588 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2012-2016 SkySQL AB, MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Write and read of logical packets to/from socket
+ ** Writes are cached into net_buffer_length big packets.
+ ** Read packets are reallocated dynamicly when reading big packets.
+ ** Each logical packet has the following pre-info:
+ ** 3 byte length & 1 byte package-number.
+ */
+
+
+#include <ma_global.h>
+#include <mysql.h>
+#include <ma_pvio.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+#include "mysql.h"
+#include "ma_server_error.h"
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <ma_pvio.h>
+#include <ma_common.h>
+#ifndef _WIN32
+#include <poll.h>
+#endif
+
+#define MAX_PACKET_LENGTH (256L*256L*256L-1)
+
+/* net_buffer_length and max_allowed_packet are defined in mysql.h
+ See bug conc-57
+ */
+#undef net_buffer_length
+
+#undef max_allowed_packet
+ulong max_allowed_packet=1024L * 1024L * 1024L;
+ulong net_read_timeout= NET_READ_TIMEOUT;
+ulong net_write_timeout= NET_WRITE_TIMEOUT;
+ulong net_buffer_length= 8192; /* Default length. Enlarged if necessary */
+
+#if !defined(_WIN32) && !defined(MSDOS)
+#include <sys/socket.h>
+#else
+#undef MYSQL_SERVER /* Win32 can't handle interrupts */
+#endif
+#if !defined(MSDOS) && !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__)
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#if !defined(alpha_linux_port)
+#include <netinet/tcp.h>
+#endif
+#endif
+
+
+/*
+ ** Give error if a too big packet is found
+ ** The server can change this with the -O switch, but because the client
+ ** can't normally do this the client should have a bigger max-buffer.
+ */
+
+static int ma_net_write_buff(NET *net,const char *packet, size_t len);
+
+
+/* Init with packet info */
+
+int ma_net_init(NET *net, MARIADB_PVIO* pvio)
+{
+ if (!(net->buff=(uchar*) malloc(net_buffer_length)))
+ return 1;
+
+ memset(net->buff, 0, net_buffer_length);
+
+ if (!net->extension)
+ {
+ printf("Fatal\n");
+ exit(-1);
+ }
+ max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
+ net->buff_end=net->buff+(net->max_packet=net_buffer_length);
+ net->pvio = pvio;
+ net->error=0; net->return_status=0;
+ net->read_timeout=(uint) net_read_timeout; /* Timeout for read */
+ net->compress_pkt_nr= net->pkt_nr= 0;
+ net->write_pos=net->read_pos = net->buff;
+ net->last_error[0]= net->sqlstate[0] =0;
+
+ net->compress=0; net->reading_or_writing=0;
+ net->where_b = net->remain_in_buf=0;
+ net->last_errno=0;
+
+ if (pvio != 0) /* If real connection */
+ {
+ ma_pvio_get_handle(pvio, &net->fd);
+ ma_pvio_blocking(pvio, 1, 0);
+ ma_pvio_fast_send(pvio);
+ }
+ return 0;
+}
+
+void ma_net_end(NET *net)
+{
+ free(net->buff);
+ net->buff=0;
+}
+
+/* Realloc the packet buffer */
+
+static my_bool net_realloc(NET *net, size_t length)
+{
+ uchar *buff;
+ size_t pkt_length;
+
+ if (length >= net->max_packet_size)
+ {
+ net->error=1;
+ net->last_errno=ER_NET_PACKET_TOO_LARGE;
+ return(1);
+ }
+ pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
+ /* reallocate buffer:
+ size= pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE */
+ if (!(buff=(uchar*) realloc(net->buff,
+ pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE)))
+ {
+ net->error=1;
+ return(1);
+ }
+ net->buff=net->write_pos=buff;
+ net->buff_end=buff+(net->max_packet=(unsigned long)pkt_length);
+ return(0);
+}
+
+/* Remove unwanted characters from connection */
+void ma_net_clear(NET *net)
+{
+ if (net->extension->multi_status > COM_MULTI_OFF)
+ return;
+ net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
+ net->write_pos=net->buff;
+ return;
+}
+
+/* Flush write_buffer if not empty. */
+int ma_net_flush(NET *net)
+{
+ int error=0;
+
+ /* don't flush if com_multi is in progress */
+ if (net->extension->multi_status > COM_MULTI_OFF)
+ return 0;
+
+ if (net->buff != net->write_pos)
+ {
+ error=ma_net_real_write(net,(char*) net->buff,
+ (size_t) (net->write_pos - net->buff));
+ net->write_pos=net->buff;
+ }
+ if (net->compress)
+ net->pkt_nr= net->compress_pkt_nr;
+ return(error);
+}
+
+/*****************************************************************************
+ ** Write something to server/client buffer
+ *****************************************************************************/
+
+/*
+ ** Write a logical packet with packet header
+ ** Format: Packet length (3 bytes), packet number(1 byte)
+ ** When compression is used a 3 byte compression length is added
+ ** NOTE: If compression is used the original package is destroyed!
+ */
+
+int ma_net_write(NET *net, const uchar *packet, size_t len)
+{
+ uchar buff[NET_HEADER_SIZE];
+ while (len >= MAX_PACKET_LENGTH)
+ {
+ const ulong max_len= MAX_PACKET_LENGTH;
+ int3store(buff,max_len);
+ buff[3]= (uchar)net->pkt_nr++;
+ if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
+ ma_net_write_buff(net, (char *)packet, max_len))
+ return 1;
+ packet+= max_len;
+ len-= max_len;
+ }
+ /* write last remaining packet, size can be zero */
+ int3store(buff, len);
+ buff[3]= (uchar)net->pkt_nr++;
+ if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
+ ma_net_write_buff(net, (char *)packet, len))
+ return 1;
+ return 0;
+}
+
+int ma_net_write_command(NET *net, uchar command,
+ const char *packet, size_t len,
+ my_bool disable_flush)
+{
+ uchar buff[NET_HEADER_SIZE+1];
+ size_t buff_size= NET_HEADER_SIZE + 1;
+ size_t length= 1 + len; /* 1 extra byte for command */
+ int rc;
+
+ buff[NET_HEADER_SIZE]= 0;
+ buff[4]=command;
+
+ if (length >= MAX_PACKET_LENGTH)
+ {
+ len= MAX_PACKET_LENGTH - 1;
+ do
+ {
+ int3store(buff, MAX_PACKET_LENGTH);
+ buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
+
+ if (ma_net_write_buff(net, (char *)buff, buff_size) ||
+ ma_net_write_buff(net, packet, len))
+ return(1);
+ packet+= len;
+ length-= MAX_PACKET_LENGTH;
+ len= MAX_PACKET_LENGTH;
+ buff_size= NET_HEADER_SIZE; /* don't send command for further packets */
+ } while (length >= MAX_PACKET_LENGTH);
+ len= length;
+ }
+ int3store(buff,length);
+ buff[3]= (net->compress) ? 0 :(uchar) (net->pkt_nr++);
+ rc= test (ma_net_write_buff(net,(char *)buff, buff_size) ||
+ ma_net_write_buff(net,packet,len));
+ if (!rc && !disable_flush)
+ return test(ma_net_flush(net));
+ return rc;
+}
+
+
+static int ma_net_write_buff(NET *net,const char *packet, size_t len)
+{
+ size_t left_length;
+
+ if (net->max_packet > MAX_PACKET_LENGTH &&
+ net->compress)
+ left_length= (size_t)(MAX_PACKET_LENGTH - (net->write_pos - net->buff));
+ else
+ left_length=(size_t) (net->buff_end - net->write_pos);
+
+ if (len > left_length)
+ {
+ if (net->write_pos != net->buff)
+ {
+ memcpy((char*) net->write_pos,packet,left_length);
+ if (ma_net_real_write(net,(char*) net->buff,
+ (size_t)(net->write_pos - net->buff) + left_length))
+ return 1;
+ packet+=left_length;
+ len-=left_length;
+ net->write_pos= net->buff;
+ }
+ if (net->compress)
+ {
+ /* uncompressed length is stored in 3 bytes,so
+ packet can't be > 0xFFFFFF */
+ left_length= MAX_PACKET_LENGTH;
+ while (len > left_length)
+ {
+ if (ma_net_real_write(net, packet, left_length))
+ return 1;
+ packet+= left_length;
+ len-= left_length;
+ }
+ }
+ if (len > net->max_packet)
+ return(test(ma_net_real_write(net, packet, len)));
+ }
+ memcpy((char*) net->write_pos,packet,len);
+ net->write_pos+=len;
+ return 0;
+}
+
+unsigned char *mysql_net_store_length(unsigned char *packet, size_t length);
+
+/* Read and write using timeouts */
+
+int ma_net_real_write(NET *net, const char *packet, size_t len)
+{
+ ssize_t length;
+ char *pos,*end;
+
+ if (net->error == 2)
+ return(-1); /* socket can't be used */
+
+ net->reading_or_writing=2;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ size_t complen;
+ uchar *b;
+ uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
+ if (!(b=(uchar*) malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1)))
+ {
+ net->last_errno=ER_OUT_OF_RESOURCES;
+ net->error=2;
+ net->reading_or_writing=0;
+ return(1);
+ }
+ memcpy(b+header_length,packet,len);
+
+ if (_mariadb_compress((unsigned char*) b+header_length,&len,&complen))
+ {
+ complen=0;
+ }
+ int3store(&b[NET_HEADER_SIZE],complen);
+ int3store(b,len);
+ b[3]=(uchar) (net->compress_pkt_nr++);
+ len+= header_length;
+ packet= (char*) b;
+ }
+#endif /* HAVE_COMPRESS */
+
+ pos=(char*) packet; end=pos+len;
+ while (pos != end)
+ {
+ if ((length=ma_pvio_write(net->pvio,(uchar *)pos,(size_t) (end-pos))) <= 0)
+ {
+ net->error=2; /* Close socket */
+ net->last_errno= ER_NET_ERROR_ON_WRITE;
+ net->reading_or_writing=0;
+ return(1);
+ }
+ pos+=length;
+ }
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ free((char*) packet);
+#endif
+ net->reading_or_writing=0;
+ return(((int) (pos != end)));
+}
+
+/*****************************************************************************
+ ** Read something from server/clinet
+ *****************************************************************************/
+static ulong ma_real_read(NET *net, size_t *complen)
+{
+ uchar *pos;
+ ssize_t length;
+ uint i;
+ ulong len=packet_error;
+ size_t remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
+ NET_HEADER_SIZE);
+ *complen = 0;
+
+ net->reading_or_writing=1;
+
+ pos = net->buff + net->where_b; /* net->packet -4 */
+ for (i=0 ; i < 2 ; i++)
+ {
+ while (remain > 0)
+ {
+ /* First read is done with non blocking mode */
+ if ((length=ma_pvio_cache_read(net->pvio, pos,remain)) <= 0L)
+ {
+ len= packet_error;
+ net->error=2; /* Close socket */
+ goto end;
+ }
+ remain -= (ulong) length;
+ pos+= (ulong) length;
+ }
+
+ if (i == 0)
+ { /* First parts is packet length */
+ ulong helping;
+ net->pkt_nr= net->buff[net->where_b + 3];
+ net->compress_pkt_nr= ++net->pkt_nr;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ /* complen is > 0 if package is really compressed */
+ *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
+ }
+#endif
+
+ len=uint3korr(net->buff+net->where_b);
+ if (!len)
+ goto end;
+ helping = max(len,(ulong)*complen) + net->where_b;
+ /* The necessary size of net->buff */
+ if (helping >= net->max_packet)
+ {
+ if (net_realloc(net, helping))
+ {
+ len= packet_error; /* Return error */
+ goto end;
+ }
+ }
+ pos=net->buff + net->where_b;
+ remain = len;
+ }
+ }
+
+end:
+ net->reading_or_writing=0;
+ return(len);
+}
+
+ulong ma_net_read(NET *net)
+{
+ size_t len,complen;
+
+#ifdef HAVE_COMPRESS
+ if (!net->compress)
+ {
+#endif
+ len = ma_real_read (net,(size_t *)&complen);
+ if (len == MAX_PACKET_LENGTH)
+ {
+ /* multi packet read */
+ size_t length= 0;
+ ulong last_pos= net->where_b;
+
+ do
+ {
+ length+= len;
+ net->where_b+= (unsigned long)len;
+ len= ma_real_read(net, &complen);
+ } while (len == MAX_PACKET_LENGTH);
+ net->where_b= last_pos;
+ if (len != packet_error)
+ len+= length;
+ }
+ net->read_pos = net->buff + net->where_b;
+ if (len != packet_error)
+ net->read_pos[len]=0; /* Safeguard for mysql_use_result */
+ return (ulong)len;
+#ifdef HAVE_COMPRESS
+ }
+ else
+ {
+ /*
+ compressed protocol:
+
+ --------------------------------------
+ packet_length 3
+ sequence_id 1
+ uncompressed_length 3
+ --------------------------------------
+ compressed data packet_length - 7
+ --------------------------------------
+
+ Another packet will follow if:
+ packet_length == MAX_PACKET_LENGTH
+
+ Last package will be identified by
+ - packet_length is zero (special case)
+ - packet_length < MAX_PACKET_LENGTH
+ */
+
+ size_t packet_length,
+ buffer_length;
+ size_t current= 0, start= 0;
+ my_bool is_multi_packet= 0;
+
+ /* check if buffer is empty */
+ if (!net->remain_in_buf)
+ {
+ buffer_length= 0;
+ }
+ else
+ {
+ /* save position and restore \0 character */
+ buffer_length= net->buf_length;
+ current= net->buf_length - net->remain_in_buf;
+ start= current;
+ net->buff[net->buf_length - net->remain_in_buf]=net->save_char;
+ }
+ for (;;)
+ {
+ if (buffer_length - current >= 4)
+ {
+ uchar *pos= net->buff + current;
+ packet_length= uint3korr(pos);
+
+ /* check if we have last package (special case: zero length) */
+ if (!packet_length)
+ {
+ current+= 4; /* length + sequence_id,
+ no more data will follow */
+ break;
+ }
+ if (packet_length + 4 <= buffer_length - current)
+ {
+ if (!is_multi_packet)
+ {
+ current= current + packet_length + 4;
+ }
+ else
+ {
+ /* remove packet_header */
+ memmove(net->buff + current,
+ net->buff + current + 4,
+ buffer_length - current);
+ buffer_length-= 4;
+ current+= packet_length;
+ }
+ /* do we have last packet ? */
+ if (packet_length != MAX_PACKET_LENGTH)
+ {
+ is_multi_packet= 0;
+ break;
+ }
+ else
+ is_multi_packet= 1;
+ if (start)
+ {
+ memmove(net->buff, net->buff + start,
+ buffer_length - start);
+ /* decrease buflen*/
+ buffer_length-= start;
+ start= 0;
+ }
+ continue;
+ }
+ }
+ if (start)
+ {
+ memmove(net->buff, net->buff + start, buffer_length - start);
+ /* decrease buflen and current */
+ current -= start;
+ buffer_length-= start;
+ start= 0;
+ }
+
+ net->where_b=(unsigned long)buffer_length;
+
+ if ((packet_length = ma_real_read(net,(size_t *)&complen)) == packet_error)
+ return packet_error;
+ if (_mariadb_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen))
+ {
+ len= packet_error;
+ net->error=2; /* caller will close socket */
+ net->last_errno=ER_NET_UNCOMPRESS_ERROR;
+ break;
+ return packet_error;
+ }
+ buffer_length+= complen;
+ }
+ /* set values */
+ net->buf_length= (unsigned long)buffer_length;
+ net->remain_in_buf= (unsigned long)(buffer_length - current);
+ net->read_pos= net->buff + start + 4;
+ len= current - start - 4;
+ if (is_multi_packet)
+ len-= 4;
+ net->save_char= net->read_pos[len]; /* Must be saved */
+ net->read_pos[len]=0; /* Safeguard for mysql_use_result */
+ }
+#endif
+ return (ulong)len;
+}
+
+int net_add_multi_command(NET *net, uchar command, const uchar *packet,
+ size_t length)
+{
+ if (net->extension->multi_status == COM_MULTI_OFF)
+ {
+ return(1);
+ }
+ /* don't increase packet number */
+ net->compress_pkt_nr= net->pkt_nr= 0;
+ return ma_net_write_command(net, command, (const char *)packet, length, 1);
+}
+
diff --git a/mysql/libmariadb/ma_password.c b/mysql/libmariadb/ma_password.c
new file mode 100644
index 0000000..b7a18e7
--- /dev/null
+++ b/mysql/libmariadb/ma_password.c
@@ -0,0 +1,169 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* password checking routines */
+/*****************************************************************************
+ The main idea is that no password are sent between client & server on
+ connection and that no password are saved in mysql in a decodable form.
+
+ On connection a random string is generated and sent to the client.
+ The client generates a new string with a random generator inited with
+ the hash values from the password and the sent string.
+ This 'check' string is sent to the server where it is compared with
+ a string generated from the stored hash_value of the password and the
+ random string.
+
+ The password is saved (in user.password) by using the PASSWORD() function in
+ mysql.
+
+ Example:
+ update user set password=PASSWORD("hello") where user="test"
+ This saves a hashed number as a string in the password field.
+*****************************************************************************/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <ma_sha1.h>
+#include "mysql.h"
+
+
+void ma_randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
+{ /* For mysql 3.21.# */
+#ifdef HAVE_purify
+ memset((char*) rand_st, 0m sizeof(*rand_st)); /* Avoid UMC varnings */
+#endif
+ rand_st->max_value= 0x3FFFFFFFL;
+ rand_st->max_value_dbl=(double) rand_st->max_value;
+ rand_st->seed1=seed1%rand_st->max_value ;
+ rand_st->seed2=seed2%rand_st->max_value;
+}
+
+double rnd(struct rand_struct *rand_st)
+{
+ rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
+ rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
+ return (((double) rand_st->seed1)/rand_st->max_value_dbl);
+}
+
+void ma_hash_password(ulong *result, const char *password, size_t len)
+{
+ register ulong nr=1345345333L, add=7, nr2=0x12345671L;
+ ulong tmp;
+ const char *password_end= password + len;
+ for (; password < password_end; password++)
+ {
+ if (*password == ' ' || *password == '\t')
+ continue; /* skipp space in password */
+ tmp= (ulong) (uchar) *password;
+ nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
+ nr2+=(nr2 << 8) ^ nr;
+ add+=tmp;
+ }
+ result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
+ result[1]=nr2 & (((ulong) 1L << 31) -1L);
+ return;
+}
+
+static inline unsigned int char_val(char X)
+{
+ return (uint) (X >= '0' && X <= '9' ? X-'0' :
+ X >= 'A' && X <= 'Z' ? X-'A'+10 :
+ X-'a'+10);
+}
+
+/*
+ * Genererate a new message based on message and password
+ * The same thing is done in client and server and the results are checked.
+ */
+
+/* scramble for 4.1 servers
+ * Code based on php_nysqlnd_scramble function from PHP's mysqlnd extension,
+ * written by Andrey Hristov (andrey@php.net)
+ * License: PHP License 3.0
+ */
+void my_crypt(unsigned char *buffer, const unsigned char *s1, const unsigned char *s2, size_t len)
+{
+ const unsigned char *s1_end= s1 + len;
+ while (s1 < s1_end) {
+ *buffer++= *s1++ ^ *s2++;
+ }
+}
+
+void ma_scramble_41(const unsigned char *buffer, const char *scramble, const char *password)
+{
+ _MA_SHA1_CTX context;
+ unsigned char sha1[SHA1_MAX_LENGTH];
+ unsigned char sha2[SHA1_MAX_LENGTH];
+
+
+ /* Phase 1: hash password */
+ ma_SHA1Init(&context);
+ ma_SHA1Update(&context, (unsigned char *)password, strlen((char *)password));
+ ma_SHA1Final(sha1, &context);
+
+ /* Phase 2: hash sha1 */
+ ma_SHA1Init(&context);
+ ma_SHA1Update(&context, (unsigned char*)sha1, SHA1_MAX_LENGTH);
+ ma_SHA1Final(sha2, &context);
+
+ /* Phase 3: hash scramble + sha2 */
+ ma_SHA1Init(&context);
+ ma_SHA1Update(&context, (unsigned char *)scramble, SCRAMBLE_LENGTH);
+ ma_SHA1Update(&context, (unsigned char*)sha2, SHA1_MAX_LENGTH);
+ ma_SHA1Final((unsigned char *)buffer, &context);
+
+ /* let's crypt buffer now */
+ my_crypt((uchar *)buffer, (const unsigned char *)buffer, (const unsigned char *)sha1, SHA1_MAX_LENGTH);
+}
+/* }}} */
+
+void ma_make_scrambled_password(char *to,const char *password)
+{
+ ulong hash_res[2];
+ ma_hash_password(hash_res,password, strlen(password));
+ sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
+}
+
+/*
+ * Genererate a new message based on message and password
+ * The same thing is done in client and server and the results are checked.
+ */
+char *ma_scramble_323(char *to, const char *message, const char *password)
+{
+ struct rand_struct rand_st;
+ ulong hash_pass[2], hash_message[2];
+
+ if (password && password[0])
+ {
+ char extra, *to_start=to;
+ const char *end_scramble323= message + SCRAMBLE_LENGTH_323;
+ ma_hash_password(hash_pass,password, (uint) strlen(password));
+ /* Don't use strlen, could be > SCRAMBLE_LENGTH_323 ! */
+ ma_hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
+ ma_randominit(&rand_st, hash_pass[0] ^ hash_message[0],
+ hash_pass[1] ^ hash_message[1]);
+ for (; message < end_scramble323; message++)
+ *to++= (char) (floor(rnd(&rand_st) * 31) + 64);
+ extra=(char) (floor(rnd(&rand_st) * 31));
+ while (to_start != to)
+ *(to_start++)^= extra;
+ }
+ *to= 0;
+ return to;
+}
diff --git a/mysql/libmariadb/ma_pvio.c b/mysql/libmariadb/ma_pvio.c
new file mode 100644
index 0000000..891d4ea
--- /dev/null
+++ b/mysql/libmariadb/ma_pvio.c
@@ -0,0 +1,582 @@
+/************************************************************************************
+ Copyright (C) 2015 MariaDB Corporation 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
+*************************************************************************************/
+
+/* MariaDB Communication IO (PVIO) interface
+
+ PVIO is the interface for client server communication and replaces former vio
+ component of the client library.
+
+ PVIO support various protcols like sockets, pipes and shared memory, which are
+ implemented as plugins and can be extended therfore easily.
+
+ Interface function description:
+
+ ma_pvio_init allocates a new PVIO object which will be used
+ for the current connection
+
+ ma_pvio_close frees all resources of previously allocated PVIO object
+ and closes open connections
+
+ ma_pvio_read reads data from server
+
+ ma_pvio_write sends data to server
+
+ ma_pvio_set_timeout sets timeout for connection, read and write
+
+ ma_pvio_register_callback
+ register callback functions for read and write
+ */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <mysql.h>
+#include <errmsg.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <ma_common.h>
+#include <ma_pvio.h>
+#include <mariadb_async.h>
+#include <ma_context.h>
+
+/* callback functions for read/write */
+LIST *pvio_callback= NULL;
+
+#define IS_BLOCKING_ERROR() \
+ IF_WIN(WSAGetLastError() != WSAEWOULDBLOCK, \
+ (errno != EAGAIN && errno != EINTR))
+
+/* {{{ MARIADB_PVIO *ma_pvio_init */
+MARIADB_PVIO *ma_pvio_init(MA_PVIO_CINFO *cinfo)
+{
+ /* check connection type and load the required plugin.
+ * Currently we support the following pvio types:
+ * pvio_socket
+ * pvio_namedpipe
+ * pvio_sharedmed
+ */
+ const char *pvio_plugins[] = {"pvio_socket", "pvio_npipe", "pvio_shmem"};
+ int type;
+ MARIADB_PVIO_PLUGIN *pvio_plugin;
+ MARIADB_PVIO *pvio= NULL;
+
+ switch (cinfo->type)
+ {
+ case PVIO_TYPE_UNIXSOCKET:
+ case PVIO_TYPE_SOCKET:
+ type= 0;
+ break;
+#ifdef _WIN32
+ case PVIO_TYPE_NAMEDPIPE:
+ type= 1;
+ break;
+ case PVIO_TYPE_SHAREDMEM:
+ type= 2;
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ if (!(pvio_plugin= (MARIADB_PVIO_PLUGIN *)
+ mysql_client_find_plugin(cinfo->mysql,
+ pvio_plugins[type],
+ MARIADB_CLIENT_PVIO_PLUGIN)))
+ {
+ /* error already set in mysql_client_find_plugin */
+ return NULL;
+ }
+
+
+ if (!(pvio= (MARIADB_PVIO *)calloc(1, sizeof(MARIADB_PVIO))))
+ {
+ PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
+ return NULL;
+ }
+
+ /* register error routine and methods */
+ pvio->methods= pvio_plugin->methods;
+ pvio->set_error= my_set_error;
+ pvio->type= cinfo->type;
+
+ /* set timeout to connect timeout - after successfull connect we will set
+ * correct values for read and write */
+ if (pvio->methods->set_timeout)
+ {
+ pvio->methods->set_timeout(pvio, PVIO_CONNECT_TIMEOUT, cinfo->mysql->options.connect_timeout);
+ pvio->methods->set_timeout(pvio, PVIO_READ_TIMEOUT, cinfo->mysql->options.connect_timeout);
+ pvio->methods->set_timeout(pvio, PVIO_WRITE_TIMEOUT, cinfo->mysql->options.connect_timeout);
+ }
+
+ if (!(pvio->cache= calloc(1, PVIO_READ_AHEAD_CACHE_SIZE)))
+ {
+ PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
+ free(pvio);
+ return NULL;
+ }
+ pvio->cache_size= 0;
+ pvio->cache_pos= pvio->cache;
+
+ return pvio;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_is_alive */
+my_bool ma_pvio_is_alive(MARIADB_PVIO *pvio)
+{
+ if (!pvio)
+ return FALSE;
+ if (pvio->methods->is_alive)
+ return pvio->methods->is_alive(pvio);
+ return TRUE;
+}
+/* }}} */
+
+/* {{{ int ma_pvio_fast_send */
+int ma_pvio_fast_send(MARIADB_PVIO *pvio)
+{
+ if (!pvio || !pvio->methods->fast_send)
+ return 1;
+ return pvio->methods->fast_send(pvio);
+}
+/* }}} */
+
+/* {{{ int ma_pvio_keepalive */
+int ma_pvio_keepalive(MARIADB_PVIO *pvio)
+{
+ if (!pvio || !pvio->methods->keepalive)
+ return 1;
+ return pvio->methods->keepalive(pvio);
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_set_timeout */
+my_bool ma_pvio_set_timeout(MARIADB_PVIO *pvio,
+ enum enum_pvio_timeout type,
+ int timeout)
+{
+ if (!pvio)
+ return 1;
+
+ if (pvio->methods->set_timeout)
+ return pvio->methods->set_timeout(pvio, type, timeout);
+ return 1;
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_read_async */
+static size_t ma_pvio_read_async(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
+{
+ ssize_t res= 0;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ int timeout= pvio->timeout[PVIO_READ_TIMEOUT];
+
+ if (!pvio->methods->async_read)
+ {
+ PVIO_SET_ERROR(pvio->mysql, CR_ASYNC_NOT_SUPPORTED, unknown_sqlstate, 0);
+ return -1;
+ }
+
+ for (;;)
+ {
+ if (pvio->methods->async_read)
+ res= pvio->methods->async_read(pvio, buffer, length);
+ if (res >= 0 || IS_BLOCKING_ERROR())
+ return res;
+ b->events_to_wait_for= MYSQL_WAIT_READ;
+ if (timeout >= 0)
+ {
+ b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
+ b->timeout_value= timeout;
+ }
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ if (b->events_occured & MYSQL_WAIT_TIMEOUT)
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_read */
+ssize_t ma_pvio_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
+{
+ ssize_t r= -1;
+ if (!pvio)
+ return -1;
+ if (IS_PVIO_ASYNC_ACTIVE(pvio))
+ {
+ r= ma_pvio_read_async(pvio, buffer, length);
+ goto end;
+ }
+ else
+ {
+ if (IS_PVIO_ASYNC(pvio))
+ {
+ /*
+ If switching from non-blocking to blocking API usage, set the socket
+ back to blocking mode.
+ */
+ my_bool old_mode;
+ ma_pvio_blocking(pvio, TRUE, &old_mode);
+ }
+ }
+
+ /* secure connection */
+#ifdef HAVE_TLS
+ if (pvio->ctls)
+ {
+ r= ma_pvio_tls_read(pvio->ctls, buffer, length);
+ goto end;
+ }
+#endif
+ if (pvio->methods->read)
+ r= pvio->methods->read(pvio, buffer, length);
+end:
+ if (pvio_callback)
+ {
+ void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
+ LIST *p= pvio_callback;
+ while (p)
+ {
+ callback= p->data;
+ callback(0, pvio->mysql, buffer, r);
+ p= p->next;
+ }
+ }
+ return r;
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_cache_read */
+ssize_t ma_pvio_cache_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
+{
+ ssize_t r;
+
+ if (!pvio)
+ return -1;
+
+ if (!pvio->cache)
+ return ma_pvio_read(pvio, buffer, length);
+
+ if (pvio->cache + pvio->cache_size > pvio->cache_pos)
+ {
+ ssize_t remaining = pvio->cache + pvio->cache_size - pvio->cache_pos;
+ assert(remaining > 0);
+ r= MIN((ssize_t)length, remaining);
+ memcpy(buffer, pvio->cache_pos, r);
+ pvio->cache_pos+= r;
+ }
+ else if (length >= PVIO_READ_AHEAD_CACHE_MIN_SIZE)
+ {
+ r= ma_pvio_read(pvio, buffer, length);
+ }
+ else
+ {
+ r= ma_pvio_read(pvio, pvio->cache, PVIO_READ_AHEAD_CACHE_SIZE);
+ if (r > 0)
+ {
+ if (length < (size_t)r)
+ {
+ pvio->cache_size= r;
+ pvio->cache_pos= pvio->cache + length;
+ r= length;
+ }
+ memcpy(buffer, pvio->cache, r);
+ }
+ }
+ return r;
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_write_async */
+static ssize_t ma_pvio_write_async(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
+{
+ ssize_t res;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ int timeout= pvio->timeout[PVIO_WRITE_TIMEOUT];
+
+ for (;;)
+ {
+ res= pvio->methods->async_write(pvio, buffer, length);
+ if (res >= 0 || IS_BLOCKING_ERROR())
+ return res;
+ b->events_to_wait_for= MYSQL_WAIT_WRITE;
+ if (timeout >= 0)
+ {
+ b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
+ b->timeout_value= timeout;
+ }
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ if (b->events_occured & MYSQL_WAIT_TIMEOUT)
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_write */
+ssize_t ma_pvio_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
+{
+ ssize_t r= 0;
+
+ if (!pvio)
+ return -1;
+
+ /* secure connection */
+#ifdef HAVE_TLS
+ if (pvio->ctls)
+ {
+ r= ma_pvio_tls_write(pvio->ctls, buffer, length);
+ goto end;
+ }
+ else
+#endif
+ if (IS_PVIO_ASYNC_ACTIVE(pvio))
+ {
+ r= ma_pvio_write_async(pvio, buffer, length);
+ goto end;
+ }
+ else
+ {
+ if (IS_PVIO_ASYNC(pvio))
+ {
+ /*
+ If switching from non-blocking to blocking API usage, set the socket
+ back to blocking mode.
+ */
+ my_bool old_mode;
+ ma_pvio_blocking(pvio, TRUE, &old_mode);
+ }
+ }
+
+ if (pvio->methods->write)
+ r= pvio->methods->write(pvio, buffer, length);
+end:
+ if (pvio_callback)
+ {
+ void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
+ LIST *p= pvio_callback;
+ while (p)
+ {
+ callback= p->data;
+ callback(1, pvio->mysql, buffer, r);
+ p= p->next;
+ }
+ }
+ return r;
+}
+/* }}} */
+
+/* {{{ void ma_pvio_close */
+void ma_pvio_close(MARIADB_PVIO *pvio)
+{
+ /* free internal structures and close connection */
+#ifdef HAVE_TLS
+ if (pvio && pvio->ctls)
+ {
+ ma_pvio_tls_close(pvio->ctls);
+ free(pvio->ctls);
+ }
+#endif
+ if (pvio && pvio->methods->close)
+ pvio->methods->close(pvio);
+
+ if (pvio->cache)
+ free(pvio->cache);
+
+ free(pvio);
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_get_handle */
+my_bool ma_pvio_get_handle(MARIADB_PVIO *pvio, void *handle)
+{
+ if (pvio && pvio->methods->get_handle)
+ return pvio->methods->get_handle(pvio, handle);
+ return 1;
+}
+/* }}} */
+
+/* {{{ ma_pvio_wait_async */
+static my_bool
+ma_pvio_wait_async(struct mysql_async_context *b, enum enum_pvio_io_event event,
+ int timeout)
+{
+ switch (event)
+ {
+ case VIO_IO_EVENT_READ:
+ b->events_to_wait_for = MYSQL_WAIT_READ;
+ break;
+ case VIO_IO_EVENT_WRITE:
+ b->events_to_wait_for = MYSQL_WAIT_WRITE;
+ break;
+ case VIO_IO_EVENT_CONNECT:
+ b->events_to_wait_for = MYSQL_WAIT_WRITE | IF_WIN(0, MYSQL_WAIT_EXCEPT);
+ break;
+ }
+
+ if (timeout >= 0)
+ {
+ b->events_to_wait_for |= MYSQL_WAIT_TIMEOUT;
+ b->timeout_value= timeout;
+ }
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ return (b->events_occured & MYSQL_WAIT_TIMEOUT) ? 0 : 1;
+}
+/* }}} */
+
+/* {{{ ma_pvio_wait_io_or_timeout */
+int ma_pvio_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout)
+{
+ if (IS_PVIO_ASYNC_ACTIVE(pvio))
+ return ma_pvio_wait_async(pvio->mysql->options.extension->async_context,
+ (is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE,
+ timeout);
+
+ if (pvio && pvio->methods->wait_io_or_timeout)
+ return pvio->methods->wait_io_or_timeout(pvio, is_read, timeout);
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_connect */
+my_bool ma_pvio_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo)
+{
+ if (pvio && pvio->methods->connect)
+ return pvio->methods->connect(pvio, cinfo);
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_blocking */
+my_bool ma_pvio_blocking(MARIADB_PVIO *pvio, my_bool block, my_bool *previous_mode)
+{
+ if (pvio && pvio->methods->blocking)
+ return pvio->methods->blocking(pvio, block, previous_mode);
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_is_blocking */
+my_bool ma_pvio_is_blocking(MARIADB_PVIO *pvio)
+{
+ if (pvio && pvio->methods->is_blocking)
+ return pvio->methods->is_blocking(pvio);
+ return 1;
+}
+/* }}} */
+
+/* {{{ ma_pvio_has_data */
+my_bool ma_pvio_has_data(MARIADB_PVIO *pvio, ssize_t *data_len)
+{
+ /* check if we still have unread data in cache */
+ if (pvio && pvio->cache)
+ if (pvio->cache_pos > pvio->cache)
+ return test(pvio->cache_pos - pvio->cache);
+ if (pvio && pvio->methods->has_data)
+ return pvio->methods->has_data(pvio, data_len);
+ return 1;
+}
+/* }}} */
+
+#ifdef HAVE_TLS
+/* {{{ my_bool ma_pvio_start_ssl */
+my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
+{
+ if (!pvio || !pvio->mysql)
+ return 1;
+ CLEAR_CLIENT_ERROR(pvio->mysql);
+ if (!(pvio->ctls= ma_pvio_tls_init(pvio->mysql)))
+ {
+ return 1;
+ }
+ if (ma_pvio_tls_connect(pvio->ctls))
+ {
+ free(pvio->ctls);
+ pvio->ctls= NULL;
+ return 1;
+ }
+
+ /* default behaviour:
+ 1. peer certificate verification
+ 2. verify CN (requires option ssl_verify_check)
+ 3. verrify finger print
+ */
+ if ((pvio->mysql->options.ssl_ca || pvio->mysql->options.ssl_capath) &&
+ (pvio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
+ ma_pvio_tls_verify_server_cert(pvio->ctls))
+ return 1;
+
+ if (pvio->mysql->options.extension &&
+ ((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) ||
+ (pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0])))
+ {
+ if (ma_pvio_tls_check_fp(pvio->ctls,
+ pvio->mysql->options.extension->tls_fp,
+ pvio->mysql->options.extension->tls_fp_list))
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+#endif
+
+/* {{{ ma_pvio_register_callback */
+int ma_pvio_register_callback(my_bool register_callback,
+ void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length))
+{
+ LIST *list;
+
+ if (!callback_function)
+ return 1;
+
+ /* plugin will unregister in it's deinit function */
+ if (register_callback)
+ {
+ list= (LIST *)malloc(sizeof(LIST));
+
+ list->data= (void *)callback_function;
+ pvio_callback= list_add(pvio_callback, list);
+ }
+ else /* unregister callback function */
+ {
+ LIST *p= pvio_callback;
+ while (p)
+ {
+ if (p->data == callback_function)
+ {
+ list_delete(pvio_callback, p);
+ break;
+ }
+ p= p->next;
+ }
+ }
+ return 0;
+}
+/* }}} */
diff --git a/mysql/libmariadb/ma_sha1.c b/mysql/libmariadb/ma_sha1.c
new file mode 100644
index 0000000..04c5760
--- /dev/null
+++ b/mysql/libmariadb/ma_sha1.c
@@ -0,0 +1,326 @@
+/****************************************************************************
+ Copyright (C) 2012 Monty Program AB
+ 2016 MariaDB Corporation 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
+ *****************************************************************************/
+
+/* This code came from the PHP project, initially written by
+ Stefan Esser */
+
+
+#include "ma_global.h"
+#include "string.h"
+
+/* This code is heavily based on the PHP md5 implementation */
+
+#include "ma_sha1.h"
+
+
+static void ma_SHA1Transform(uint32[5], const unsigned char[64]);
+static void ma_SHA1Encode(unsigned char *, uint32 *, unsigned int);
+static void ma_SHA1Decode(uint32 *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic SHA1 functions.
+*/
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((x) ^ (y) ^ (z))
+#define H(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))
+#define I(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+*/
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* W[i]
+*/
+#define W(i) ( tmp=x[(i-3)&15]^x[(i-8)&15]^x[(i-14)&15]^x[i&15], \
+ (x[i&15]=ROTATE_LEFT(tmp, 1)) )
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+*/
+#define FF(a, b, c, d, e, w) { \
+ (e) += F ((b), (c), (d)) + (w) + (uint32)(0x5A827999); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+#define GG(a, b, c, d, e, w) { \
+ (e) += G ((b), (c), (d)) + (w) + (uint32)(0x6ED9EBA1); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+#define HH(a, b, c, d, e, w) { \
+ (e) += H ((b), (c), (d)) + (w) + (uint32)(0x8F1BBCDC); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+#define II(a, b, c, d, e, w) { \
+ (e) += I ((b), (c), (d)) + (w) + (uint32)(0xCA62C1D6); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+
+
+/* {{{ ma_SHA1Init
+ * SHA1 initialization. Begins an SHA1 operation, writing a new context.
+ */
+void ma_SHA1Init(_MA_SHA1_CTX * context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xc3d2e1f0;
+}
+/* }}} */
+
+/* {{{ ma_SHA1Update
+ SHA1 block update operation. Continues an SHA1 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void ma_SHA1Update(_MA_SHA1_CTX * context, const unsigned char *input,
+ size_t inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((uint32) inputLen << 3))
+ < ((uint32) inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((uint32) inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ memcpy
+ ((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen);
+ ma_SHA1Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ ma_SHA1Transform(context->state, &input[i]);
+
+ index = 0;
+ } else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy
+ ((unsigned char*) & context->buffer[index], (unsigned char*) & input[i],
+ inputLen - i);
+}
+/* }}} */
+
+/* {{{ ma_SHA1Final
+ SHA1 finalization. Ends an SHA1 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void ma_SHA1Final(unsigned char digest[20], _MA_SHA1_CTX * context)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ bits[7] = context->count[0] & 0xFF;
+ bits[6] = (context->count[0] >> 8) & 0xFF;
+ bits[5] = (context->count[0] >> 16) & 0xFF;
+ bits[4] = (context->count[0] >> 24) & 0xFF;
+ bits[3] = context->count[1] & 0xFF;
+ bits[2] = (context->count[1] >> 8) & 0xFF;
+ bits[1] = (context->count[1] >> 16) & 0xFF;
+ bits[0] = (context->count[1] >> 24) & 0xFF;
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ ma_SHA1Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ ma_SHA1Update(context, bits, 8);
+
+ /* Store state in digest */
+ ma_SHA1Encode(digest, context->state, 20);
+
+ /* Zeroize sensitive information.
+ */
+ memset((unsigned char*) context, 0, sizeof(*context));
+}
+/* }}} */
+
+/* {{{ ma_SHA1Transform
+ * SHA1 basic transformation. Transforms state based on block.
+ */
+static void ma_SHA1Transform(uint32 state[5], const unsigned char block[64])
+{
+ uint32 a = state[0], b = state[1], c = state[2];
+ uint32 d = state[3], e = state[4], x[16], tmp;
+
+ ma_SHA1Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, e, x[0]); /* 1 */
+ FF(e, a, b, c, d, x[1]); /* 2 */
+ FF(d, e, a, b, c, x[2]); /* 3 */
+ FF(c, d, e, a, b, x[3]); /* 4 */
+ FF(b, c, d, e, a, x[4]); /* 5 */
+ FF(a, b, c, d, e, x[5]); /* 6 */
+ FF(e, a, b, c, d, x[6]); /* 7 */
+ FF(d, e, a, b, c, x[7]); /* 8 */
+ FF(c, d, e, a, b, x[8]); /* 9 */
+ FF(b, c, d, e, a, x[9]); /* 10 */
+ FF(a, b, c, d, e, x[10]); /* 11 */
+ FF(e, a, b, c, d, x[11]); /* 12 */
+ FF(d, e, a, b, c, x[12]); /* 13 */
+ FF(c, d, e, a, b, x[13]); /* 14 */
+ FF(b, c, d, e, a, x[14]); /* 15 */
+ FF(a, b, c, d, e, x[15]); /* 16 */
+ FF(e, a, b, c, d, W(16)); /* 17 */
+ FF(d, e, a, b, c, W(17)); /* 18 */
+ FF(c, d, e, a, b, W(18)); /* 19 */
+ FF(b, c, d, e, a, W(19)); /* 20 */
+
+ /* Round 2 */
+ GG(a, b, c, d, e, W(20)); /* 21 */
+ GG(e, a, b, c, d, W(21)); /* 22 */
+ GG(d, e, a, b, c, W(22)); /* 23 */
+ GG(c, d, e, a, b, W(23)); /* 24 */
+ GG(b, c, d, e, a, W(24)); /* 25 */
+ GG(a, b, c, d, e, W(25)); /* 26 */
+ GG(e, a, b, c, d, W(26)); /* 27 */
+ GG(d, e, a, b, c, W(27)); /* 28 */
+ GG(c, d, e, a, b, W(28)); /* 29 */
+ GG(b, c, d, e, a, W(29)); /* 30 */
+ GG(a, b, c, d, e, W(30)); /* 31 */
+ GG(e, a, b, c, d, W(31)); /* 32 */
+ GG(d, e, a, b, c, W(32)); /* 33 */
+ GG(c, d, e, a, b, W(33)); /* 34 */
+ GG(b, c, d, e, a, W(34)); /* 35 */
+ GG(a, b, c, d, e, W(35)); /* 36 */
+ GG(e, a, b, c, d, W(36)); /* 37 */
+ GG(d, e, a, b, c, W(37)); /* 38 */
+ GG(c, d, e, a, b, W(38)); /* 39 */
+ GG(b, c, d, e, a, W(39)); /* 40 */
+
+ /* Round 3 */
+ HH(a, b, c, d, e, W(40)); /* 41 */
+ HH(e, a, b, c, d, W(41)); /* 42 */
+ HH(d, e, a, b, c, W(42)); /* 43 */
+ HH(c, d, e, a, b, W(43)); /* 44 */
+ HH(b, c, d, e, a, W(44)); /* 45 */
+ HH(a, b, c, d, e, W(45)); /* 46 */
+ HH(e, a, b, c, d, W(46)); /* 47 */
+ HH(d, e, a, b, c, W(47)); /* 48 */
+ HH(c, d, e, a, b, W(48)); /* 49 */
+ HH(b, c, d, e, a, W(49)); /* 50 */
+ HH(a, b, c, d, e, W(50)); /* 51 */
+ HH(e, a, b, c, d, W(51)); /* 52 */
+ HH(d, e, a, b, c, W(52)); /* 53 */
+ HH(c, d, e, a, b, W(53)); /* 54 */
+ HH(b, c, d, e, a, W(54)); /* 55 */
+ HH(a, b, c, d, e, W(55)); /* 56 */
+ HH(e, a, b, c, d, W(56)); /* 57 */
+ HH(d, e, a, b, c, W(57)); /* 58 */
+ HH(c, d, e, a, b, W(58)); /* 59 */
+ HH(b, c, d, e, a, W(59)); /* 60 */
+
+ /* Round 4 */
+ II(a, b, c, d, e, W(60)); /* 61 */
+ II(e, a, b, c, d, W(61)); /* 62 */
+ II(d, e, a, b, c, W(62)); /* 63 */
+ II(c, d, e, a, b, W(63)); /* 64 */
+ II(b, c, d, e, a, W(64)); /* 65 */
+ II(a, b, c, d, e, W(65)); /* 66 */
+ II(e, a, b, c, d, W(66)); /* 67 */
+ II(d, e, a, b, c, W(67)); /* 68 */
+ II(c, d, e, a, b, W(68)); /* 69 */
+ II(b, c, d, e, a, W(69)); /* 70 */
+ II(a, b, c, d, e, W(70)); /* 71 */
+ II(e, a, b, c, d, W(71)); /* 72 */
+ II(d, e, a, b, c, W(72)); /* 73 */
+ II(c, d, e, a, b, W(73)); /* 74 */
+ II(b, c, d, e, a, W(74)); /* 75 */
+ II(a, b, c, d, e, W(75)); /* 76 */
+ II(e, a, b, c, d, W(76)); /* 77 */
+ II(d, e, a, b, c, W(77)); /* 78 */
+ II(c, d, e, a, b, W(78)); /* 79 */
+ II(b, c, d, e, a, W(79)); /* 80 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ /* Zeroize sensitive information. */
+ memset((unsigned char*) x, 0, sizeof(x));
+}
+/* }}} */
+
+/* {{{ ma_SHA1Encode
+ Encodes input (uint32) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void ma_SHA1Encode(unsigned char *output, uint32 *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char) ((input[i] >> 24) & 0xff);
+ output[j + 1] = (unsigned char) ((input[i] >> 16) & 0xff);
+ output[j + 2] = (unsigned char) ((input[i] >> 8) & 0xff);
+ output[j + 3] = (unsigned char) (input[i] & 0xff);
+ }
+}
+/* }}} */
+
+/* {{{ ma_SHA1Decode
+ Decodes input (unsigned char) into output (uint32). Assumes len is
+ a multiple of 4.
+ */
+static void ma_SHA1Decode(uint32 *output, const unsigned char * input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((uint32) input[j + 3]) | (((uint32) input[j + 2]) << 8) |
+ (((uint32) input[j + 1]) << 16) | (((uint32) input[j]) << 24);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/mysql/libmariadb/ma_stmt_codec.c b/mysql/libmariadb/ma_stmt_codec.c
new file mode 100644
index 0000000..72ea965
--- /dev/null
+++ b/mysql/libmariadb/ma_stmt_codec.c
@@ -0,0 +1,1081 @@
+/****************************************************************************
+ Copyright (C) 2012 Monty Program AB
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not see <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ Part of this code includes code from the PHP project which
+ is freely available from http://www.php.net
+*****************************************************************************/
+
+/* The implementation for prepared statements was ported from PHP's mysqlnd
+ extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
+
+ Original file header:
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "ma_global.h"
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include "mysql.h"
+#include <math.h> /* ceil() */
+
+#define MYSQL_SILENT
+
+/* ranges for C-binding */
+#define UINT_MAX32 0xFFFFFFFFL
+#define UINT_MAX24 0x00FFFFFF
+#define UINT_MAX16 0xFFFF
+#ifndef INT_MIN8
+#define INT_MIN8 (~0x7F)
+#define INT_MAX8 0x7F
+#endif
+#define UINT_MAX8 0xFF
+
+ #define MAX_DOUBLE_STRING_REP_LENGTH 300
+#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN)
+#define LONGLONG_MIN ((long long) 0x8000000000000000LL)
+#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL)
+#endif
+
+#if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)
+/* First check for ANSI C99 definition: */
+#ifdef ULLONG_MAX
+#define ULONGLONG_MAX ULLONG_MAX
+#else
+#define ULONGLONG_MAX ((unsigned long long)(~0ULL))
+#endif
+#endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/
+
+#define YY_PART_YEAR 70
+
+MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1];
+my_bool mysql_ps_subsystem_initialized= 0;
+
+
+#define NUMERIC_TRUNCATION(val,min_range, max_range)\
+ ((((val) > (max_range)) || ((val) < (min_range)) ? 1 : 0))
+
+
+void ma_bmove_upp(register char *dst, register const char *src, register size_t len)
+{
+ while (len-- != 0) *--dst = *--src;
+}
+
+/* {{{ ps_fetch_from_1_to_8_bytes */
+void ps_fetch_from_1_to_8_bytes(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row, unsigned int byte_count)
+{
+ my_bool is_unsigned= test(field->flags & UNSIGNED_FLAG);
+ r_param->buffer_length= byte_count;
+ switch (byte_count) {
+ case 1:
+ *(uchar *)r_param->buffer= **row;
+ *r_param->error= is_unsigned != r_param->is_unsigned && *(uchar *)r_param->buffer > INT_MAX8;
+ break;
+ case 2:
+ shortstore(r_param->buffer, ((ushort) sint2korr(*row)));
+ *r_param->error= is_unsigned != r_param->is_unsigned && *(ushort *)r_param->buffer > INT_MAX16;
+ break;
+ case 4:
+ {
+ longstore(r_param->buffer, ((uint32)sint4korr(*row)));
+ *r_param->error= is_unsigned != r_param->is_unsigned && *(uint32 *)r_param->buffer > INT_MAX32;
+ }
+ break;
+ case 8:
+ {
+ ulonglong val= (ulonglong)sint8korr(*row);
+ longlongstore(r_param->buffer, val);
+ *r_param->error= is_unsigned != r_param->is_unsigned && val > LONGLONG_MAX ;
+ }
+ break;
+ default:
+ r_param->buffer_length= 0;
+ break;
+ }
+ (*row)+= byte_count;
+}
+/* }}} */
+
+static longlong my_atoll(const char *number, const char *end, int *error)
+{
+ char buffer[255];
+ longlong llval= 0;
+ size_t i;
+ *error= 0;
+ /* set error at the following conditions:
+ - string contains invalid character(s)
+ - length > 254
+ - strtoll returns invalid range
+ */
+
+ memcpy(buffer, number, MIN((uint)(end - number), 254));
+ buffer[(uint)(end - number)]= 0;
+
+ errno= 0;
+#ifdef _MSC_VER
+ llval = _strtoi64(buffer, NULL, 10);
+#else
+ llval= strtoll(buffer, NULL, 10);
+#endif
+
+ /* check size */
+ if ((uint)(end - number) > 254)
+ {
+ *error= 1;
+ return llval;
+ }
+
+ /* check characters */
+ for (i=0; i < strlen(buffer); i++)
+ {
+ if ((buffer[i] < '0' || buffer[i] > '9') && !isspace(buffer[i]))
+ {
+ *error= 1;
+ return llval;
+ }
+ }
+
+ /* check strtoll result */
+ if (errno == ERANGE)
+ *error= errno;
+ return llval;
+}
+
+double my_atod(const char *number, const char *end, int *error)
+{
+ double val= 0.0;
+ char buffer[255];
+ int len= (int)(end - number);
+
+ if (len > 254)
+ *error= 1;
+
+ len= MIN(len, 254);
+ memcpy(&buffer, number, len);
+ buffer[len]= '\0';
+
+ val= strtod(buffer, NULL);
+/* if (!*error)
+ *error= errno; */
+ return val;
+}
+
+my_bool str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm)
+{
+ my_bool is_time=0, is_date=0, has_time_frac=0;
+ char *p= (char *)str;
+
+ if ((p= strchr(str, '-')) && p <= str + length)
+ is_date= 1;
+ if ((p= strchr(str, ':')) && p <= str + length)
+ is_time= 1;
+ if ((p= strchr(str, '.')) && p <= str + length)
+ has_time_frac= 1;
+
+ p= (char *)str;
+
+ memset(tm, 0, sizeof(MYSQL_TIME));
+
+ if (is_date)
+ {
+ sscanf(str, "%d-%d-%d", &tm->year, &tm->month, &tm->day);
+ p= strchr(str, ' ');
+ if (!p)
+ {
+ tm->time_type= MYSQL_TIMESTAMP_DATE;
+ return 0;
+ }
+ }
+ if (has_time_frac)
+ {
+ sscanf(p, "%d:%d:%d.%ld", &tm->hour, &tm->minute, &tm->second, &tm->second_part);
+ tm->time_type= (is_date) ? MYSQL_TIMESTAMP_DATETIME : MYSQL_TIMESTAMP_TIME;
+ return 0;
+ }
+ if (is_time)
+ {
+ sscanf(p, "%d:%d:%d", &tm->hour, &tm->minute, &tm->second);
+ tm->time_type= (is_date) ? MYSQL_TIMESTAMP_DATETIME : MYSQL_TIMESTAMP_TIME;
+ return 0;
+ }
+ return 1;
+}
+
+
+static void convert_froma_string(MYSQL_BIND *r_param, char *buffer, size_t len)
+{
+ int error= 0;
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ {
+ longlong val= my_atoll(buffer, buffer + len, &error);
+ *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8) || error > 0;
+ int1store(r_param->buffer, (uchar) val);
+ r_param->buffer_length= sizeof(uchar);
+ }
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ {
+ longlong val= my_atoll(buffer, buffer + len, &error);
+ *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16) || error > 0;
+ shortstore(r_param->buffer, (short)val);
+ r_param->buffer_length= sizeof(short);
+ }
+ break;
+ case MYSQL_TYPE_LONG:
+ {
+ longlong val= my_atoll(buffer, buffer + len, &error);
+ *r_param->error=error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32) || error > 0;
+ longstore(r_param->buffer, (int32)val);
+ r_param->buffer_length= sizeof(uint32);
+ }
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ longlong val= my_atoll(buffer, buffer + len, &error);
+ *r_param->error= error > 0; /* no need to check for truncation */
+ longlongstore(r_param->buffer, val);
+ r_param->buffer_length= sizeof(longlong);
+ }
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double val= my_atod(buffer, buffer + len, &error);
+ *r_param->error= error > 0; /* no need to check for truncation */
+ doublestore((uchar *)r_param->buffer, val);
+ r_param->buffer_length= sizeof(double);
+ }
+ break;
+ case MYSQL_TYPE_FLOAT:
+ {
+ float val= (float)my_atod(buffer, buffer + len, &error);
+ *r_param->error= error > 0; /* no need to check for truncation */
+ floatstore((uchar *)r_param->buffer, val);
+ r_param->buffer_length= sizeof(float);
+ }
+ break;
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ MYSQL_TIME *tm= (MYSQL_TIME *)r_param->buffer;
+ str_to_TIME(buffer, len, tm);
+ break;
+ }
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ default:
+ {
+ char *start= buffer + r_param->offset; /* stmt_fetch_column sets offset */
+ char *end= buffer + len;
+ size_t copylen= 0;
+
+ if (start < end)
+ {
+ copylen= end - start;
+ if (r_param->buffer_length)
+ memcpy(r_param->buffer, start, MIN(copylen, r_param->buffer_length));
+ }
+ if (copylen < r_param->buffer_length)
+ ((char *)r_param->buffer)[copylen]= 0;
+ *r_param->error= (copylen > r_param->buffer_length);
+
+ *r_param->length= (ulong)len;
+ }
+ break;
+ }
+}
+
+static void convert_from_long(MYSQL_BIND *r_param, const MYSQL_FIELD *field, longlong val, my_bool is_unsigned)
+{
+ switch (r_param->buffer_type) {
+ case MYSQL_TYPE_TINY:
+ *(uchar *)r_param->buffer= (uchar)val;
+ *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8);
+ r_param->buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ shortstore(r_param->buffer, (short)val);
+ *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16);
+ r_param->buffer_length= 2;
+ break;
+ case MYSQL_TYPE_LONG:
+ longstore(r_param->buffer, (int32)val);
+ *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32);
+ r_param->buffer_length= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ *r_param->error= (val < 0 && r_param->is_unsigned != is_unsigned);
+ longlongstore(r_param->buffer, val);
+ r_param->buffer_length= 8;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ {
+ volatile double dbl;
+
+ dbl= (is_unsigned) ? ulonglong2double((ulonglong)val) : (double)val;
+ doublestore(r_param->buffer, dbl);
+
+ *r_param->error = (dbl != ceil(dbl)) ||
+ (is_unsigned ? (ulonglong )dbl != (ulonglong)val :
+ (longlong)dbl != (longlong)val);
+
+ r_param->buffer_length= 8;
+ break;
+ }
+ case MYSQL_TYPE_FLOAT:
+ {
+ float fval;
+ fval= is_unsigned ? (float)(ulonglong)(val) : (float)val;
+ floatstore((uchar *)r_param->buffer, fval);
+ *r_param->error= (fval != ceilf(fval)) ||
+ (is_unsigned ? (ulonglong)fval != (ulonglong)val :
+ (longlong)fval != val);
+ r_param->buffer_length= 4;
+ }
+ break;
+ default:
+ {
+ char buffer[22];
+ char *endptr;
+ uint len;
+
+ endptr= ma_ll2str(val, buffer, is_unsigned ? 10 : -10);
+ len= (uint)(endptr - buffer);
+
+ /* check if field flag is zerofill */
+ if (field->flags & ZEROFILL_FLAG &&
+ len < field->length && len < r_param->buffer_length)
+ {
+ ma_bmove_upp(buffer + field->length, buffer + len, len);
+ memset((char*) buffer, '0', field->length - len);
+ len= field->length;
+ }
+
+ convert_froma_string(r_param, buffer, len);
+ }
+ break;
+ }
+}
+
+
+/* {{{ ps_fetch_null */
+static
+void ps_fetch_null(MYSQL_BIND *r_param __attribute__((unused)),
+ const MYSQL_FIELD * field __attribute__((unused)),
+ unsigned char **row __attribute__((unused)))
+{
+ /* do nothing */
+}
+/* }}} */
+
+#define GET_LVALUE_FROM_ROW(is_unsigned, data, ucast, scast)\
+ (is_unsigned) ? (longlong)(ucast) *(longlong *)(data) : (longlong)(scast) *(longlong *)(data)
+/* {{{ ps_fetch_int8 */
+static
+void ps_fetch_int8(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch(r_param->buffer_type) {
+ case MYSQL_TYPE_TINY:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
+ break;
+ default:
+ {
+ uchar val= **row;
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong) val : (longlong)(signed char)val;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 1;
+ }
+ break;
+ }
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int16 */
+static
+void ps_fetch_int16(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch (r_param->buffer_type) {
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
+ break;
+ default:
+ {
+ short sval= sint2korr(*row);
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ushort) sval : (longlong)sval;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 2;
+ }
+ break;
+ }
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int32 */
+static
+void ps_fetch_int32(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch (r_param->buffer_type) {
+/* case MYSQL_TYPE_TINY:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
+ break; */
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
+ break;
+ default:
+ {
+ int32 sval= sint4korr(*row);
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(uint32) sval : (longlong)sval;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 4;
+ }
+ break;
+ }
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int64 */
+static
+void ps_fetch_int64(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch(r_param->buffer_type)
+ {
+/* case MYSQL_TYPE_TINY:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
+ break;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
+ break; */
+ case MYSQL_TYPE_LONGLONG:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 8);
+ break;
+ default:
+ {
+ longlong sval= (longlong)sint8korr(*row);
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ulonglong) sval : (longlong)sval;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 8;
+ }
+ break;
+ }
+}
+/* }}} */
+
+static void convert_from_float(MYSQL_BIND *r_param, const MYSQL_FIELD *field, float val, int size __attribute__((unused)))
+{
+ double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
+ char *buf= (char *)r_param->buffer;
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
+ *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
+ (double)((int8)*buf));
+ r_param->buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ {
+ if (r_param->is_unsigned)
+ {
+ ushort sval= (ushort)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ } else {
+ short sval= (short)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ }
+ r_param->buffer_length= 2;
+ }
+ break;
+ case MYSQL_TYPE_LONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ uint32 lval= (uint32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ } else {
+ int32 lval= (int32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ }
+ r_param->buffer_length= 4;
+ }
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ ulonglong llval= (ulonglong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ } else {
+ longlong llval= (longlong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ }
+ r_param->buffer_length= 8;
+ }
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double dval= (double)val;
+ memcpy(buf, &dval, sizeof(double));
+ r_param->buffer_length= 8;
+ }
+ break;
+ default:
+ {
+ char buff[MAX_DOUBLE_STRING_REP_LENGTH];
+ size_t length;
+
+ length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
+
+ if (field->decimals >= NOT_FIXED_DEC)
+ {
+ length= ma_gcvt(val, MY_GCVT_ARG_FLOAT, (int)length, buff, NULL);
+ }
+ else
+ {
+ length= ma_fcvt(val, field->decimals, buff, NULL);
+ }
+
+ /* check if ZEROFILL flag is active */
+ if (field->flags & ZEROFILL_FLAG)
+ {
+ /* enough space available ? */
+ if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
+ break;
+ ma_bmove_upp(buff + field->length, buff + length, length);
+ memset((char*) buff, '0', field->length - length);
+ length= field->length;
+ }
+
+ convert_froma_string(r_param, buff, length);
+ }
+ break;
+ }
+}
+
+static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size __attribute__((unused)))
+{
+ double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
+ char *buf= (char *)r_param->buffer;
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
+ *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
+ (double)((int8)*buf));
+ r_param->buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ {
+ if (r_param->is_unsigned)
+ {
+ ushort sval= (ushort)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ } else {
+ short sval= (short)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ }
+ r_param->buffer_length= 2;
+ }
+ break;
+ case MYSQL_TYPE_LONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ uint32 lval= (uint32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ } else {
+ int32 lval= (int32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ }
+ r_param->buffer_length= 4;
+ }
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ ulonglong llval= (ulonglong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ } else {
+ longlong llval= (longlong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ }
+ r_param->buffer_length= 8;
+ }
+ break;
+ case MYSQL_TYPE_FLOAT:
+ {
+ float fval= (float)val;
+ memcpy(buf, &fval, sizeof(float));
+ *r_param->error= (*(float*)buf != fval);
+ r_param->buffer_length= 4;
+ }
+ break;
+ default:
+ {
+ char buff[MAX_DOUBLE_STRING_REP_LENGTH];
+ size_t length;
+
+ length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
+
+ if (field->decimals >= NOT_FIXED_DEC)
+ {
+ length= ma_gcvt(val, MY_GCVT_ARG_DOUBLE, (int)length, buff, NULL);
+ }
+ else
+ {
+ length= ma_fcvt(val, field->decimals, buff, NULL);
+ }
+
+ /* check if ZEROFILL flag is active */
+ if (field->flags & ZEROFILL_FLAG)
+ {
+ /* enough space available ? */
+ if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
+ break;
+ ma_bmove_upp(buff + field->length, buff + length, length);
+ memset((char*) buff, '0', field->length - length);
+ length= field->length;
+ }
+ convert_froma_string(r_param, buff, length);
+ }
+ break;
+ }
+}
+
+
+/* {{{ ps_fetch_double */
+static
+void ps_fetch_double(MYSQL_BIND *r_param, const MYSQL_FIELD * field , unsigned char **row)
+{
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double *value= (double *)r_param->buffer;
+ float8get(*value, *row);
+ r_param->buffer_length= 8;
+ }
+ break;
+ default:
+ {
+ double value;
+ float8get(value, *row);
+ convert_from_double(r_param, field, value, sizeof(double));
+ }
+ break;
+ }
+ (*row)+= 8;
+}
+/* }}} */
+
+/* {{{ ps_fetch_float */
+static
+void ps_fetch_float(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row)
+{
+ switch(r_param->buffer_type)
+ {
+ case MYSQL_TYPE_FLOAT:
+ {
+ float *value= (float *)r_param->buffer;
+ float4get(*value, *row);
+ r_param->buffer_length= 4;
+ *r_param->error= 0;
+ }
+ break;
+ default:
+ {
+ float value;
+ memcpy(&value, *row, sizeof(float));
+ float4get(value, (char *)*row);
+ convert_from_float(r_param, field, value, sizeof(float));
+ }
+ break;
+ }
+ (*row)+= 4;
+}
+/* }}} */
+
+static void convert_to_datetime(MYSQL_TIME *t, unsigned char **row, uint len, enum enum_field_types type)
+{
+ memset(t, 0, sizeof(MYSQL_TIME));
+
+ /* binary protocol for datetime:
+ 4-bytes: DATE
+ 7-bytes: DATE + TIME
+ >7 bytes: DATE + TIME with second_part
+ */
+ if (len)
+ {
+ unsigned char *to= *row;
+ int has_date= 0;
+ uint offset= 7;
+
+ if (type == MYSQL_TYPE_TIME)
+ {
+ t->neg= to[0];
+ t->day= (ulong) sint4korr(to + 1);
+ t->time_type= MYSQL_TIMESTAMP_TIME;
+ offset= 8;
+ to++;
+ } else
+ {
+ t->year= (uint) sint2korr(to);
+ t->month= (uint) to[2];
+ t->day= (uint) to[3];
+ t->time_type= MYSQL_TIMESTAMP_DATE;
+ if (type == MYSQL_TYPE_DATE)
+ return;
+ has_date= 1;
+ }
+
+ if (len > 4)
+ {
+ t->hour= (uint) to[4];
+ if (type == MYSQL_TYPE_TIME)
+ t->hour+= t->day * 24;
+ t->minute= (uint) to[5];
+ t->second= (uint) to[6];
+ if (has_date)
+ t->time_type= MYSQL_TIMESTAMP_DATETIME;
+ }
+ if (len > offset)
+ {
+ t->second_part= (ulong)sint4korr(to+7);
+ }
+ }
+}
+
+
+/* {{{ ps_fetch_datetime */
+static
+void ps_fetch_datetime(MYSQL_BIND *r_param, const MYSQL_FIELD * field,
+ unsigned char **row)
+{
+ MYSQL_TIME *t= (MYSQL_TIME *)r_param->buffer;
+ unsigned int len= net_field_length(row);
+
+ switch (r_param->buffer_type) {
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ convert_to_datetime(t, row, len, field->type);
+ break;
+ case MYSQL_TYPE_DATE:
+ convert_to_datetime(t, row, len, field->type);
+ break;
+ case MYSQL_TYPE_TIME:
+ convert_to_datetime(t, row, len, field->type);
+ t->year= t->day= t->month= 0;
+ break;
+ case MYSQL_TYPE_YEAR:
+ {
+ MYSQL_TIME tm;
+ convert_to_datetime(&tm, row, len, field->type);
+ shortstore(r_param->buffer, tm.year);
+ break;
+ }
+ default:
+ {
+ char dtbuffer[60];
+ MYSQL_TIME tm;
+ size_t length;
+ convert_to_datetime(&tm, row, len, field->type);
+ /*
+ if (tm.time_type== MYSQL_TIMESTAMP_TIME && tm.day)
+ {
+ tm.hour+= tm.day * 24;
+ tm.day=0;
+ }
+*/
+ switch(field->type) {
+ case MYSQL_TYPE_DATE:
+ length= sprintf(dtbuffer, "%04u-%02u-%02u", tm.year, tm.month, tm.day);
+ break;
+ case MYSQL_TYPE_TIME:
+ length= sprintf(dtbuffer, "%s%02u:%02u:%02u", (tm.neg ? "-" : ""), tm.hour, tm.minute, tm.second);
+ if (field->decimals && field->decimals <= 6)
+ {
+ char ms[8];
+ sprintf(ms, ".%06lu", tm.second_part);
+ if (field->decimals < 6)
+ ms[field->decimals + 1]= 0;
+ length+= strlen(ms);
+ strcat(dtbuffer, ms);
+ }
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ length= sprintf(dtbuffer, "%04u-%02u-%02u %02u:%02u:%02u", tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second);
+ if (field->decimals && field->decimals <= 6)
+ {
+ char ms[8];
+ sprintf(ms, ".%06lu", tm.second_part);
+ if (field->decimals < 6)
+ ms[field->decimals + 1]= 0;
+ length+= strlen(ms);
+ strcat(dtbuffer, ms);
+ }
+ break;
+ default:
+ dtbuffer[0]= 0;
+ length= 0;
+ break;
+ }
+ convert_froma_string(r_param, dtbuffer, length);
+ break;
+ }
+ }
+ (*row) += len;
+}
+/* }}} */
+
+/* {{{ ps_fetch_string */
+static
+void ps_fetch_string(MYSQL_BIND *r_param,
+ const MYSQL_FIELD *field __attribute__((unused)),
+ unsigned char **row)
+{
+ /* C-API differs from PHP. While PHP just converts string to string,
+ C-API needs to convert the string to the defined type with in
+ the result bind buffer.
+ */
+ ulong field_length= net_field_length(row);
+
+ convert_froma_string(r_param, (char *)*row, field_length);
+ (*row) += field_length;
+}
+/* }}} */
+
+/* {{{ ps_fetch_bin */
+static
+void ps_fetch_bin(MYSQL_BIND *r_param,
+ const MYSQL_FIELD *field,
+ unsigned char **row)
+{
+ if (field->charsetnr == 63)
+ {
+ ulong field_length= *r_param->length= net_field_length(row);
+ uchar *current_pos= (*row) + r_param->offset,
+ *end= (*row) + field_length;
+ size_t copylen= 0;
+
+ if (current_pos < end)
+ {
+ copylen= end - current_pos;
+ if (r_param->buffer_length)
+ memcpy(r_param->buffer, current_pos, MIN(copylen, r_param->buffer_length));
+ }
+ if (copylen < r_param->buffer_length &&
+ (r_param->buffer_type == MYSQL_TYPE_STRING ||
+ r_param->buffer_type == MYSQL_TYPE_JSON))
+ ((char *)r_param->buffer)[copylen]= 0;
+ *r_param->error= copylen > r_param->buffer_length;
+ (*row)+= field_length;
+ }
+ else
+ ps_fetch_string(r_param, field, row);
+}
+/* }}} */
+
+/* {{{ _mysqlnd_init_ps_subsystem */
+void mysql_init_ps_subsystem(void)
+{
+ memset(mysql_ps_fetch_functions, 0, sizeof(mysql_ps_fetch_functions));
+ mysql_ps_fetch_functions[MYSQL_TYPE_NULL].func= ps_fetch_null;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NULL].max_len = 0;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY].max_len = 4;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].max_len = 6;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16;
+ mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2;
+ mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].max_len = 6;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32;
+ mysql_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4;
+ mysql_ps_fetch_functions[MYSQL_TYPE_INT24].max_len = 9;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG].max_len = 11;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].max_len = 21;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float;
+ mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4;
+ mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIME].max_len = 17;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATE].max_len = 10;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].max_len = 30;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].max_len = 30;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BIT].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_STRING].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_JSON].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_JSON].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SET].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].max_len = -1;
+
+ mysql_ps_subsystem_initialized= 1;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/mysql/libmariadb/ma_string.c b/mysql/libmariadb/ma_string.c
new file mode 100644
index 0000000..be3a21f
--- /dev/null
+++ b/mysql/libmariadb/ma_string.c
@@ -0,0 +1,133 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/*
+ Code for handling strings with can grow dynamicly.
+ Copyright Monty Program KB.
+ By monty.
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+
+my_bool ma_init_dynamic_string(DYNAMIC_STRING *str, const char *init_str,
+ size_t init_alloc, size_t alloc_increment)
+{
+ uint length;
+
+ if (!alloc_increment)
+ alloc_increment=128;
+ length=1;
+ if (init_str && (length= (uint) strlen(init_str)+1) < init_alloc)
+ init_alloc=((length+alloc_increment-1)/alloc_increment)*alloc_increment;
+ if (!init_alloc)
+ init_alloc=alloc_increment;
+
+ if (!(str->str=(char*) malloc(init_alloc)))
+ return(TRUE);
+ str->length=length-1;
+ if (init_str)
+ memcpy(str->str,init_str,length);
+ str->max_length=init_alloc;
+ str->alloc_increment=alloc_increment;
+ return(FALSE);
+}
+
+my_bool ma_dynstr_set(DYNAMIC_STRING *str, const char *init_str)
+{
+ uint length;
+
+ if (init_str && (length= (uint) strlen(init_str)+1) > str->max_length)
+ {
+ str->max_length=((length+str->alloc_increment-1)/str->alloc_increment)*
+ str->alloc_increment;
+ if (!str->max_length)
+ str->max_length=str->alloc_increment;
+ if (!(str->str=(char*) realloc(str->str,str->max_length)))
+ return(TRUE);
+ }
+ if (init_str)
+ {
+ str->length=length-1;
+ memcpy(str->str,init_str,length);
+ }
+ else
+ str->length=0;
+ return(FALSE);
+}
+
+
+my_bool ma_dynstr_realloc(DYNAMIC_STRING *str, size_t additional_size)
+{
+ if (!additional_size) return(FALSE);
+ if (str->length + additional_size > str->max_length)
+ {
+ str->max_length=((str->length + additional_size+str->alloc_increment-1)/
+ str->alloc_increment)*str->alloc_increment;
+ if (!(str->str=(char*) realloc(str->str,str->max_length)))
+ return(TRUE);
+ }
+ return(FALSE);
+}
+
+
+my_bool ma_dynstr_append(DYNAMIC_STRING *str, const char *append)
+{
+ return ma_dynstr_append_mem(str,append,strlen(append));
+}
+
+
+my_bool ma_dynstr_append_mem(DYNAMIC_STRING *str, const char *append,
+ size_t length)
+{
+ char *new_ptr;
+ if (str->length+length >= str->max_length)
+ {
+ size_t new_length=(str->length+length+str->alloc_increment)/
+ str->alloc_increment;
+ new_length*=str->alloc_increment;
+ if (!(new_ptr=(char*) realloc(str->str,new_length)))
+ return TRUE;
+ str->str=new_ptr;
+ str->max_length=new_length;
+ }
+ memcpy(str->str + str->length,append,length);
+ str->length+=length;
+ str->str[str->length]=0; /* Safety for C programs */
+ return FALSE;
+}
+
+
+void ma_dynstr_free(DYNAMIC_STRING *str)
+{
+ if (str->str)
+ {
+ free(str->str);
+ str->str=0;
+ }
+}
+
+char *ma_strmake(register char *dst, register const char *src, size_t length)
+{
+ while (length--)
+ if (! (*dst++ = *src++))
+ return dst-1;
+ *dst=0;
+ return dst;
+}
diff --git a/mysql/libmariadb/ma_time.c b/mysql/libmariadb/ma_time.c
new file mode 100644
index 0000000..5b4087b
--- /dev/null
+++ b/mysql/libmariadb/ma_time.c
@@ -0,0 +1,65 @@
+/****************************************************************************
+ Copyright (C) 2013 Monty Program AB
+ 2016 MariaDB Corporation 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
+*****************************************************************************/
+#include <ma_global.h>
+#include <mysql.h>
+#include <stdio.h>
+
+
+size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len,
+ unsigned int digits)
+{
+ size_t length;
+
+ if (!time_str || !len)
+ return 0;
+
+ if (digits == AUTO_SEC_PART_DIGITS)
+ digits= MIN((tm->second_part) ? SEC_PART_DIGITS : 0, 15);
+
+ switch(tm->time_type) {
+ case MYSQL_TIMESTAMP_DATE:
+ length= snprintf(time_str, len, "%04u-%02u-%02u", tm->year, tm->month, tm->day);
+ digits= 0;
+ break;
+ case MYSQL_TIMESTAMP_DATETIME:
+ length= snprintf(time_str, len, "%04u-%02u-%02u %02u:%02u:%02u",
+ tm->year, tm->month, tm->day, tm->hour, tm->minute, tm->second);
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ length= snprintf(time_str, len, "%s%02u:%02u:%02u",
+ (tm->neg ? "-" : ""), tm->hour, tm->minute, tm->second);
+ break;
+ default:
+ time_str[0]= '\0';
+ return 0;
+ break;
+ }
+ if (digits && (len < length))
+ {
+ char helper[16];
+ snprintf(helper, 16, ".%%0%du", digits);
+ length+= snprintf(time_str + length, len - length, helper, digits);
+ }
+ return length;
+}
+
diff --git a/mysql/libmariadb/ma_tls.c b/mysql/libmariadb/ma_tls.c
new file mode 100644
index 0000000..7629bca
--- /dev/null
+++ b/mysql/libmariadb/ma_tls.c
@@ -0,0 +1,232 @@
+/************************************************************************************
+ Copyright (C) 2014 MariaDB Corporation 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
+
+ *************************************************************************************/
+
+/*
+ * this is the abstraction layer for communication via SSL.
+ * The following SSL libraries/variants are currently supported:
+ * - openssl
+ * - gnutls
+ * - schannel (windows only)
+ *
+ * Different SSL variants are implemented as plugins
+ * On Windows schannel is implemented as (standard)
+ * built-in plugin.
+ */
+
+#ifdef HAVE_TLS
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <string.h>
+#include <errmsg.h>
+#include <ma_pvio.h>
+#include <ma_tls.h>
+#include <mysql/client_plugin.h>
+#include <mariadb/ma_io.h>
+
+#ifdef HAVE_NONBLOCK
+#include <mariadb_async.h>
+#include <ma_context.h>
+#endif
+
+/* Errors should be handled via pvio callback function */
+my_bool ma_tls_initialized= FALSE;
+unsigned int mariadb_deinitialize_ssl= 1;
+
+const char *tls_protocol_version[]=
+ {"SSLv3", "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3", "Unknown"};
+
+MARIADB_TLS *ma_pvio_tls_init(MYSQL *mysql)
+{
+ MARIADB_TLS *ctls= NULL;
+
+ if (!ma_tls_initialized)
+ ma_tls_start(mysql->net.last_error, MYSQL_ERRMSG_SIZE);
+
+ if (!(ctls= (MARIADB_TLS *)calloc(1, sizeof(MARIADB_TLS))))
+ {
+ return NULL;
+ }
+
+ /* register error routine and methods */
+ ctls->pvio= mysql->net.pvio;
+ if (!(ctls->ssl= ma_tls_init(mysql)))
+ {
+ free(ctls);
+ ctls= NULL;
+ }
+ return ctls;
+}
+
+my_bool ma_pvio_tls_connect(MARIADB_TLS *ctls)
+{
+ my_bool rc;
+
+ if ((rc= ma_tls_connect(ctls)))
+ ma_tls_close(ctls);
+ return rc;
+}
+
+ssize_t ma_pvio_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ return ma_tls_read(ctls, buffer, length);
+}
+
+ssize_t ma_pvio_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ return ma_tls_write(ctls, buffer, length);
+}
+
+my_bool ma_pvio_tls_close(MARIADB_TLS *ctls)
+{
+ return ma_tls_close(ctls);
+}
+
+int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls)
+{
+ return ma_tls_verify_server_cert(ctls);
+}
+
+const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls)
+{
+ return ma_tls_get_cipher(ctls);
+}
+
+void ma_pvio_tls_end()
+{
+ ma_tls_end();
+}
+
+int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls)
+{
+ return ma_tls_get_protocol_version(ctls);
+}
+
+const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls)
+{
+ int version;
+
+ version= ma_tls_get_protocol_version(ctls);
+ if (version < 0 || version > PROTOCOL_MAX)
+ return tls_protocol_version[PROTOCOL_UNKNOWN];
+ return tls_protocol_version[version];
+}
+
+static char ma_hex2int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+ return -1;
+}
+
+static my_bool ma_pvio_tls_compare_fp(const char *cert_fp,
+ unsigned int cert_fp_len,
+ const char *fp, unsigned int fp_len)
+{
+ char *p= (char *)fp,
+ *c;
+
+ /* check length */
+ if (cert_fp_len != 20)
+ return 1;
+
+ /* We support two formats:
+ 2 digits hex numbers, separated by colons (length=59)
+ 20 * 2 digits hex numbers without separators (length = 40)
+ */
+ if (fp_len != (strchr(fp, ':') ? 59 : 40))
+ return 1;
+
+ for(c= (char *)cert_fp; c < cert_fp + cert_fp_len; c++)
+ {
+ char d1, d2;
+ if (*p == ':')
+ p++;
+ if (p - fp > (int)fp_len -1)
+ return 1;
+ if ((d1 = ma_hex2int(*p)) == - 1 ||
+ (d2 = ma_hex2int(*(p+1))) == -1 ||
+ (char)(d1 * 16 + d2) != *c)
+ return 1;
+ p+= 2;
+ }
+ return 0;
+}
+
+my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list)
+{
+ unsigned int cert_fp_len= 64;
+ char *cert_fp= NULL;
+ my_bool rc=1;
+ MYSQL *mysql= ctls->pvio->mysql;
+
+ cert_fp= (char *)malloc(cert_fp_len);
+
+ if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1)
+ goto end;
+ if (fp)
+ rc= ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, fp, (unsigned int)strlen(fp));
+ else if (fp_list)
+ {
+ MA_FILE *fp;
+ char buff[255];
+
+ if (!(fp = ma_open(fp_list, "r", mysql)))
+ goto end;
+
+ while (ma_gets(buff, sizeof(buff)-1, fp))
+ {
+ /* remove trailing new line character */
+ char *pos= strchr(buff, '\r');
+ if (!pos)
+ pos= strchr(buff, '\n');
+ if (pos)
+ *pos= '\0';
+
+ if (!ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, buff, (unsigned int)strlen(buff)))
+ {
+ /* finger print is valid: close file and exit */
+ ma_close(fp);
+ rc= 0;
+ goto end;
+ }
+ }
+
+ /* No finger print matched - close file and return error */
+ ma_close(fp);
+ }
+
+end:
+ if (cert_fp)
+ free(cert_fp);
+ if (rc)
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR),
+ "Fingerprint verification of server certificate failed");
+ }
+ return rc;
+}
+#endif /* HAVE_TLS */
diff --git a/mysql/libmariadb/mariadb_async.c b/mysql/libmariadb/mariadb_async.c
new file mode 100644
index 0000000..6d607cd
--- /dev/null
+++ b/mysql/libmariadb/mariadb_async.c
@@ -0,0 +1,1946 @@
+/* Copyright (C) 2012 MariaDB Services and Kristian Nielsen
+ 2015 MariaDB Corporation
+
+ 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+*/
+
+/*
+ MySQL non-blocking client library functions.
+*/
+
+#include "ma_global.h"
+#include "ma_sys.h"
+#include "mysql.h"
+#include "errmsg.h"
+#ifndef LIBMARIADB
+#include "sql_common.h"
+#else
+#include "ma_common.h"
+#endif
+#include "ma_context.h"
+#include "ma_pvio.h"
+#include "mariadb_async.h"
+#include <string.h>
+
+
+#ifdef _WIN32
+/*
+ Windows does not support MSG_DONTWAIT for send()/recv(). So we need to ensure
+ that the socket is non-blocking at the start of every operation.
+*/
+#define WIN_SET_NONBLOCKING(mysql) { \
+ my_bool old_mode; \
+ if ((mysql)->net.pvio) ma_pvio_blocking((mysql)->net.pvio, FALSE, &old_mode); \
+ }
+#else
+#define WIN_SET_NONBLOCKING(mysql)
+#endif
+
+extern void mysql_close_slow_part(MYSQL *mysql);
+
+
+void
+my_context_install_suspend_resume_hook(struct mysql_async_context *b,
+ void (*hook)(my_bool, void *),
+ void *user_data)
+{
+ b->suspend_resume_hook= hook;
+ b->suspend_resume_hook_user_data= user_data;
+}
+
+
+/* Asynchronous connect(); socket must already be set non-blocking. */
+int
+my_connect_async(MARIADB_PVIO *pvio,
+ const struct sockaddr *name, uint namelen, int vio_timeout)
+{
+ int res;
+ size_socket s_err_size;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ my_socket sock;
+
+ ma_pvio_get_handle(pvio, &sock);
+
+ /* Make the socket non-blocking. */
+ ma_pvio_blocking(pvio, 0, 0);
+
+ b->events_to_wait_for= 0;
+ /*
+ Start to connect asynchronously.
+ If this will block, we suspend the call and return control to the
+ application context. The application will then resume us when the socket
+ polls ready for write, indicating that the connection attempt completed.
+ */
+ res= connect(sock, name, namelen);
+ if (res != 0)
+ {
+#ifdef _WIN32
+ int wsa_err= WSAGetLastError();
+ if (wsa_err != WSAEWOULDBLOCK)
+ return res;
+ b->events_to_wait_for|= MYSQL_WAIT_EXCEPT;
+#else
+ int err= errno;
+ if (err != EINPROGRESS && err != EALREADY && err != EAGAIN)
+ return res;
+#endif
+ b->events_to_wait_for|= MYSQL_WAIT_WRITE;
+ if (vio_timeout >= 0)
+ {
+ b->timeout_value= vio_timeout;
+ b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
+ }
+ else
+ b->timeout_value= 0;
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ if (b->events_occured & MYSQL_WAIT_TIMEOUT)
+ return -1;
+
+ s_err_size= sizeof(res);
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*) &res, &s_err_size) != 0)
+ return -1;
+ if (res)
+ {
+ errno= res;
+ return -1;
+ }
+ }
+ return res;
+}
+
+#define IS_BLOCKING_ERROR() \
+ IF_WIN(WSAGetLastError() != WSAEWOULDBLOCK, \
+ (errno != EAGAIN && errno != EINTR))
+
+#ifdef _AIX
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+#endif
+
+#ifdef HAVE_TLS_FIXME
+static my_bool
+my_ssl_async_check_result(int res, struct mysql_async_context *b, MARIADB_SSL *cssl)
+{
+ int ssl_err;
+ b->events_to_wait_for= 0;
+ if (res >= 0)
+ return 1;
+ ssl_err= SSL_get_error(ssl, res);
+ if (ssl_err == SSL_ERROR_WANT_READ)
+ b->events_to_wait_for|= MYSQL_WAIT_READ;
+ else if (ssl_err == SSL_ERROR_WANT_WRITE)
+ b->events_to_wait_for|= MYSQL_WAIT_WRITE;
+ else
+ return 1;
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ return 0;
+}
+
+int
+my_ssl_read_async(struct mysql_async_context *b, SSL *ssl,
+ void *buf, int size)
+{
+ int res;
+
+ for (;;)
+ {
+ res= SSL_read(ssl, buf, size);
+ if (my_ssl_async_check_result(res, b, ssl))
+ return res;
+ }
+}
+
+int
+my_ssl_write_async(struct mysql_async_context *b, SSL *ssl,
+ const void *buf, int size)
+{
+ int res;
+
+ for (;;)
+ {
+ res= SSL_write(ssl, buf, size);
+ if (my_ssl_async_check_result(res, b, ssl))
+ return res;
+ }
+}
+#endif /* HAVE_OPENSSL */
+
+
+
+
+/*
+ Now create non-blocking definitions for all the calls that may block.
+
+ Each call FOO gives rise to FOO_start() that prepares the MYSQL object for
+ doing non-blocking calls that can suspend operation mid-way, and then starts
+ the call itself. And a FOO_start_internal trampoline to assist with running
+ the real call in a co-routine that can be suspended. And a FOO_cont() that
+ can continue a suspended operation.
+*/
+
+#define MK_ASYNC_INTERNAL_BODY(call, invoke_args, mysql_val, ret_type, ok_val)\
+ struct call ## _params *parms= (struct call ## _params *)d; \
+ ret_type ret; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ \
+ ret= call invoke_args; \
+ b->ret_result. ok_val = ret; \
+ b->events_to_wait_for= 0;
+
+#define MK_ASYNC_START_BODY(call, mysql_val, parms_assign, err_val, ok_val, extra1) \
+ int res; \
+ struct mysql_async_context *b; \
+ struct call ## _params parms; \
+ \
+ extra1 \
+ b= mysql_val->options.extension->async_context; \
+ parms_assign \
+ \
+ b->active= 1; \
+ res= my_context_spawn(&b->async_context, call ## _start_internal, &parms); \
+ b->active= b->suspended= 0; \
+ if (res > 0) \
+ { \
+ /* Suspended. */ \
+ b->suspended= 1; \
+ return b->events_to_wait_for; \
+ } \
+ if (res < 0) \
+ { \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ *ret= err_val; \
+ } \
+ else \
+ *ret= b->ret_result. ok_val; \
+ return 0;
+
+#define MK_ASYNC_CONT_BODY(mysql_val, err_val, ok_val) \
+ int res; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ if (!b->suspended) \
+ { \
+ set_mariadb_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \
+ *ret= err_val; \
+ return 0; \
+ } \
+ \
+ b->active= 1; \
+ b->events_occured= ready_status; \
+ res= my_context_continue(&b->async_context); \
+ b->active= 0; \
+ if (res > 0) \
+ return b->events_to_wait_for; /* (Still) suspended */ \
+ b->suspended= 0; \
+ if (res < 0) \
+ { \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ *ret= err_val; \
+ } \
+ else \
+ *ret= b->ret_result. ok_val; /* Finished. */ \
+ return 0;
+
+#define MK_ASYNC_INTERNAL_BODY_VOID_RETURN(call, invoke_args, mysql_val) \
+ struct call ## _params *parms= (struct call ## _params *)d; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ \
+ call invoke_args; \
+ b->events_to_wait_for= 0;
+
+#define MK_ASYNC_START_BODY_VOID_RETURN(call, mysql_val, parms_assign, extra1)\
+ int res; \
+ struct mysql_async_context *b; \
+ struct call ## _params parms; \
+ \
+ extra1 \
+ b= mysql_val->options.extension->async_context; \
+ parms_assign \
+ \
+ b->active= 1; \
+ res= my_context_spawn(&b->async_context, call ## _start_internal, &parms); \
+ b->active= b->suspended= 0; \
+ if (res > 0) \
+ { \
+ /* Suspended. */ \
+ b->suspended= 1; \
+ return b->events_to_wait_for; \
+ } \
+ if (res < 0) \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ return 0;
+
+#define MK_ASYNC_CONT_BODY_VOID_RETURN(mysql_val) \
+ int res; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ if (!b->suspended) \
+ { \
+ set_mariadb_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \
+ return 0; \
+ } \
+ \
+ b->active= 1; \
+ b->events_occured= ready_status; \
+ res= my_context_continue(&b->async_context); \
+ b->active= 0; \
+ if (res > 0) \
+ return b->events_to_wait_for; /* (Still) suspended */ \
+ b->suspended= 0; \
+ if (res < 0) \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ return 0;
+
+
+/* Structure used to pass parameters from mysql_real_connect_start(). */
+struct mysql_real_connect_params {
+ MYSQL *mysql;
+ const char *host;
+ const char *user;
+ const char *passwd;
+ const char *db;
+ unsigned int port;
+ const char *unix_socket;
+ unsigned long client_flags;
+};
+static void
+mysql_real_connect_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_real_connect,
+ (parms->mysql, parms->host, parms->user, parms->passwd, parms->db,
+ parms->port, parms->unix_socket, parms->client_flags),
+ parms->mysql,
+ MYSQL *,
+ r_ptr)
+}
+int STDCALL
+mysql_real_connect_start(MYSQL **ret, MYSQL *mysql, const char *host,
+ const char *user, const char *passwd, const char *db,
+ unsigned int port, const char *unix_socket,
+ unsigned long client_flags)
+{
+MK_ASYNC_START_BODY(
+ mysql_real_connect,
+ mysql,
+ {
+ parms.mysql= mysql;
+ parms.host= host;
+ parms.user= user;
+ parms.passwd= passwd;
+ parms.db= db;
+ parms.port= port;
+ parms.unix_socket= unix_socket;
+ parms.client_flags= client_flags | CLIENT_REMEMBER_OPTIONS;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_real_connect_cont(MYSQL **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_real_query_start(). */
+struct mysql_real_query_params {
+ MYSQL *mysql;
+ const char *stmt_str;
+ unsigned long length;
+};
+static void
+mysql_real_query_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_real_query,
+ (parms->mysql, parms->stmt_str, parms->length),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_real_query_start(int *ret, MYSQL *mysql, const char *stmt_str, unsigned long length)
+{
+ int res;
+ struct mysql_async_context *b;
+ struct mysql_real_query_params parms;
+
+ b= mysql->options.extension->async_context;
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.stmt_str= stmt_str;
+ parms.length= length;
+ }
+
+ b->active= 1;
+ res= my_context_spawn(&b->async_context, mysql_real_query_start_internal, &parms);
+ b->active= b->suspended= 0;
+ if (res > 0)
+ {
+ /* Suspended. */
+ b->suspended= 1;
+ return b->events_to_wait_for;
+ }
+ if (res < 0)
+ {
+ set_mariadb_error((mysql), CR_OUT_OF_MEMORY, unknown_sqlstate);
+ *ret= 1;
+ }
+ else
+ *ret= b->ret_result.r_int;
+ return 0;
+
+}
+int STDCALL
+mysql_real_query_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_fetch_row_start(). */
+struct mysql_fetch_row_params {
+ MYSQL_RES *result;
+};
+static void
+mysql_fetch_row_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_fetch_row,
+ (parms->result),
+ parms->result->handle,
+ MYSQL_ROW,
+ r_ptr)
+}
+int STDCALL
+mysql_fetch_row_start(MYSQL_ROW *ret, MYSQL_RES *result)
+{
+MK_ASYNC_START_BODY(
+ mysql_fetch_row,
+ result->handle,
+ {
+ WIN_SET_NONBLOCKING(result->handle)
+ parms.result= result;
+ },
+ NULL,
+ r_ptr,
+ /*
+ If we already fetched all rows from server (eg. mysql_store_result()),
+ then result->handle will be NULL and we cannot suspend. But that is fine,
+ since in this case mysql_fetch_row cannot block anyway. Just return
+ directly.
+ */
+ if (!result->handle)
+ {
+ *ret= mysql_fetch_row(result);
+ return 0;
+ })
+}
+int STDCALL
+mysql_fetch_row_cont(MYSQL_ROW *ret, MYSQL_RES *result, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ result->handle,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_set_character_set_start(). */
+struct mysql_set_character_set_params {
+ MYSQL *mysql;
+ const char *csname;
+};
+static void
+mysql_set_character_set_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_set_character_set,
+ (parms->mysql, parms->csname),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_set_character_set_start(int *ret, MYSQL *mysql, const char *csname)
+{
+MK_ASYNC_START_BODY(
+ mysql_set_character_set,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.csname= csname;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_set_character_set_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_sekect_db_start(). */
+struct mysql_select_db_params {
+ MYSQL *mysql;
+ const char *db;
+};
+static void
+mysql_select_db_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_select_db,
+ (parms->mysql, parms->db),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_select_db_start(int *ret, MYSQL *mysql, const char *db)
+{
+MK_ASYNC_START_BODY(
+ mysql_select_db,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.db= db;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_select_db_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_send_query_start(). */
+struct mysql_send_query_params {
+ MYSQL *mysql;
+ const char *q;
+ unsigned long length;
+};
+static void
+mysql_send_query_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_send_query,
+ (parms->mysql, parms->q, parms->length),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_send_query_start(int *ret, MYSQL *mysql, const char *q, unsigned long length)
+{
+MK_ASYNC_START_BODY(
+ mysql_send_query,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.q= q;
+ parms.length= length;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_send_query_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_store_result_start(). */
+struct mysql_store_result_params {
+ MYSQL *mysql;
+};
+static void
+mysql_store_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_store_result,
+ (parms->mysql),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_store_result_start(MYSQL_RES **ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_store_result,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_store_result_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_free_result_start(). */
+struct mysql_free_result_params {
+ MYSQL_RES *result;
+};
+static void
+mysql_free_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY_VOID_RETURN(
+ mysql_free_result,
+ (parms->result),
+ parms->result->handle)
+}
+int STDCALL
+mysql_free_result_start(MYSQL_RES *result)
+{
+MK_ASYNC_START_BODY_VOID_RETURN(
+ mysql_free_result,
+ result->handle,
+ {
+ WIN_SET_NONBLOCKING(result->handle)
+ parms.result= result;
+ },
+ /*
+ mysql_free_result() can have NULL in result->handle (this happens when all
+ rows have been fetched and mysql_fetch_row() returned NULL.)
+ So we cannot suspend, but it does not matter, as in this case
+ mysql_free_result() cannot block.
+ It is also legitimate to have NULL result, which will do nothing.
+ */
+ if (!result || !result->handle)
+ {
+ mysql_free_result(result);
+ return 0;
+ })
+}
+int STDCALL
+mysql_free_result_cont(MYSQL_RES *result, int ready_status)
+{
+MK_ASYNC_CONT_BODY_VOID_RETURN(result->handle)
+}
+
+/* Structure used to pass parameters from mysql_close_slow_part_start(). */
+struct mysql_close_slow_part_params {
+ MYSQL *sock;
+};
+/*
+ We need special handling for mysql_close(), as the first part may block,
+ while the last part needs to free our extra library context stack.
+
+ So we do the first part (mysql_close_slow_part()) non-blocking, but the last
+ part blocking.
+*/
+static void
+mysql_close_slow_part_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY_VOID_RETURN(
+ mysql_close_slow_part,
+ (parms->sock),
+ parms->sock)
+}
+
+int STDCALL
+mysql_close_slow_part_start(MYSQL *sock)
+{
+MK_ASYNC_START_BODY_VOID_RETURN(
+ mysql_close_slow_part,
+ sock,
+ {
+ WIN_SET_NONBLOCKING(sock)
+ parms.sock= sock;
+ },
+ /* Nothing */)
+}
+int STDCALL
+mysql_close_slow_part_cont(MYSQL *sock, int ready_status)
+{
+MK_ASYNC_CONT_BODY_VOID_RETURN(sock)
+}
+int STDCALL
+mysql_close_start(MYSQL *sock)
+{
+ int res;
+
+ /* It is legitimate to have NULL sock argument, which will do nothing. */
+ if (sock && sock->net.pvio)
+ {
+ res= mysql_close_slow_part_start(sock);
+ /* If we need to block, return now and do the rest in mysql_close_cont(). */
+ if (res)
+ return res;
+ }
+ mysql_close(sock);
+ return 0;
+}
+int STDCALL
+mysql_close_cont(MYSQL *sock, int ready_status)
+{
+ int res;
+
+ res= mysql_close_slow_part_cont(sock, ready_status);
+ if (res)
+ return res;
+ mysql_close(sock);
+ return 0;
+}
+
+/*
+ These following are not available inside the server (neither blocking or
+ non-blocking).
+*/
+#ifndef MYSQL_SERVER
+/* Structure used to pass parameters from mysql_change_user_start(). */
+struct mysql_change_user_params {
+ MYSQL *mysql;
+ const char *user;
+ const char *passwd;
+ const char *db;
+};
+static void
+mysql_change_user_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_change_user,
+ (parms->mysql, parms->user, parms->passwd, parms->db),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_change_user_start(my_bool *ret, MYSQL *mysql, const char *user, const char *passwd, const char *db)
+{
+MK_ASYNC_START_BODY(
+ mysql_change_user,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.user= user;
+ parms.passwd= passwd;
+ parms.db= db;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_change_user_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_query_start(). */
+struct mysql_query_params {
+ MYSQL *mysql;
+ const char *q;
+};
+static void
+mysql_query_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_query,
+ (parms->mysql, parms->q),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_query_start(int *ret, MYSQL *mysql, const char *q)
+{
+MK_ASYNC_START_BODY(
+ mysql_query,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.q= q;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_query_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_shutdown_start(). */
+struct mysql_shutdown_params {
+ MYSQL *mysql;
+ enum mysql_enum_shutdown_level shutdown_level;
+};
+static void
+mysql_shutdown_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_shutdown,
+ (parms->mysql, parms->shutdown_level),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_shutdown_start(int *ret, MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level)
+{
+MK_ASYNC_START_BODY(
+ mysql_shutdown,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.shutdown_level= shutdown_level;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_shutdown_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_dump_debug_info_start(). */
+struct mysql_dump_debug_info_params {
+ MYSQL *mysql;
+};
+static void
+mysql_dump_debug_info_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_dump_debug_info,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_dump_debug_info_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_dump_debug_info,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_dump_debug_info_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_refresh_start(). */
+struct mysql_refresh_params {
+ MYSQL *mysql;
+ unsigned int refresh_options;
+};
+static void
+mysql_refresh_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_refresh,
+ (parms->mysql, parms->refresh_options),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_refresh_start(int *ret, MYSQL *mysql, unsigned int refresh_options)
+{
+MK_ASYNC_START_BODY(
+ mysql_refresh,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.refresh_options= refresh_options;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_refresh_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_kill_start(). */
+struct mysql_kill_params {
+ MYSQL *mysql;
+ unsigned long pid;
+};
+static void
+mysql_kill_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_kill,
+ (parms->mysql, parms->pid),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_kill_start(int *ret, MYSQL *mysql, unsigned long pid)
+{
+MK_ASYNC_START_BODY(
+ mysql_kill,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.pid= pid;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_kill_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_set_server_option_start(). */
+struct mysql_set_server_option_params {
+ MYSQL *mysql;
+ enum enum_mysql_set_option option;
+};
+static void
+mysql_set_server_option_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_set_server_option,
+ (parms->mysql, parms->option),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_set_server_option_start(int *ret, MYSQL *mysql,
+ enum enum_mysql_set_option option)
+{
+MK_ASYNC_START_BODY(
+ mysql_set_server_option,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.option= option;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_set_server_option_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_ping_start(). */
+struct mysql_ping_params {
+ MYSQL *mysql;
+};
+static void
+mysql_ping_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_ping,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_ping_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_ping,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_ping_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_reset_connection_start(). */
+struct mysql_reset_connection_params {
+ MYSQL *mysql;
+};
+static void
+mysql_reset_connection_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_reset_connection,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_reset_connection_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_reset_connection,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_reset_connection_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stat_start(). */
+struct mysql_stat_params {
+ MYSQL *mysql;
+};
+static void
+mysql_stat_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stat,
+ (parms->mysql),
+ parms->mysql,
+ const char *,
+ r_const_ptr)
+}
+int STDCALL
+mysql_stat_start(const char **ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_stat,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ NULL,
+ r_const_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_stat_cont(const char **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_const_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_dbs_start(). */
+struct mysql_list_dbs_params {
+ MYSQL *mysql;
+ const char *wild;
+};
+static void
+mysql_list_dbs_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_dbs,
+ (parms->mysql, parms->wild),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_dbs_start(MYSQL_RES **ret, MYSQL *mysql, const char *wild)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_dbs,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.wild= wild;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_dbs_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_tables_start(). */
+struct mysql_list_tables_params {
+ MYSQL *mysql;
+ const char *wild;
+};
+static void
+mysql_list_tables_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_tables,
+ (parms->mysql, parms->wild),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_tables_start(MYSQL_RES **ret, MYSQL *mysql, const char *wild)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_tables,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.wild= wild;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_tables_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_processes_start(). */
+struct mysql_list_processes_params {
+ MYSQL *mysql;
+};
+static void
+mysql_list_processes_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_processes,
+ (parms->mysql),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_processes_start(MYSQL_RES **ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_processes,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_processes_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_fields_start(). */
+struct mysql_list_fields_params {
+ MYSQL *mysql;
+ const char *table;
+ const char *wild;
+};
+static void
+mysql_list_fields_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_fields,
+ (parms->mysql, parms->table, parms->wild),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_fields_start(MYSQL_RES **ret, MYSQL *mysql, const char *table,
+ const char *wild)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_fields,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.table= table;
+ parms.wild= wild;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_fields_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_read_query_result_start(). */
+struct mysql_read_query_result_params {
+ MYSQL *mysql;
+};
+static void
+mysql_read_query_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_read_query_result,
+ (parms->mysql),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_read_query_result_start(my_bool *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_read_query_result,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_read_query_result_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_prepare_start(). */
+struct mysql_stmt_prepare_params {
+ MYSQL_STMT *stmt;
+ const char *query;
+ unsigned long length;
+};
+static void
+mysql_stmt_prepare_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_prepare,
+ (parms->stmt, parms->query, parms->length),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_prepare_start(int *ret, MYSQL_STMT *stmt, const char *query,
+ unsigned long length)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_prepare,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ parms.query= query;
+ parms.length= length;
+ },
+ 1,
+ r_int,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_prepare(stmt, query, length);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_prepare_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_execute_start(). */
+struct mysql_stmt_execute_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_execute_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_execute,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_execute_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_execute,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /*
+ If eg. mysql_change_user(), stmt->mysql will be NULL.
+ In this case, we cannot block.
+ */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_execute(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_execute_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_fetch_start(). */
+struct mysql_stmt_fetch_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_fetch_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_fetch,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_fetch_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_fetch,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_fetch(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_fetch_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_store_result_start(). */
+struct mysql_stmt_store_result_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_store_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_store_result,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_store_result_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_store_result,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_store_result(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_store_result_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_close_start(). */
+struct mysql_stmt_close_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_close_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_close,
+ (parms->stmt),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_close_start(my_bool *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_close,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_close(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_close_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_reset_start(). */
+struct mysql_stmt_reset_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_reset_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_reset,
+ (parms->stmt),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_reset_start(my_bool *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_reset,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_reset(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_reset_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_free_result_start(). */
+struct mysql_stmt_free_result_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_free_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_free_result,
+ (parms->stmt),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_free_result_start(my_bool *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_free_result,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_free_result(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_free_result_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_send_long_data_start(). */
+struct mysql_stmt_send_long_data_params {
+ MYSQL_STMT *stmt;
+ unsigned int param_number;
+ const char *data;
+ unsigned long length;
+};
+static void
+mysql_stmt_send_long_data_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_send_long_data,
+ (parms->stmt, parms->param_number, parms->data, parms->length),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_send_long_data_start(my_bool *ret, MYSQL_STMT *stmt,
+ unsigned int param_number,
+ const char *data, unsigned long length)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_send_long_data,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ parms.param_number= param_number;
+ parms.data= data;
+ parms.length= length;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_send_long_data(stmt, param_number, data, length);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_send_long_data_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_commit_start(). */
+struct mysql_commit_params {
+ MYSQL *mysql;
+};
+static void
+mysql_commit_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_commit,
+ (parms->mysql),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_commit_start(my_bool *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_commit,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_commit_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_rollback_start(). */
+struct mysql_rollback_params {
+ MYSQL *mysql;
+};
+static void
+mysql_rollback_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_rollback,
+ (parms->mysql),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_rollback_start(my_bool *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_rollback,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_rollback_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_autocommit_start(). */
+struct mysql_autocommit_params {
+ MYSQL *mysql;
+ my_bool auto_mode;
+};
+static void
+mysql_autocommit_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_autocommit,
+ (parms->mysql, parms->auto_mode),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_autocommit_start(my_bool *ret, MYSQL *mysql, my_bool auto_mode)
+{
+MK_ASYNC_START_BODY(
+ mysql_autocommit,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.auto_mode= auto_mode;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_autocommit_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_next_result_start(). */
+struct mysql_next_result_params {
+ MYSQL *mysql;
+};
+static void
+mysql_next_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_next_result,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_next_result_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_next_result,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_next_result_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_next_result_start(). */
+struct mysql_stmt_next_result_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_next_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_next_result,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_next_result_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_next_result,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_stmt_next_result_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+#endif
+
+
+/*
+ The following functions are deprecated, and so have no non-blocking version:
+
+ mysql_connect
+ mysql_create_db
+ mysql_drop_db
+*/
+
+/*
+ The following functions can newer block, and so do not have special
+ non-blocking versions:
+
+ mysql_num_rows()
+ mysql_num_fields()
+ mysql_eof()
+ mysql_fetch_field_direct()
+ mysql_fetch_fields()
+ mysql_row_tell()
+ mysql_field_tell()
+ mysql_field_count()
+ mysql_affected_rows()
+ mysql_insert_id()
+ mysql_errno()
+ mysql_error()
+ mysql_sqlstate()
+ mysql_warning_count()
+ mysql_info()
+ mysql_thread_id()
+ mysql_character_set_name()
+ mysql_init()
+ mysql_ssl_set()
+ mysql_get_ssl_cipher()
+ mysql_use_result()
+ mysql_get_character_set_info()
+ mysql_set_local_infile_handler()
+ mysql_set_local_infile_default()
+ mysql_get_server_info()
+ mysql_get_server_name()
+ mysql_get_client_info()
+ mysql_get_client_version()
+ mysql_get_host_info()
+ mysql_get_server_version()
+ mysql_get_proto_info()
+ mysql_options()
+ mysql_data_seek()
+ mysql_row_seek()
+ mysql_field_seek()
+ mysql_fetch_lengths()
+ mysql_fetch_field()
+ mysql_escape_string()
+ mysql_hex_string()
+ mysql_real_escape_string()
+ mysql_debug()
+ myodbc_remove_escape()
+ mysql_thread_safe()
+ mysql_embedded()
+ mariadb_connection()
+ mysql_stmt_init()
+ mysql_stmt_fetch_column()
+ mysql_stmt_param_count()
+ mysql_stmt_attr_set()
+ mysql_stmt_attr_get()
+ mysql_stmt_bind_param()
+ mysql_stmt_bind_result()
+ mysql_stmt_result_metadata()
+ mysql_stmt_param_metadata()
+ mysql_stmt_errno()
+ mysql_stmt_error()
+ mysql_stmt_sqlstate()
+ mysql_stmt_row_seek()
+ mysql_stmt_row_tell()
+ mysql_stmt_data_seek()
+ mysql_stmt_num_rows()
+ mysql_stmt_affected_rows()
+ mysql_stmt_insert_id()
+ mysql_stmt_field_count()
+ mysql_more_results()
+ mysql_get_socket()
+ mysql_get_timeout_value()
+*/
diff --git a/mysql/libmariadb/mariadb_charset.c b/mysql/libmariadb/mariadb_charset.c
new file mode 100644
index 0000000..fd89aea
--- /dev/null
+++ b/mysql/libmariadb/mariadb_charset.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016 MariaDB Corporation 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+// #include "mysys_err.h"
+#include <mariadb_ctype.h>
+#include <ma_string.h>
+
+MARIADB_CHARSET_INFO *ma_default_charset_info = (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[5];
+MARIADB_CHARSET_INFO *ma_charset_bin= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[32];
+MARIADB_CHARSET_INFO *ma_charset_latin1= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[5];
+MARIADB_CHARSET_INFO *ma_charset_utf8_general_ci= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[21];
+MARIADB_CHARSET_INFO *ma_charset_utf16le_general_ci= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[68];
+
+MARIADB_CHARSET_INFO * STDCALL mysql_get_charset_by_nr(uint cs_number)
+{
+ int i= 0;
+
+ while (mariadb_compiled_charsets[i].nr && cs_number != mariadb_compiled_charsets[i].nr)
+ i++;
+
+ return (mariadb_compiled_charsets[i].nr) ? (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[i] : NULL;
+}
+
+my_bool set_default_charset(uint cs, myf flags __attribute__((unused)))
+{
+ MARIADB_CHARSET_INFO *new_charset;
+ new_charset = mysql_get_charset_by_nr(cs);
+ if (!new_charset)
+ {
+ return(TRUE); /* error */
+ }
+ ma_default_charset_info = new_charset;
+ return(FALSE);
+}
+
+MARIADB_CHARSET_INFO * STDCALL mysql_get_charset_by_name(const char *cs_name)
+{
+ int i= 0;
+
+ while (mariadb_compiled_charsets[i].nr && strcmp(cs_name, mariadb_compiled_charsets[i].csname) != 0)
+ i++;
+
+ return (mariadb_compiled_charsets[i].nr) ? (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[i] : NULL;
+}
+
+my_bool set_default_charset_by_name(const char *cs_name, myf flags __attribute__((unused)))
+{
+ MARIADB_CHARSET_INFO *new_charset;
+ new_charset = mysql_get_charset_by_name(cs_name);
+ if (!new_charset)
+ {
+ return(TRUE); /* error */
+ }
+
+ ma_default_charset_info = new_charset;
+ return(FALSE);
+}
diff --git a/mysql/libmariadb/mariadb_dyncol.c b/mysql/libmariadb/mariadb_dyncol.c
new file mode 100644
index 0000000..80c12b8
--- /dev/null
+++ b/mysql/libmariadb/mariadb_dyncol.c
@@ -0,0 +1,4367 @@
+/* Copyright (c) 2011,2013 Monty Program Ab;
+ Copyright (c) 2011,2012 Oleksandr Byelkin
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+*/
+
+/*
+ Numeric format:
+ ===============
+ * Fixed header part
+ 1 byte flags:
+ 0,1 bits - <offset size> - 1
+ 2-7 bits - 0
+ 2 bytes column counter
+ * Columns directory sorted by column number, each entry contains of:
+ 2 bytes column number
+ <offset size> bytes (1-4) combined offset from beginning of
+ the data segment + 3 bit type
+ * Data of above columns size of data and length depend on type
+
+ Columns with names:
+ ===================
+ * Fixed header part
+ 1 byte flags:
+ 0,1 bits - <offset size> - 2
+ 2 bit - 1 (means format with names)
+ 3,4 bits - 00 (means <names offset size> - 2,
+ now 2 is the only supported size)
+ 5-7 bits - 0
+ 2 bytes column counter
+ * Variable header part (now it is actually fixed part)
+ <names offset size> (2) bytes size of stored names pool
+ * Column directory sorted by names, each consists of
+ <names offset size> (2) bytes offset of name
+ <offset size> bytes (2-5)bytes combined offset from beginning of
+ the data segment + 4 bit type
+ * Names stored one after another
+ * Data of above columns size of data and length depend on type
+*/
+
+#include <stdio.h>
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <ma_hash.h>
+#include <mariadb_dyncol.h>
+#include <mysql.h>
+
+
+
+#ifndef LIBMARIADB
+uint32 copy_and_convert(char *to, uint32 to_length, MARIADB_CHARSET_INFO *to_cs,
+ const char *from, uint32 from_length,
+ MARIADB_CHARSET_INFO *from_cs, uint *errors);
+#else
+
+size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len,
+ unsigned int digits);
+size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, MARIADB_CHARSET_INFO *from_cs,
+ char *to, size_t *to_len, MARIADB_CHARSET_INFO *to_cs, int *errorcode);
+#endif
+/*
+ Flag byte bits
+
+ 2 bits which determinate size of offset in the header -1
+*/
+/* mask to get above bits */
+#define DYNCOL_FLG_OFFSET (1|2)
+#define DYNCOL_FLG_NAMES 4
+#define DYNCOL_FLG_NMOFFSET (8|16)
+/**
+ All known flags mask that could be set.
+
+ @note DYNCOL_FLG_NMOFFSET should be 0 for now.
+*/
+#define DYNCOL_FLG_KNOWN (1|2|4)
+
+/* formats */
+enum enum_dyncol_format
+{
+ dyncol_fmt_num= 0,
+ dyncol_fmt_str= 1
+};
+
+/* dynamic column size reserve */
+#define DYNCOL_SYZERESERVE 80
+
+#define DYNCOL_OFFSET_ERROR 0xffffffff
+
+/* length of fixed string header 1 byte - flags, 2 bytes - columns counter */
+#define FIXED_HEADER_SIZE 3
+/*
+ length of fixed string header with names
+ 1 byte - flags, 2 bytes - columns counter, 2 bytes - name pool size
+*/
+#define FIXED_HEADER_SIZE_NM 5
+
+#define COLUMN_NUMBER_SIZE 2
+/* 2 bytes offset from the name pool */
+#define COLUMN_NAMEPTR_SIZE 2
+
+#define MAX_OFFSET_LENGTH 4
+#define MAX_OFFSET_LENGTH_NM 5
+
+#define DYNCOL_NUM_CHAR 6
+
+my_bool mariadb_dyncol_has_names(DYNAMIC_COLUMN *str)
+{
+ if (str->length < 1)
+ return FALSE;
+ return test(str->str[0] & DYNCOL_FLG_NAMES);
+}
+
+static enum enum_dyncol_func_result
+dynamic_column_time_store(DYNAMIC_COLUMN *str,
+ MYSQL_TIME *value, enum enum_dyncol_format format);
+static enum enum_dyncol_func_result
+dynamic_column_date_store(DYNAMIC_COLUMN *str,
+ MYSQL_TIME *value);
+static enum enum_dyncol_func_result
+dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length);
+static enum enum_dyncol_func_result
+dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length);
+static enum enum_dyncol_func_result
+dynamic_column_get_internal(DYNAMIC_COLUMN *str,
+ DYNAMIC_COLUMN_VALUE *store_it_here,
+ uint num_key, LEX_STRING *str_key);
+static enum enum_dyncol_func_result
+dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key,
+ LEX_STRING *str_key);
+static enum enum_dyncol_func_result
+dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool string_keys);
+static int plan_sort_num(const void *a, const void *b);
+static int plan_sort_named(const void *a, const void *b);
+
+/*
+ Structure to hold information about dynamic columns record and
+ iterate through it.
+*/
+
+struct st_dyn_header
+{
+ uchar *header, *nmpool, *dtpool, *data_end;
+ size_t offset_size;
+ size_t entry_size;
+ size_t header_size;
+ size_t nmpool_size;
+ size_t data_size;
+ /* dyncol_fmt_num - numeric columns, dyncol_fmt_str - column names */
+ enum enum_dyncol_format format;
+ uint column_count;
+
+ uchar *entry, *data, *name;
+ size_t offset;
+ size_t length;
+ enum enum_dynamic_column_type type;
+};
+
+typedef struct st_dyn_header DYN_HEADER;
+
+static inline my_bool read_fixed_header(DYN_HEADER *hdr,
+ DYNAMIC_COLUMN *str);
+static void set_fixed_header(DYNAMIC_COLUMN *str,
+ uint offset_size,
+ uint column_count);
+
+/*
+ Calculate entry size (E) and header size (H) by offset size (O) and column
+ count (C) and fixed part of entry size (F).
+*/
+
+#define calc_param(E,H,F,O,C) do { \
+ (*(E))= (O) + F; \
+ (*(H))= (*(E)) * (C); \
+}while(0);
+
+
+/**
+ Name pool size functions, for numeric format it is 0
+*/
+
+static size_t name_size_num(void *keys __attribute__((unused)),
+ uint i __attribute__((unused)))
+{
+ return 0;
+}
+
+
+/**
+ Name pool size functions.
+*/
+static size_t name_size_named(void *keys, uint i)
+{
+ return ((LEX_STRING *) keys)[i].length;
+}
+
+
+/**
+ Comparator function for references on column numbers for qsort
+ (numeric format)
+*/
+
+static int column_sort_num(const void *a, const void *b)
+{
+ return **((uint **)a) - **((uint **)b);
+}
+
+/**
+ Comparator function for references on column numbers for qsort
+ (names format)
+*/
+
+int mariadb_dyncol_column_cmp_named(const LEX_STRING *s1, const LEX_STRING *s2)
+{
+ /*
+ We compare instead of subtraction to avoid data loss in case of huge
+ length difference (more then fit in int).
+ */
+ int rc= (s1->length > s2->length ? 1 :
+ (s1->length < s2->length ? -1 : 0));
+ if (rc == 0)
+ rc= memcmp((void *)s1->str, (void *)s2->str,
+ (size_t) s1->length);
+ return rc;
+}
+
+
+/**
+ Comparator function for references on column numbers for qsort
+ (names format)
+*/
+
+static int column_sort_named(const void *a, const void *b)
+{
+ return mariadb_dyncol_column_cmp_named(*((LEX_STRING **)a),
+ *((LEX_STRING **)b));
+}
+
+
+/**
+ Check limit function (numeric format)
+*/
+
+static my_bool check_limit_num(const void *val)
+{
+ return **((uint **)val) > UINT_MAX16;
+}
+
+
+/**
+ Check limit function (names format)
+*/
+
+static my_bool check_limit_named(const void *val)
+{
+ return (*((LEX_STRING **)val))->length > MAX_NAME_LENGTH;
+}
+
+
+/**
+ Write numeric format static header part.
+*/
+
+static void set_fixed_header_num(DYNAMIC_COLUMN *str, DYN_HEADER *hdr)
+{
+ set_fixed_header(str, (uint)hdr->offset_size, hdr->column_count);
+ hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE;
+ hdr->nmpool= hdr->dtpool= hdr->header + hdr->header_size;
+}
+
+
+/**
+ Write names format static header part.
+*/
+
+static void set_fixed_header_named(DYNAMIC_COLUMN *str, DYN_HEADER *hdr)
+{
+ DBUG_ASSERT(hdr->column_count <= 0xffff);
+ DBUG_ASSERT(hdr->offset_size <= MAX_OFFSET_LENGTH_NM);
+ /* size of data offset, named format flag, size of names offset (0 means 2) */
+ str->str[0]=
+ (char) ((str->str[0] & ~(DYNCOL_FLG_OFFSET | DYNCOL_FLG_NMOFFSET)) |
+ (hdr->offset_size - 2) | DYNCOL_FLG_NAMES);
+ int2store(str->str + 1, hdr->column_count); /* columns number */
+ int2store(str->str + 3, hdr->nmpool_size);
+ hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE_NM;
+ hdr->nmpool= hdr->header + hdr->header_size;
+ hdr->dtpool= hdr->nmpool + hdr->nmpool_size;
+}
+
+
+/**
+ Store offset and type information in the given place
+
+ @param place Beginning of the index entry
+ @param offset_size Size of offset field in bytes
+ @param type Type to be written
+ @param offset Offset to be written
+*/
+
+static my_bool type_and_offset_store_num(uchar *place, size_t offset_size,
+ DYNAMIC_COLUMN_TYPE type,
+ size_t offset)
+{
+ ulong val = (((ulong) offset) << 3) | (type - 1);
+ DBUG_ASSERT(type != DYN_COL_NULL);
+ DBUG_ASSERT(((type - 1) & (~7)) == 0); /* fit in 3 bits */
+ DBUG_ASSERT(offset_size >= 1 && offset_size <= 4);
+
+ /* Index entry starts with column number; jump over it */
+ place+= COLUMN_NUMBER_SIZE;
+
+ switch (offset_size) {
+ case 1:
+ if (offset >= 0x1f) /* all 1 value is reserved */
+ return TRUE;
+ place[0]= (uchar)val;
+ break;
+ case 2:
+ if (offset >= 0x1fff) /* all 1 value is reserved */
+ return TRUE;
+ int2store(place, val);
+ break;
+ case 3:
+ if (offset >= 0x1fffff) /* all 1 value is reserved */
+ return TRUE;
+ int3store(place, val);
+ break;
+ case 4:
+ if (offset >= 0x1fffffff) /* all 1 value is reserved */
+ return TRUE;
+ int4store(place, val);
+ break;
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static my_bool type_and_offset_store_named(uchar *place, size_t offset_size,
+ DYNAMIC_COLUMN_TYPE type,
+ size_t offset)
+{
+ ulonglong val = (((ulong) offset) << 4) | (type - 1);
+ DBUG_ASSERT(type != DYN_COL_NULL);
+ DBUG_ASSERT(((type - 1) & (~0xf)) == 0); /* fit in 4 bits */
+ DBUG_ASSERT(offset_size >= 2 && offset_size <= 5);
+
+ /* Index entry starts with name offset; jump over it */
+ place+= COLUMN_NAMEPTR_SIZE;
+ switch (offset_size) {
+ case 2:
+ if (offset >= 0xfff) /* all 1 value is reserved */
+ return TRUE;
+ int2store(place, val);
+ break;
+ case 3:
+ if (offset >= 0xfffff) /* all 1 value is reserved */
+ return TRUE;
+ int3store(place, val);
+ break;
+ case 4:
+ if (offset >= 0xfffffff) /* all 1 value is reserved */
+ return TRUE;
+ int4store(place, val);
+ break;
+ case 5:
+#if SIZEOF_SIZE_T > 4
+ if (offset >= 0xfffffffffull) /* all 1 value is reserved */
+ return TRUE;
+#endif
+ int5store(place, val);
+ break;
+ case 1:
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ Write numeric format header entry
+ 2 bytes - column number
+ 1-4 bytes - data offset combined with type
+
+ @param hdr descriptor of dynamic column record
+ @param column_key pointer to uint (column number)
+ @param value value which will be written (only type used)
+ @param offset offset of the data
+*/
+
+static my_bool put_header_entry_num(DYN_HEADER *hdr,
+ void *column_key,
+ DYNAMIC_COLUMN_VALUE *value,
+ size_t offset)
+{
+ uint *column_number= (uint *)column_key;
+ int2store(hdr->entry, *column_number);
+ DBUG_ASSERT(hdr->nmpool_size == 0);
+ if (type_and_offset_store_num(hdr->entry, hdr->offset_size,
+ value->type,
+ offset))
+ return TRUE;
+ hdr->entry= hdr->entry + hdr->entry_size;
+ return FALSE;
+}
+
+
+/**
+ Write names format header entry
+ 1 byte - name length
+ 2 bytes - name offset in the name pool
+ 1-4 bytes - data offset combined with type
+
+ @param hdr descriptor of dynamic column record
+ @param column_key pointer to LEX_STRING (column name)
+ @param value value which will be written (only type used)
+ @param offset offset of the data
+*/
+
+static my_bool put_header_entry_named(DYN_HEADER *hdr,
+ void *column_key,
+ DYNAMIC_COLUMN_VALUE *value,
+ size_t offset)
+{
+ LEX_STRING *column_name= (LEX_STRING *)column_key;
+ DBUG_ASSERT(column_name->length <= MAX_NAME_LENGTH);
+ DBUG_ASSERT(hdr->name - hdr->nmpool < (long) 0x10000L);
+ int2store(hdr->entry, hdr->name - hdr->nmpool);
+ memcpy(hdr->name, column_name->str, column_name->length);
+ DBUG_ASSERT(hdr->nmpool_size != 0 || column_name->length == 0);
+ if (type_and_offset_store_named(hdr->entry, hdr->offset_size,
+ value->type,
+ offset))
+ return TRUE;
+ hdr->entry+= hdr->entry_size;
+ hdr->name+= column_name->length;
+ return FALSE;
+}
+
+
+/**
+ Calculate length of offset field for given data length
+
+ @param data_length Length of the data segment
+
+ @return number of bytes
+*/
+
+static size_t dynamic_column_offset_bytes_num(size_t data_length)
+{
+ if (data_length < 0x1f) /* all 1 value is reserved */
+ return 1;
+ if (data_length < 0x1fff) /* all 1 value is reserved */
+ return 2;
+ if (data_length < 0x1fffff) /* all 1 value is reserved */
+ return 3;
+ if (data_length < 0x1fffffff) /* all 1 value is reserved */
+ return 4;
+ return MAX_OFFSET_LENGTH + 1; /* For an error generation*/
+}
+
+static size_t dynamic_column_offset_bytes_named(size_t data_length)
+{
+ if (data_length < 0xfff) /* all 1 value is reserved */
+ return 2;
+ if (data_length < 0xfffff) /* all 1 value is reserved */
+ return 3;
+ if (data_length < 0xfffffff) /* all 1 value is reserved */
+ return 4;
+#if SIZEOF_SIZE_T > 4
+ if (data_length < 0xfffffffffull) /* all 1 value is reserved */
+#endif
+ return 5;
+ return MAX_OFFSET_LENGTH_NM + 1; /* For an error generation */
+}
+
+/**
+ Read offset and type information from index entry
+
+ @param type Where to put type info
+ @param offset Where to put offset info
+ @param place beginning of the type and offset
+ @param offset_size Size of offset field in bytes
+*/
+
+static my_bool type_and_offset_read_num(DYNAMIC_COLUMN_TYPE *type,
+ size_t *offset,
+ uchar *place, size_t offset_size)
+{
+ ulong UNINIT_VAR(val);
+ ulong UNINIT_VAR(lim);
+
+ DBUG_ASSERT(offset_size >= 1 && offset_size <= 4);
+
+ switch (offset_size) {
+ case 1:
+ val= (ulong)place[0];
+ lim= 0x1f;
+ break;
+ case 2:
+ val= uint2korr(place);
+ lim= 0x1fff;
+ break;
+ case 3:
+ val= uint3korr(place);
+ lim= 0x1fffff;
+ break;
+ case 4:
+ val= uint4korr(place);
+ lim= 0x1fffffff;
+ break;
+ default:
+ DBUG_ASSERT(0); /* impossible */
+ return 1;
+ }
+ *type= (val & 0x7) + 1;
+ *offset= val >> 3;
+ return (*offset >= lim);
+}
+
+static my_bool type_and_offset_read_named(DYNAMIC_COLUMN_TYPE *type,
+ size_t *offset,
+ uchar *place, size_t offset_size)
+{
+ ulonglong UNINIT_VAR(val);
+ ulonglong UNINIT_VAR(lim);
+ DBUG_ASSERT(offset_size >= 2 && offset_size <= 5);
+
+ switch (offset_size) {
+ case 2:
+ val= uint2korr(place);
+ lim= 0xfff;
+ break;
+ case 3:
+ val= uint3korr(place);
+ lim= 0xfffff;
+ break;
+ case 4:
+ val= uint4korr(place);
+ lim= 0xfffffff;
+ break;
+ case 5:
+ val= uint5korr(place);
+ lim= 0xfffffffffull;
+ break;
+ case 1:
+ default:
+ DBUG_ASSERT(0); /* impossible */
+ return 1;
+ }
+ *type= (val & 0xf) + 1;
+ *offset= (size_t)(val >> 4);
+ return (*offset >= lim);
+}
+
+/**
+ Format descriptor, contain constants and function references for
+ format processing
+*/
+
+struct st_service_funcs
+{
+ /* size of fixed header */
+ uint fixed_hdr;
+ /* size of fixed part of header entry */
+ uint fixed_hdr_entry;
+
+ /*size of array element which stores keys */
+ uint key_size_in_array;
+
+ /* Maximum data offset size in bytes */
+ size_t max_offset_size;
+
+ size_t (*name_size)
+ (void *, uint);
+ int (*column_sort)
+ (const void *a, const void *b);
+ my_bool (*check_limit)
+ (const void *val);
+ void (*set_fixed_hdr)
+ (DYNAMIC_COLUMN *str, DYN_HEADER *hdr);
+ my_bool (*put_header_entry)(DYN_HEADER *hdr,
+ void *column_key,
+ DYNAMIC_COLUMN_VALUE *value,
+ size_t offset);
+ int (*plan_sort)(const void *a, const void *b);
+ size_t (*dynamic_column_offset_bytes)(size_t data_length);
+ my_bool (*type_and_offset_read)(DYNAMIC_COLUMN_TYPE *type,
+ size_t *offset,
+ uchar *place, size_t offset_size);
+
+};
+
+
+/**
+ Actual our 2 format descriptors
+*/
+
+static struct st_service_funcs fmt_data[2]=
+{
+ {
+ FIXED_HEADER_SIZE,
+ COLUMN_NUMBER_SIZE,
+ sizeof(uint),
+ MAX_OFFSET_LENGTH,
+ &name_size_num,
+ &column_sort_num,
+ &check_limit_num,
+ &set_fixed_header_num,
+ &put_header_entry_num,
+ &plan_sort_num,
+ &dynamic_column_offset_bytes_num,
+ &type_and_offset_read_num
+ },
+ {
+ FIXED_HEADER_SIZE_NM,
+ COLUMN_NAMEPTR_SIZE,
+ sizeof(LEX_STRING),
+ MAX_OFFSET_LENGTH_NM,
+ &name_size_named,
+ &column_sort_named,
+ &check_limit_named,
+ &set_fixed_header_named,
+ &put_header_entry_named,
+ &plan_sort_named,
+ &dynamic_column_offset_bytes_named,
+ &type_and_offset_read_named
+ }
+};
+
+
+/**
+ Read dynamic column record header and fill the descriptor
+
+ @param hdr dynamic columns record descriptor to fill
+ @param str dynamic columns record
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+init_read_hdr(DYN_HEADER *hdr, DYNAMIC_COLUMN *str)
+{
+ if (read_fixed_header(hdr, str))
+ return ER_DYNCOL_FORMAT;
+ hdr->header= (uchar*)str->str + fmt_data[hdr->format].fixed_hdr;
+ calc_param(&hdr->entry_size, &hdr->header_size,
+ fmt_data[hdr->format].fixed_hdr_entry, hdr->offset_size,
+ hdr->column_count);
+ hdr->nmpool= hdr->header + hdr->header_size;
+ hdr->dtpool= hdr->nmpool + hdr->nmpool_size;
+ hdr->data_size= str->length - fmt_data[hdr->format].fixed_hdr -
+ hdr->header_size - hdr->nmpool_size;
+ hdr->data_end= (uchar*)str->str + str->length;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Initialize dynamic column string with (make it empty but correct format)
+
+ @param str The string to initialize
+ @param size Amount of preallocated memory for the string.
+
+ @retval FALSE OK
+ @retval TRUE error
+*/
+
+static my_bool dynamic_column_init_named(DYNAMIC_COLUMN *str, size_t size)
+{
+ DBUG_ASSERT(size != 0);
+
+ /*
+ Make string with no fields (empty header)
+ - First \0 is flags
+ - other 2 \0 is number of fields
+ */
+ if (ma_init_dynamic_string(str, NULL, size, DYNCOL_SYZERESERVE))
+ return TRUE;
+ return FALSE;
+}
+
+
+/**
+ Calculate how many bytes needed to store val as variable length integer
+ where first bit indicate continuation of the sequence.
+
+ @param val The value for which we are calculating length
+
+ @return number of bytes
+*/
+
+static size_t dynamic_column_var_uint_bytes(ulonglong val)
+{
+ size_t len= 0;
+ do
+ {
+ len++;
+ val>>= 7;
+ } while (val);
+ return len;
+}
+
+
+/**
+ Stores variable length unsigned integer value to a string
+
+ @param str The string where to append the value
+ @param val The value to put in the string
+
+ @return ER_DYNCOL_* return code
+
+ @notes
+ This is used to store a number together with other data in the same
+ object. (Like decimals, length of string etc)
+ (As we don't know the length of this object, we can't store 0 in 0 bytes)
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_var_uint_store(DYNAMIC_COLUMN *str, ulonglong val)
+{
+ if (ma_dynstr_realloc(str, 10)) /* max what we can use */
+ return ER_DYNCOL_RESOURCE;
+
+ do
+ {
+ ulonglong rest= val >> 7;
+ str->str[str->length++]= ((val & 0x7f) | (rest ? 0x80 : 0x00));
+ val= rest;
+ } while (val);
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Reads variable length unsigned integer value from a string
+
+ @param data The string from which the int should be read
+ @param data_length Max length of data
+ @param len Where to put length of the string read in bytes
+
+ @return value of the unsigned integer read from the string
+
+ In case of error, *len is set to 0
+*/
+
+static ulonglong
+dynamic_column_var_uint_get(uchar *data, size_t data_length,
+ size_t *len)
+{
+ ulonglong val= 0;
+ uint length;
+ uchar *end= data + data_length;
+
+ for (length=0; data < end ; data++)
+ {
+ val+= (((ulonglong)((*data) & 0x7f)) << (length * 7));
+ length++;
+ if (!((*data) & 0x80))
+ {
+ /* End of data */
+ *len= length;
+ return val;
+ }
+ }
+ /* Something was wrong with data */
+ *len= 0; /* Mark error */
+ return 0;
+}
+
+
+/**
+ Calculate how many bytes needed to store val as unsigned.
+
+ @param val The value for which we are calculating length
+
+ @return number of bytes (0-8)
+*/
+
+static size_t dynamic_column_uint_bytes(ulonglong val)
+{
+ size_t len;
+
+ for (len= 0; val ; val>>= 8, len++)
+ ;
+ return len;
+}
+
+
+/**
+ Append the string with given unsigned int value.
+
+ @param str The string where to put the value
+ @param val The value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_uint_store(DYNAMIC_COLUMN *str, ulonglong val)
+{
+ if (ma_dynstr_realloc(str, 8)) /* max what we can use */
+ return ER_DYNCOL_RESOURCE;
+
+ for (; val; val>>= 8)
+ str->str[str->length++]= (char) (val & 0xff);
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read unsigned int value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_uint_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ ulonglong value= 0;
+ size_t i;
+
+ for (i= 0; i < length; i++)
+ value+= ((ulonglong)data[i]) << (i*8);
+
+ store_it_here->x.ulong_value= value;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Calculate how many bytes needed to store val as signed in following encoding:
+ 0 -> 0
+ -1 -> 1
+ 1 -> 2
+ -2 -> 3
+ 2 -> 4
+ ...
+
+ @param val The value for which we are calculating length
+
+ @return number of bytes
+*/
+
+static size_t dynamic_column_sint_bytes(longlong val)
+{
+ return dynamic_column_uint_bytes((val << 1) ^
+ (val < 0 ? 0xffffffffffffffffull : 0));
+}
+
+
+/**
+ Append the string with given signed int value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_sint_store(DYNAMIC_COLUMN *str, longlong val)
+{
+ return dynamic_column_uint_store(str,
+ (val << 1) ^
+ (val < 0 ? 0xffffffffffffffffULL : 0));
+}
+
+
+/**
+ Read signed int value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_sint_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ ulonglong val;
+ dynamic_column_uint_read(store_it_here, data, length);
+ val= store_it_here->x.ulong_value;
+ if (val & 1)
+ val= (val >> 1) ^ 0xffffffffffffffffULL;
+ else
+ val>>= 1;
+ store_it_here->x.long_value= (longlong) val;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Calculate how many bytes needed to store the value.
+
+ @param value The value for which we are calculating length
+
+ @return
+ Error: (size_t) ~0
+ ok number of bytes
+*/
+
+static size_t
+dynamic_column_value_len(DYNAMIC_COLUMN_VALUE *value,
+ enum enum_dyncol_format format)
+{
+ switch (value->type) {
+ case DYN_COL_NULL:
+ return 0;
+ case DYN_COL_INT:
+ return dynamic_column_sint_bytes(value->x.long_value);
+ case DYN_COL_UINT:
+ return dynamic_column_uint_bytes(value->x.ulong_value);
+ case DYN_COL_DOUBLE:
+ return 8;
+ case DYN_COL_STRING:
+#ifdef LIBMARIADB
+ return (dynamic_column_var_uint_bytes(value->x.string.charset->nr) +
+ value->x.string.value.length);
+#else
+ return (dynamic_column_var_uint_bytes(value->x.string.charset->number) +
+ value->x.string.value.length);
+#endif
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ {
+ int precision= value->x.decimal.value.intg + value->x.decimal.value.frac;
+ int scale= value->x.decimal.value.frac;
+
+ if (precision == 0 || decimal_is_zero(&value->x.decimal.value))
+ {
+ /* This is here to simplify dynamic_column_decimal_store() */
+ value->x.decimal.value.intg= value->x.decimal.value.frac= 0;
+ return 0;
+ }
+ /*
+ Check if legal decimal; This is needed to not get an assert in
+ decimal_bin_size(). However this should be impossible as all
+ decimals entered here should be valid and we have the special check
+ above to handle the unlikely but possible case that decimal.value.intg
+ and decimal.frac is 0.
+ */
+ if (scale < 0 || precision <= 0)
+ {
+ DBUG_ASSERT(0); /* Impossible */
+ return (size_t) ~0;
+ }
+ return (dynamic_column_var_uint_bytes(value->x.decimal.value.intg) +
+ dynamic_column_var_uint_bytes(value->x.decimal.value.frac) +
+ decimal_bin_size(precision, scale));
+ }
+#endif
+ case DYN_COL_DATETIME:
+ if (format == dyncol_fmt_num || value->x.time_value.second_part)
+ /* date+time in bits: 14 + 4 + 5 + 10 + 6 + 6 + 20 + 1 66bits ~= 9 bytes*/
+ return 9;
+ else
+ return 6;
+ case DYN_COL_DATE:
+ /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/
+ return 3;
+ case DYN_COL_TIME:
+ if (format == dyncol_fmt_num || value->x.time_value.second_part)
+ /* time in bits: 10 + 6 + 6 + 20 + 1 = 43bits ~= 6bytes*/
+ return 6;
+ else
+ return 3;
+ case DYN_COL_DYNCOL:
+ return value->x.string.value.length;
+ default:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+/**
+ Append double value to a string
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_double_store(DYNAMIC_COLUMN *str, double val)
+{
+ if (ma_dynstr_realloc(str, 8))
+ return ER_DYNCOL_RESOURCE;
+ float8store(str->str + str->length, val);
+ str->length+= 8;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read double value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_double_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ if (length != 8)
+ return ER_DYNCOL_FORMAT;
+ float8get(store_it_here->x.double_value, data);
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Append the string with given string value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_string_store(DYNAMIC_COLUMN *str, LEX_STRING *string,
+ MARIADB_CHARSET_INFO *charset)
+{
+ enum enum_dyncol_func_result rc;
+#ifdef LIBMARIADB
+ if ((rc= dynamic_column_var_uint_store(str, charset->nr)))
+#else
+ if ((rc= dynamic_column_var_uint_store(str, charset->number)))
+#endif
+ return rc;
+ if (ma_dynstr_append_mem(str, string->str, string->length))
+ return ER_DYNCOL_RESOURCE;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Append the string with given string value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_dyncol_store(DYNAMIC_COLUMN *str, LEX_STRING *string)
+{
+ if (ma_dynstr_append_mem(str, string->str, string->length))
+ return ER_DYNCOL_RESOURCE;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Read string value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_string_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ size_t len;
+ uint charset_nr= (uint)dynamic_column_var_uint_get(data, length, &len);
+ if (len == 0) /* Wrong packed number */
+ return ER_DYNCOL_FORMAT;
+#ifndef LIBMARIADB
+ store_it_here->x.string.charset= get_charset_by_nr(charset_nr);
+#else
+ store_it_here->x.string.charset= mariadb_get_charset_by_nr(charset_nr);
+#endif
+ if (store_it_here->x.string.charset == NULL)
+ return ER_DYNCOL_UNKNOWN_CHARSET;
+ data+= len;
+ store_it_here->x.string.value.length= (length-= len);
+ store_it_here->x.string.value.str= (char*) data;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Read Dynamic columns packet string value of given length
+ from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_dyncol_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ store_it_here->x.string.charset= ma_charset_bin;
+ store_it_here->x.string.value.length= length;
+ store_it_here->x.string.value.str= (char*) data;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Append the string with given decimal value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+#ifndef LIBMARIADB
+static enum enum_dyncol_func_result
+dynamic_column_decimal_store(DYNAMIC_COLUMN *str,
+ decimal_t *value)
+{
+ uint bin_size;
+ int precision= value->intg + value->frac;
+
+ /* Store decimal zero as empty string */
+ if (precision == 0)
+ return ER_DYNCOL_OK;
+
+ bin_size= decimal_bin_size(precision, value->frac);
+ if (ma_dynstr_realloc(str, bin_size + 20))
+ return ER_DYNCOL_RESOURCE;
+
+ /* The following can't fail as memory is already allocated */
+ (void) dynamic_column_var_uint_store(str, value->intg);
+ (void) dynamic_column_var_uint_store(str, value->frac);
+
+ decimal2bin(value, (uchar *) str->str + str->length,
+ precision, value->frac);
+ str->length+= bin_size;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Prepare the value to be used as decimal.
+
+ @param value The value structure which sould be setup.
+*/
+
+void mariadb_dyncol_prepare_decimal(DYNAMIC_COLUMN_VALUE *value)
+{
+ value->x.decimal.value.buf= value->x.decimal.buffer;
+ value->x.decimal.value.len= DECIMAL_BUFF_LENGTH;
+ /* just to be safe */
+ value->type= DYN_COL_DECIMAL;
+ decimal_make_zero(&value->x.decimal.value);
+}
+
+void dynamic_column_prepare_decimal(DYNAMIC_COLUMN_VALUE *value)
+{
+ mariadb_dyncol_prepare_decimal(value);
+}
+
+
+
+/**
+ Read decimal value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_decimal_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ size_t intg_len, frac_len;
+ int intg, frac, precision, scale;
+
+ dynamic_column_prepare_decimal(store_it_here);
+ /* Decimals 0.0 is stored as a zero length string */
+ if (length == 0)
+ return ER_DYNCOL_OK; /* value contains zero */
+
+ intg= (int)dynamic_column_var_uint_get(data, length, &intg_len);
+ data+= intg_len;
+ frac= (int)dynamic_column_var_uint_get(data, length - intg_len, &frac_len);
+ data+= frac_len;
+
+ /* Check the size of data is correct */
+ precision= intg + frac;
+ scale= frac;
+ if (scale < 0 || precision <= 0 || scale > precision ||
+ (length - intg_len - frac_len) >
+ (size_t) (DECIMAL_BUFF_LENGTH*sizeof(decimal_digit_t)) ||
+ decimal_bin_size(intg + frac, frac) !=
+ (int) (length - intg_len - frac_len))
+ return ER_DYNCOL_FORMAT;
+
+ if (bin2decimal(data, &store_it_here->x.decimal.value, precision, scale) !=
+ E_DEC_OK)
+ return ER_DYNCOL_FORMAT;
+ return ER_DYNCOL_OK;
+}
+#endif
+
+/**
+ Append the string with given datetime value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value,
+ enum enum_dyncol_format format)
+{
+ enum enum_dyncol_func_result rc;
+ /*
+ 0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds--->
+ 12345678901234123412345 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456><123456><123456><123456>
+ */
+ if ((rc= dynamic_column_date_store(str, value)) ||
+ (rc= dynamic_column_time_store(str, value, format)))
+ return rc;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read datetime value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_time_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
+ /*
+ 0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds--->
+ 12345678901234123412345 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456><123456><123456><123456>
+ */
+ if (length != 9 && length != 6)
+ goto err;
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATETIME;
+ if ((rc= dynamic_column_date_read_internal(store_it_here, data, 3)) ||
+ (rc= dynamic_column_time_read_internal(store_it_here, data + 3,
+ length - 3)))
+ goto err;
+ return ER_DYNCOL_OK;
+
+err:
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
+ return rc;
+}
+
+
+/**
+ Append the string with given time value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value,
+ enum enum_dyncol_format format)
+{
+ uchar *buf;
+ if (ma_dynstr_realloc(str, 6))
+ return ER_DYNCOL_RESOURCE;
+
+ buf= ((uchar *)str->str) + str->length;
+
+ if (value->time_type == MYSQL_TIMESTAMP_NONE ||
+ value->time_type == MYSQL_TIMESTAMP_ERROR ||
+ value->time_type == MYSQL_TIMESTAMP_DATE)
+ {
+ value->neg= 0;
+ value->second_part= 0;
+ value->hour= 0;
+ value->minute= 0;
+ value->second= 0;
+ }
+ DBUG_ASSERT(value->hour <= 838);
+ DBUG_ASSERT(value->minute <= 59);
+ DBUG_ASSERT(value->second <= 59);
+ DBUG_ASSERT(value->second_part <= 999999);
+ if (format == dyncol_fmt_num || value->second_part)
+ {
+ /*
+ 00000!<-hours--><min-><sec-><---microseconds--->
+ 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456>
+ */
+ buf[0]= (value->second_part & 0xff);
+ buf[1]= ((value->second_part & 0xff00) >> 8);
+ buf[2]= (uchar)(((value->second & 0xf) << 4) |
+ ((value->second_part & 0xf0000) >> 16));
+ buf[3]= ((value->minute << 2) | ((value->second & 0x30) >> 4));
+ buf[4]= (value->hour & 0xff);
+ buf[5]= ((value->neg ? 0x4 : 0) | (value->hour >> 8));
+ str->length+= 6;
+ }
+ else
+ {
+ /*
+ !<-hours--><min-><sec->
+ 11234567890123456123456
+ <123456><123456><123456>
+ */
+ buf[0]= (value->second) | ((value->minute & 0x3) << 6);
+ buf[1]= (value->minute >> 2) | ((value->hour & 0xf) << 4);
+ buf[2]= (value->hour >> 4) | (value->neg ? 0x80 : 0);
+ str->length+= 3;
+ }
+
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read time value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_time_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ store_it_here->x.time_value.year= store_it_here->x.time_value.month=
+ store_it_here->x.time_value.day= 0;
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_TIME;
+ return dynamic_column_time_read_internal(store_it_here, data, length);
+}
+
+/**
+ Internal function for reading time part from the string.
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ if (length != 6 && length != 3)
+ goto err;
+ if (length == 6)
+ {
+ /*
+ 00000!<-hours--><min-><sec-><---microseconds--->
+ 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456>
+ */
+ store_it_here->x.time_value.second_part= (data[0] |
+ (data[1] << 8) |
+ ((data[2] & 0xf) << 16));
+ store_it_here->x.time_value.second= ((data[2] >> 4) |
+ ((data[3] & 0x3) << 4));
+ store_it_here->x.time_value.minute= (data[3] >> 2);
+ store_it_here->x.time_value.hour= (((((uint)data[5]) & 0x3 ) << 8) | data[4]);
+ store_it_here->x.time_value.neg= ((data[5] & 0x4) ? 1 : 0);
+ }
+ else
+ {
+ /*
+ !<-hours--><min-><sec->
+ 11234567890123456123456
+ <123456><123456><123456>
+ */
+ store_it_here->x.time_value.second_part= 0;
+ store_it_here->x.time_value.second= (data[0] & 0x3f);
+ store_it_here->x.time_value.minute= (data[0] >> 6) | ((data[1] & 0xf) << 2);
+ store_it_here->x.time_value.hour= (data[1] >> 4) | ((data[2] & 0x3f) << 4);
+ store_it_here->x.time_value.neg= ((data[2] & 0x80) ? 1 : 0);
+ }
+ if (store_it_here->x.time_value.second > 59 ||
+ store_it_here->x.time_value.minute > 59 ||
+ store_it_here->x.time_value.hour > 838 ||
+ store_it_here->x.time_value.second_part > 999999)
+ goto err;
+ return ER_DYNCOL_OK;
+
+err:
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
+ return ER_DYNCOL_FORMAT;
+}
+
+
+/**
+ Append the string with given date value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value)
+{
+ uchar *buf;
+ if (ma_dynstr_realloc(str, 3))
+ return ER_DYNCOL_RESOURCE;
+
+ buf= ((uchar *)str->str) + str->length;
+ if (value->time_type == MYSQL_TIMESTAMP_NONE ||
+ value->time_type == MYSQL_TIMESTAMP_ERROR ||
+ value->time_type == MYSQL_TIMESTAMP_TIME)
+ value->year= value->month= value->day = 0;
+ DBUG_ASSERT(value->year <= 9999);
+ DBUG_ASSERT(value->month <= 12);
+ DBUG_ASSERT(value->day <= 31);
+ /*
+ 0<----year----><mn><day>
+ 012345678901234123412345
+ <123456><123456><123456>
+ */
+ buf[0]= (value->day |
+ ((value->month & 0x7) << 5));
+ buf[1]= ((value->month >> 3) | ((value->year & 0x7F) << 1));
+ buf[2]= (value->year >> 7);
+ str->length+= 3;
+ return ER_DYNCOL_OK;
+}
+
+
+
+/**
+ Read date value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ store_it_here->x.time_value.neg= 0;
+ store_it_here->x.time_value.second_part= 0;
+ store_it_here->x.time_value.hour= 0;
+ store_it_here->x.time_value.minute= 0;
+ store_it_here->x.time_value.second= 0;
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATE;
+ return dynamic_column_date_read_internal(store_it_here, data, length);
+}
+
+/**
+ Internal function for reading date part from the string.
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data,
+ size_t length)
+{
+ if (length != 3)
+ goto err;
+ /*
+ 0<----year----><mn><day>
+ 12345678901234123412345
+ <123456><123456><123456>
+ */
+ store_it_here->x.time_value.day= (data[0] & 0x1f);
+ store_it_here->x.time_value.month= (((data[1] & 0x1) << 3) |
+ (data[0] >> 5));
+ store_it_here->x.time_value.year= ((((uint)data[2]) << 7) |
+ (data[1] >> 1));
+ if (store_it_here->x.time_value.day > 31 ||
+ store_it_here->x.time_value.month > 12 ||
+ store_it_here->x.time_value.year > 9999)
+ goto err;
+ return ER_DYNCOL_OK;
+
+err:
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
+ return ER_DYNCOL_FORMAT;
+}
+
+
+/**
+ Append the string with given value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+data_store(DYNAMIC_COLUMN *str, DYNAMIC_COLUMN_VALUE *value,
+ enum enum_dyncol_format format)
+{
+ switch (value->type) {
+ case DYN_COL_INT:
+ return dynamic_column_sint_store(str, value->x.long_value);
+ case DYN_COL_UINT:
+ return dynamic_column_uint_store(str, value->x.ulong_value);
+ case DYN_COL_DOUBLE:
+ return dynamic_column_double_store(str, value->x.double_value);
+ case DYN_COL_STRING:
+ return dynamic_column_string_store(str, &value->x.string.value,
+ value->x.string.charset);
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ return dynamic_column_decimal_store(str, &value->x.decimal.value);
+#endif
+ case DYN_COL_DATETIME:
+ /* date+time in bits: 14 + 4 + 5 + 5 + 6 + 6 40bits = 5 bytes */
+ return dynamic_column_date_time_store(str, &value->x.time_value, format);
+ case DYN_COL_DATE:
+ /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/
+ return dynamic_column_date_store(str, &value->x.time_value);
+ case DYN_COL_TIME:
+ /* time in bits: 5 + 6 + 6 = 17bits ~= 3bytes*/
+ return dynamic_column_time_store(str, &value->x.time_value, format);
+ case DYN_COL_DYNCOL:
+ return dynamic_column_dyncol_store(str, &value->x.string.value);
+ case DYN_COL_NULL:
+ break; /* Impossible */
+ default:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return ER_DYNCOL_OK; /* Impossible */
+}
+
+
+/**
+ Write information to the fixed header
+
+ @param str String where to write the header
+ @param offset_size Size of offset field in bytes
+ @param column_count Number of columns
+*/
+
+static void set_fixed_header(DYNAMIC_COLUMN *str,
+ uint offset_size,
+ uint column_count)
+{
+ DBUG_ASSERT(column_count <= 0xffff);
+ DBUG_ASSERT(offset_size <= MAX_OFFSET_LENGTH);
+ str->str[0]= ((str->str[0] & ~DYNCOL_FLG_OFFSET) |
+ (offset_size - 1)); /* size of offset */
+ int2store(str->str + 1, column_count); /* columns number */
+ DBUG_ASSERT((str->str[0] & (~DYNCOL_FLG_KNOWN)) == 0);
+}
+
+/**
+ Adds columns into the empty string
+
+ @param str String where to write the data (the record)
+ @param hdr Dynamic columns record descriptor
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys (uint or LEX_STRING)
+ @param values Array of columns values
+ @param new_str True if we need to allocate new string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_new_column_store(DYNAMIC_COLUMN *str,
+ DYN_HEADER *hdr,
+ uint column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_str)
+{
+ struct st_service_funcs *fmt= fmt_data + hdr->format;
+ void **columns_order;
+ uchar *element;
+ uint i;
+ enum enum_dyncol_func_result rc= ER_DYNCOL_RESOURCE;
+ size_t all_headers_size;
+
+ if (!(columns_order= malloc(sizeof(void*)*column_count)))
+ return ER_DYNCOL_RESOURCE;
+ if (new_str || str->str == 0)
+ {
+ if (column_count)
+ {
+ if (dynamic_column_init_named(str,
+ fmt->fixed_hdr +
+ hdr->header_size +
+ hdr->nmpool_size +
+ hdr->data_size +
+ DYNCOL_SYZERESERVE))
+ goto err;
+ }
+ else
+ {
+ dynamic_column_initialize(str);
+ }
+ }
+ else
+ {
+ str->length= 0;
+ if (ma_dynstr_realloc(str,
+ fmt->fixed_hdr +
+ hdr->header_size +
+ hdr->nmpool_size +
+ hdr->data_size +
+ DYNCOL_SYZERESERVE))
+ goto err;
+ }
+ if (!column_count)
+ return ER_DYNCOL_OK;
+
+ memset(str->str, 0, fmt->fixed_hdr);
+ str->length= fmt->fixed_hdr;
+
+ /* sort columns for the header */
+ for (i= 0, element= (uchar *) column_keys;
+ i < column_count;
+ i++, element+= fmt->key_size_in_array)
+ columns_order[i]= (void *)element;
+ qsort(columns_order, (size_t)column_count, sizeof(void*), fmt->column_sort);
+
+ /*
+ For now we don't allow creating two columns with the same number
+ at the time of create. This can be fixed later to just use the later
+ by comparing the pointers.
+ */
+ for (i= 0; i < column_count - 1; i++)
+ {
+ if ((*fmt->check_limit)(&columns_order[i]) ||
+ (*fmt->column_sort)(&columns_order[i], &columns_order[i + 1]) == 0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto err;
+ }
+ }
+ if ((*fmt->check_limit)(&columns_order[i]))
+ {
+ rc= ER_DYNCOL_DATA;
+ goto err;
+ }
+
+ (*fmt->set_fixed_hdr)(str, hdr);
+ /* reserve place for header and name pool */
+ str->length+= hdr->header_size + hdr->nmpool_size;
+
+ hdr->entry= hdr->header;
+ hdr->name= hdr->nmpool;
+ all_headers_size= fmt->fixed_hdr + hdr->header_size + hdr->nmpool_size;
+ for (i= 0; i < column_count; i++)
+ {
+ uint ord= (uint)(((uchar*)columns_order[i] - (uchar*)column_keys) /
+ fmt->key_size_in_array);
+ if (values[ord].type != DYN_COL_NULL)
+ {
+ /* Store header first in the str */
+ if ((*fmt->put_header_entry)(hdr, columns_order[i], values + ord,
+ str->length - all_headers_size))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+
+ /* Store value in 'str + str->length' and increase str->length */
+ if ((rc= data_store(str, values + ord, hdr->format)))
+ goto err;
+ }
+ }
+ rc= ER_DYNCOL_OK;
+err:
+ free(columns_order);
+ return rc;
+}
+
+/**
+ Calculate size of header, name pool and data pool
+
+ @param hdr descriptor of dynamic column record
+ @param column_count number of elements in arrays
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys (uint or LEX_STRING)
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+calc_var_sizes(DYN_HEADER *hdr,
+ uint column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ struct st_service_funcs *fmt= fmt_data + hdr->format;
+ uint i;
+ hdr->nmpool_size= hdr->data_size= 0;
+ hdr->column_count= 0;
+ for (i= 0; i < column_count; i++)
+ {
+ if (values[i].type != DYN_COL_NULL)
+ {
+ size_t tmp;
+ hdr->column_count++;
+ hdr->data_size+= (tmp= dynamic_column_value_len(values + i,
+ hdr->format));
+ if (tmp == (size_t) ~0)
+ return ER_DYNCOL_DATA;
+ hdr->nmpool_size+= (*fmt->name_size)(column_keys, i);
+ }
+ }
+ /*
+ We can handle data up to 0x1fffffff (old format) and
+ 0xfffffffff (new format) bytes now.
+ */
+ if ((hdr->offset_size= fmt->dynamic_column_offset_bytes(hdr->data_size)) >=
+ fmt->max_offset_size)
+ return ER_DYNCOL_LIMIT;
+
+ /* header entry is column number or string pointer + offset & type */
+ hdr->entry_size= fmt->fixed_hdr_entry + hdr->offset_size;
+ hdr->header_size= hdr->column_count * hdr->entry_size;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Create packed string which contains given columns (internal multi format)
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys (format dependent)
+ @param values Array of columns values
+ @param new_str True if we need allocate new string
+ @param string_keys keys are strings
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_create_many_internal_fmt(DYNAMIC_COLUMN *str,
+ uint column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_str,
+ my_bool string_keys)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc;
+ memset(&header, 0, sizeof(header));
+ header.format= (string_keys ? 1 : 0);
+
+ if (new_str)
+ {
+ /* to make dynstr_free() working in case of errors */
+ memset(str, 0, sizeof(DYNAMIC_COLUMN));
+ }
+
+ if ((rc= calc_var_sizes(&header, column_count, column_keys, values)) < 0)
+ return rc;
+
+ return dynamic_new_column_store(str, &header,
+ column_count,
+ column_keys, values,
+ new_str);
+}
+
+
+/**
+ Create packed string which contains given columns
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_numbers Array of columns numbers
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+dynamic_column_create_many(DYNAMIC_COLUMN *str,
+ uint column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return(dynamic_column_create_many_internal_fmt(str, column_count,
+ column_numbers, values,
+ TRUE, FALSE));
+}
+
+/**
+ Create packed string which contains given columns
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_numbers Array of columns numbers
+ @param values Array of columns values
+ @param new_string True if we need allocate new string
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_create_many_num(DYNAMIC_COLUMN *str,
+ uint column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_string)
+{
+ return(dynamic_column_create_many_internal_fmt(str, column_count,
+ column_numbers, values,
+ new_string, FALSE));
+}
+
+/**
+ Create packed string which contains given columns
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys
+ @param values Array of columns value
+ @param new_string True if we need allocate new string
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_create_many_named(DYNAMIC_COLUMN *str,
+ uint column_count,
+ LEX_STRING *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_string)
+{
+ return(dynamic_column_create_many_internal_fmt(str, column_count,
+ column_keys, values,
+ new_string, TRUE));
+}
+
+/**
+ Create packed string which contains given column
+
+ @param str String where to write the data
+ @param column_number Column number
+ @param value The columns value
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+dynamic_column_create(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *value)
+{
+ return(dynamic_column_create_many(str, 1, &column_nr, value));
+}
+
+
+/**
+ Calculate length of data between given two header entries
+
+ @param entry Pointer to the first entry
+ @param entry_next Pointer to the last entry
+ @param header_end Pointer to the header end
+ @param offset_size Size of offset field in bytes
+ @param last_offset Size of the data segment
+
+ @return number of bytes
+*/
+
+static size_t get_length_interval(uchar *entry, uchar *entry_next,
+ uchar *header_end, size_t offset_size,
+ size_t last_offset)
+{
+ size_t offset, offset_next;
+ DYNAMIC_COLUMN_TYPE type, type_next;
+ DBUG_ASSERT(entry < entry_next);
+
+ if (type_and_offset_read_num(&type, &offset, entry + COLUMN_NUMBER_SIZE,
+ offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ if (entry_next >= header_end)
+ return (last_offset - offset);
+ if (type_and_offset_read_num(&type_next, &offset_next,
+ entry_next + COLUMN_NUMBER_SIZE, offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ return (offset_next - offset);
+}
+
+
+/**
+ Calculate length of data between given hdr->entry and next_entry
+
+ @param hdr descriptor of dynamic column record
+ @param next_entry next header entry (can point just after last header
+ entry)
+
+ @return number of bytes
+*/
+
+static size_t hdr_interval_length(DYN_HEADER *hdr, uchar *next_entry)
+{
+ struct st_service_funcs *fmt= fmt_data + hdr->format;
+ size_t next_entry_offset;
+ DYNAMIC_COLUMN_TYPE next_entry_type;
+ DBUG_ASSERT(hdr->entry < next_entry);
+ DBUG_ASSERT(hdr->entry >= hdr->header);
+ DBUG_ASSERT(next_entry <= hdr->header + hdr->header_size);
+
+ if ((*fmt->type_and_offset_read)(&hdr->type, &hdr->offset,
+ hdr->entry + fmt->fixed_hdr_entry,
+ hdr->offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ if (next_entry == hdr->header + hdr->header_size)
+ return hdr->data_size - hdr->offset;
+ if ((*fmt->type_and_offset_read)(&next_entry_type, &next_entry_offset,
+ next_entry + fmt->fixed_hdr_entry,
+ hdr->offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ return (next_entry_offset - hdr->offset);
+}
+
+
+/**
+ Comparator function for references to header entries for qsort
+*/
+
+static int header_compar_num(const void *a, const void *b)
+{
+ uint va= uint2korr((uchar*)a), vb= uint2korr((uchar*)b);
+ return (va > vb ? 1 : (va < vb ? -1 : 0));
+}
+
+
+/**
+ Find entry in the numeric format header by the column number
+
+ @param hdr descriptor of dynamic column record
+ @param key number to find
+
+ @return pointer to the entry or NULL
+*/
+
+static uchar *find_entry_num(DYN_HEADER *hdr, uint key)
+{
+ uchar header_entry[2+4];
+ DBUG_ASSERT(hdr->format == dyncol_fmt_num);
+ int2store(header_entry, key);
+ return hdr->entry= bsearch(header_entry, hdr->header,
+ (size_t)hdr->column_count,
+ hdr->entry_size, &header_compar_num);
+}
+
+
+/**
+ Read name from header entry
+
+ @param hdr descriptor of dynamic column record
+ @param entry pointer to the header entry
+ @param name where to put name
+
+ @return 0 ok
+ @return 1 error in data
+*/
+
+static my_bool read_name(DYN_HEADER *hdr, uchar *entry, LEX_STRING *name)
+{
+ size_t nmoffset= uint2korr(entry);
+ uchar *next_entry= entry + hdr->entry_size;
+
+ if (nmoffset > hdr->nmpool_size)
+ return 1;
+
+ name->str= (char *)hdr->nmpool + nmoffset;
+ if (next_entry == hdr->header + hdr->header_size)
+ name->length= hdr->nmpool_size - nmoffset;
+ else
+ {
+ size_t next_nmoffset= uint2korr(next_entry);
+ if (next_nmoffset > hdr->nmpool_size)
+ return 1;
+ name->length= next_nmoffset - nmoffset;
+ }
+ return 0;
+}
+
+
+/**
+ Find entry in the names format header by the column number
+
+ @param hdr descriptor of dynamic column record
+ @param key name to find
+
+ @return pointer to the entry or NULL
+*/
+static uchar *find_entry_named(DYN_HEADER *hdr, LEX_STRING *key)
+{
+ uchar *min= hdr->header;
+ uchar *max= hdr->header + (hdr->column_count - 1) * hdr->entry_size;
+ uchar *mid;
+ DBUG_ASSERT(hdr->format == dyncol_fmt_str);
+ DBUG_ASSERT(hdr->nmpool != NULL);
+ while (max >= min)
+ {
+ LEX_STRING name;
+ int cmp;
+ mid= hdr->header + ((min - hdr->header) +
+ (max - hdr->header)) /
+ 2 /
+ hdr->entry_size * hdr->entry_size;
+ if (read_name(hdr, mid, &name))
+ return NULL;
+ cmp= mariadb_dyncol_column_cmp_named(&name, key);
+ if (cmp < 0)
+ min= mid + hdr->entry_size;
+ else if (cmp > 0)
+ max= mid - hdr->entry_size;
+ else
+ return mid;
+ }
+ return NULL;
+}
+
+
+/**
+ Write number in the buffer (backward direction - starts from the buffer end)
+
+ @return pointer on the number begining
+*/
+
+static char *backwritenum(char *chr, uint numkey)
+{
+ if (numkey == 0)
+ *(--chr)= '0';
+ else
+ while (numkey > 0)
+ {
+ *(--chr)= '0' + numkey % 10;
+ numkey/= 10;
+ }
+ return chr;
+}
+
+
+/**
+ Find column and fill information about it
+
+ @param hdr descriptor of dynamic column record
+ @param numkey Number of the column to fetch (if strkey is NULL)
+ @param strkey Name of the column to fetch (or NULL)
+
+ @return 0 ok
+ @return 1 error in data
+*/
+
+static my_bool
+find_column(DYN_HEADER *hdr, uint numkey, LEX_STRING *strkey)
+{
+ LEX_STRING nmkey;
+ char nmkeybuff[DYNCOL_NUM_CHAR]; /* to fit max 2 bytes number */
+ DBUG_ASSERT(hdr->header != NULL);
+
+ if (hdr->header + hdr->header_size > hdr->data_end)
+ return TRUE;
+
+ /* fix key */
+ if (hdr->format == dyncol_fmt_num && strkey != NULL)
+ {
+ char *end;
+ numkey= (uint) strtoul(strkey->str, &end, 10);
+ if (end != strkey->str + strkey->length)
+ {
+ /* we can't find non-numeric key among numeric ones */
+ hdr->type= DYN_COL_NULL;
+ return 0;
+ }
+ }
+ else if (hdr->format == dyncol_fmt_str && strkey == NULL)
+ {
+ nmkey.str= backwritenum(nmkeybuff + sizeof(nmkeybuff), numkey);
+ nmkey.length= (nmkeybuff + sizeof(nmkeybuff)) - nmkey.str;
+ strkey= &nmkey;
+ }
+ if (hdr->format == dyncol_fmt_num)
+ hdr->entry= find_entry_num(hdr, numkey);
+ else
+ hdr->entry= find_entry_named(hdr, strkey);
+
+ if (!hdr->entry)
+ {
+ /* Column not found */
+ hdr->type= DYN_COL_NULL;
+ return 0;
+ }
+ hdr->length= hdr_interval_length(hdr, hdr->entry + hdr->entry_size);
+ hdr->data= hdr->dtpool + hdr->offset;
+ /*
+ Check that the found data is withing the ranges. This can happen if
+ we get data with wrong offsets.
+ */
+ if (hdr->length == DYNCOL_OFFSET_ERROR ||
+ hdr->length > INT_MAX || hdr->offset > hdr->data_size)
+ return 1;
+
+ return 0;
+}
+
+
+/**
+ Read and check the header of the dynamic string
+
+ @param hdr descriptor of dynamic column record
+ @param str Dynamic string
+
+ @retval FALSE OK
+ @retval TRUE error
+
+ Note
+ We don't check for str->length == 0 as all code that calls this
+ already have handled this case.
+*/
+
+static inline my_bool read_fixed_header(DYN_HEADER *hdr,
+ DYNAMIC_COLUMN *str)
+{
+ DBUG_ASSERT(str != NULL && str->length != 0);
+ if ((str->length < 1) ||
+ (str->str[0] & (~DYNCOL_FLG_KNOWN)))
+ return 1;
+ hdr->format= ((str->str[0] & DYNCOL_FLG_NAMES) ?
+ dyncol_fmt_str:
+ dyncol_fmt_num);
+ if ((str->length < fmt_data[hdr->format].fixed_hdr))
+ return 1; /* Wrong header */
+ hdr->offset_size= (str->str[0] & DYNCOL_FLG_OFFSET) + 1 +
+ (hdr->format == dyncol_fmt_str ? 1 : 0);
+ hdr->column_count= uint2korr(str->str + 1);
+ if (hdr->format == dyncol_fmt_str)
+ hdr->nmpool_size= uint2korr(str->str + 3); // only 2 bytes supported for now
+ else
+ hdr->nmpool_size= 0;
+ return 0;
+}
+
+
+/**
+ Get dynamic column value by column number
+
+ @param str The packed string to extract the column
+ @param column_nr Number of column to fetch
+ @param store_it_here Where to store the extracted value
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+dynamic_column_get(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ return dynamic_column_get_internal(str, store_it_here, column_nr, NULL);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_get_num(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ return dynamic_column_get_internal(str, store_it_here, column_nr, NULL);
+}
+
+
+/**
+ Get dynamic column value by name
+
+ @param str The packed string to extract the column
+ @param name Name of column to fetch
+ @param store_it_here Where to store the extracted value
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_get_named(DYNAMIC_COLUMN *str, LEX_STRING *name,
+ DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ DBUG_ASSERT(name != NULL);
+ return dynamic_column_get_internal(str, store_it_here, 0, name);
+}
+
+
+static enum enum_dyncol_func_result
+dynamic_column_get_value(DYN_HEADER *hdr, DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ static enum enum_dyncol_func_result rc;
+ switch ((store_it_here->type= hdr->type)) {
+ case DYN_COL_INT:
+ rc= dynamic_column_sint_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_UINT:
+ rc= dynamic_column_uint_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_DOUBLE:
+ rc= dynamic_column_double_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_STRING:
+ rc= dynamic_column_string_read(store_it_here, hdr->data, hdr->length);
+ break;
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ rc= dynamic_column_decimal_read(store_it_here, hdr->data, hdr->length);
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ rc= dynamic_column_date_time_read(store_it_here, hdr->data,
+ hdr->length);
+ break;
+ case DYN_COL_DATE:
+ rc= dynamic_column_date_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_TIME:
+ rc= dynamic_column_time_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_NULL:
+ rc= ER_DYNCOL_OK;
+ break;
+ case DYN_COL_DYNCOL:
+ rc= dynamic_column_dyncol_read(store_it_here, hdr->data, hdr->length);
+ break;
+ default:
+ rc= ER_DYNCOL_FORMAT;
+ store_it_here->type= DYN_COL_NULL;
+ break;
+ }
+ return rc;
+}
+
+/**
+ Get dynamic column value by number or name
+
+ @param str The packed string to extract the column
+ @param store_it_here Where to store the extracted value
+ @param numkey Number of the column to fetch (if strkey is NULL)
+ @param strkey Name of the column to fetch (or NULL)
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_get_internal(DYNAMIC_COLUMN *str,
+ DYNAMIC_COLUMN_VALUE *store_it_here,
+ uint num_key, LEX_STRING *str_key)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
+ memset(&header, 0, sizeof(header));
+
+ if (str->length == 0)
+ goto null;
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ goto err;
+
+ if (header.column_count == 0)
+ goto null;
+
+ if (find_column(&header, num_key, str_key))
+ goto err;
+
+ rc= dynamic_column_get_value(&header, store_it_here);
+ return rc;
+
+null:
+ rc= ER_DYNCOL_OK;
+err:
+ store_it_here->type= DYN_COL_NULL;
+ return rc;
+}
+
+
+/**
+ Check existence of the column in the packed string (by number)
+
+ @param str The packed string to check the column
+ @param column_nr Number of column to check
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_exists_num(DYNAMIC_COLUMN *str, uint column_nr)
+{
+ return dynamic_column_exists_internal(str, column_nr, NULL);
+}
+
+/**
+ Check existence of the column in the packed string (by name)
+
+ @param str The packed string to check the column
+ @param name Name of column to check
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_exists_named(DYNAMIC_COLUMN *str, LEX_STRING *name)
+{
+ DBUG_ASSERT(name != NULL);
+ return dynamic_column_exists_internal(str, 0, name);
+}
+
+
+/**
+ Check existence of the column in the packed string (by name of number)
+
+ @param str The packed string to check the column
+ @param num_key Number of the column to fetch (if strkey is NULL)
+ @param str_key Name of the column to fetch (or NULL)
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key,
+ LEX_STRING *str_key)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc;
+ memset(&header, 0, sizeof(header));
+
+ if (str->length == 0)
+ return ER_DYNCOL_NO; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ if (header.column_count == 0)
+ return ER_DYNCOL_NO; /* no columns */
+
+ if (find_column(&header, num_key, str_key))
+ return ER_DYNCOL_FORMAT;
+
+ return (header.type != DYN_COL_NULL ? ER_DYNCOL_YES : ER_DYNCOL_NO);
+}
+
+
+/**
+ List not-null columns in the packed string (only numeric format)
+
+ @param str The packed string
+ @param array_of_uint Where to put reference on created array
+
+ @return ER_DYNCOL_* return code
+*/
+enum enum_dyncol_func_result
+dynamic_column_list(DYNAMIC_COLUMN *str, DYNAMIC_ARRAY *array_of_uint)
+{
+ DYN_HEADER header;
+ uchar *read;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ memset(array_of_uint, 0, sizeof(*array_of_uint)); /* In case of errors */
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ if (header.format != dyncol_fmt_num)
+ return ER_DYNCOL_FORMAT;
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ if (ma_init_dynamic_array(array_of_uint, sizeof(uint), header.column_count, 0))
+ return ER_DYNCOL_RESOURCE;
+
+ for (i= 0, read= header.header;
+ i < header.column_count;
+ i++, read+= header.entry_size)
+ {
+ uint nm= uint2korr(read);
+ /* Insert can't never fail as it's pre-allocated above */
+ (void) ma_insert_dynamic(array_of_uint, (uchar *)&nm);
+ }
+ return ER_DYNCOL_OK;
+}
+
+/**
+ List not-null columns in the packed string (only numeric format)
+
+ @param str The packed string
+ @param array_of_uint Where to put reference on created array
+
+ @return ER_DYNCOL_* return code
+*/
+enum enum_dyncol_func_result
+mariadb_dyncol_list_num(DYNAMIC_COLUMN *str, uint *count, uint **nums)
+{
+ DYN_HEADER header;
+ uchar *read;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ (*nums)= 0; (*count)= 0; /* In case of errors */
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ if (header.format != dyncol_fmt_num)
+ return ER_DYNCOL_FORMAT;
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ if (!((*nums)= (uint *)malloc(sizeof(uint) * header.column_count)))
+ return ER_DYNCOL_RESOURCE;
+
+ for (i= 0, read= header.header;
+ i < header.column_count;
+ i++, read+= header.entry_size)
+ {
+ (*nums)[i]= uint2korr(read);
+ }
+ (*count)= header.column_count;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ List not-null columns in the packed string (any format)
+
+ @param str The packed string
+ @param count Number of names in the list
+ @param names Where to put names list (should be freed)
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_list_named(DYNAMIC_COLUMN *str, uint *count, LEX_STRING **names)
+{
+ DYN_HEADER header;
+ uchar *read;
+ char *pool;
+ struct st_service_funcs *fmt;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ (*names)= 0; (*count)= 0;
+
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ fmt= fmt_data + header.format;
+
+ if (header.entry_size * header.column_count + fmt->fixed_hdr >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ if (header.format == dyncol_fmt_num)
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count +
+ DYNCOL_NUM_CHAR * header.column_count);
+ else
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count +
+ header.nmpool_size + header.column_count);
+ if (!(*names))
+ return ER_DYNCOL_RESOURCE;
+ pool= ((char *)(*names)) + sizeof(LEX_STRING) * header.column_count;
+
+ for (i= 0, read= header.header;
+ i < header.column_count;
+ i++, read+= header.entry_size)
+ {
+ if (header.format == dyncol_fmt_num)
+ {
+ uint nm= uint2korr(read);
+ (*names)[i].str= pool;
+ pool+= DYNCOL_NUM_CHAR;
+ (*names)[i].length=
+ ma_ll2str(nm, (*names)[i].str, 10) - (*names)[i].str;
+ }
+ else
+ {
+ LEX_STRING tmp;
+ if (read_name(&header, read, &tmp))
+ return ER_DYNCOL_FORMAT;
+ (*names)[i].length= tmp.length;
+ (*names)[i].str= pool;
+ pool+= tmp.length + 1;
+ memcpy((*names)[i].str, (const void *)tmp.str, tmp.length);
+ (*names)[i].str[tmp.length]= '\0'; // just for safety
+ }
+ }
+ (*count)= header.column_count;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Find the place of the column in the header or place where it should be put
+
+ @param hdr descriptor of dynamic column record
+ @param key Name or number of column to fetch
+ (depends on string_key)
+ @param string_key True if we gave pointer to LEX_STRING.
+
+ @retval TRUE found
+ @retval FALSE pointer set to the next row
+*/
+
+static my_bool
+find_place(DYN_HEADER *hdr, void *key, my_bool string_keys)
+{
+ uint mid, start, end, val;
+ int UNINIT_VAR(flag);
+ LEX_STRING str;
+ char buff[DYNCOL_NUM_CHAR];
+ my_bool need_conversion= ((string_keys ? dyncol_fmt_str : dyncol_fmt_num) !=
+ hdr->format);
+ /* new format can't be numeric if the old one is names */
+ DBUG_ASSERT(string_keys ||
+ hdr->format == dyncol_fmt_num);
+
+ start= 0;
+ end= hdr->column_count -1;
+ mid= 1;
+ while (start != end)
+ {
+ uint val;
+ mid= (start + end) / 2;
+ hdr->entry= hdr->header + mid * hdr->entry_size;
+ if (!string_keys)
+ {
+ val= uint2korr(hdr->entry);
+ flag= CMP_NUM(*((uint *)key), val);
+ }
+ else
+ {
+ if (need_conversion)
+ {
+ str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry));
+ str.length= (buff + sizeof(buff)) - str.str;
+ }
+ else
+ {
+ DBUG_ASSERT(hdr->format == dyncol_fmt_str);
+ if (read_name(hdr, hdr->entry, &str))
+ return 0;
+ }
+ flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str);
+ }
+ if (flag <= 0)
+ end= mid;
+ else
+ start= mid + 1;
+ }
+ hdr->entry= hdr->header + start * hdr->entry_size;
+ if (start != mid)
+ {
+ if (!string_keys)
+ {
+ val= uint2korr(hdr->entry);
+ flag= CMP_NUM(*((uint *)key), val);
+ }
+ else
+ {
+ if (need_conversion)
+ {
+ str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry));
+ str.length= (buff + sizeof(buff)) - str.str;
+ }
+ else
+ {
+ DBUG_ASSERT(hdr->format == dyncol_fmt_str);
+ if (read_name(hdr, hdr->entry, &str))
+ return 0;
+ }
+ flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str);
+ }
+ }
+ if (flag > 0)
+ hdr->entry+= hdr->entry_size; /* Point at next bigger key */
+ return flag == 0;
+}
+
+
+/*
+ It is internal structure which describes a plan of changing the record
+ of dynamic columns
+*/
+
+typedef enum {PLAN_REPLACE, PLAN_ADD, PLAN_DELETE, PLAN_NOP} PLAN_ACT;
+
+struct st_plan {
+ DYNAMIC_COLUMN_VALUE *val;
+ void *key;
+ uchar *place;
+ size_t length;
+ long long hdelta, ddelta, ndelta;
+ long long mv_offset, mv_length;
+ uint mv_end;
+ PLAN_ACT act;
+};
+typedef struct st_plan PLAN;
+
+
+/**
+ Sort function for plan by column number
+*/
+
+static int plan_sort_num(const void *a, const void *b)
+{
+ return *((uint *)((PLAN *)a)->key) - *((uint *)((PLAN *)b)->key);
+}
+
+
+/**
+ Sort function for plan by column name
+*/
+
+static int plan_sort_named(const void *a, const void *b)
+{
+ return mariadb_dyncol_column_cmp_named((LEX_STRING *)((PLAN *)a)->key,
+ (LEX_STRING *)((PLAN *)b)->key);
+}
+
+#define DELTA_CHECK(S, D, C) \
+ if ((S) == 0) \
+ (S)= (D); \
+ else if (((S) > 0 && (D) < 0) || \
+ ((S) < 0 && (D) > 0)) \
+ { \
+ (C)= TRUE; \
+ }
+
+/**
+ Update dynamic column by copying in a new record (string).
+
+ @param str Dynamic column record to change
+ @param plan Plan of changing the record
+ @param add_column_count number of records in the plan array.
+ @param hdr descriptor of old dynamic column record
+ @param new_hdr descriptor of new dynamic column record
+ @param convert need conversion from numeric to names format
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_update_copy(DYNAMIC_COLUMN *str, PLAN *plan,
+ uint add_column_count,
+ DYN_HEADER *hdr, DYN_HEADER *new_hdr,
+ my_bool convert)
+{
+ DYNAMIC_COLUMN tmp;
+ struct st_service_funcs *fmt= fmt_data + hdr->format,
+ *new_fmt= fmt_data + new_hdr->format;
+ uint i, j, k;
+ size_t all_headers_size;
+
+ if (dynamic_column_init_named(&tmp,
+ (new_fmt->fixed_hdr + new_hdr->header_size +
+ new_hdr->nmpool_size +
+ new_hdr->data_size + DYNCOL_SYZERESERVE)))
+ {
+ return ER_DYNCOL_RESOURCE;
+ }
+ memset(tmp.str, 0, new_fmt->fixed_hdr);
+ (*new_fmt->set_fixed_hdr)(&tmp, new_hdr);
+ /* Adjust tmp to contain whole the future header */
+ tmp.length= new_fmt->fixed_hdr + new_hdr->header_size + new_hdr->nmpool_size;
+
+
+ /*
+ Copy data to the new string
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ new_hdr->entry= new_hdr->header;
+ new_hdr->name= new_hdr->nmpool;
+ all_headers_size= new_fmt->fixed_hdr +
+ new_hdr->header_size + new_hdr->nmpool_size;
+ for (i= 0, j= 0; i < add_column_count || j < hdr->column_count; i++)
+ {
+ size_t UNINIT_VAR(first_offset);
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ if (i == add_column_count)
+ j= end= hdr->column_count;
+ else
+ {
+ /*
+ old data portion. We don't need to check that j < column_count
+ as plan[i].place is guaranteed to have a pointer inside the
+ data.
+ */
+ while (hdr->header + j * hdr->entry_size < plan[i].place)
+ j++;
+ end= j;
+ if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++; /* data at 'j' will be removed */
+ }
+
+ /*
+ Adjust all headers since last loop.
+ We have to do this as the offset for data has moved
+ */
+ for (k= start; k < end; k++)
+ {
+ uchar *read= hdr->header + k * hdr->entry_size;
+ void *key;
+ LEX_STRING name;
+ size_t offs;
+ uint nm;
+ DYNAMIC_COLUMN_TYPE tp;
+ char buff[DYNCOL_NUM_CHAR];
+
+ if (hdr->format == dyncol_fmt_num)
+ {
+ if (convert)
+ {
+ name.str= backwritenum(buff + sizeof(buff), uint2korr(read));
+ name.length= (buff + sizeof(buff)) - name.str;
+ key= &name;
+ }
+ else
+ {
+ nm= uint2korr(read); /* Column nummber */
+ key= &nm;
+ }
+ }
+ else
+ {
+ if (read_name(hdr, read, &name))
+ goto err;
+ key= &name;
+ }
+ if (fmt->type_and_offset_read(&tp, &offs,
+ read + fmt->fixed_hdr_entry,
+ hdr->offset_size))
+ goto err;
+ if (k == start)
+ first_offset= offs;
+ else if (offs < first_offset)
+ goto err;
+
+ offs+= (size_t)plan[i].ddelta;
+ {
+ DYNAMIC_COLUMN_VALUE val;
+ val.type= tp; // only the type used in the header
+ if ((*new_fmt->put_header_entry)(new_hdr, key, &val, offs))
+ goto err;
+ }
+ }
+
+ /* copy first the data that was not replaced in original packed data */
+ if (start < end)
+ {
+ size_t data_size;
+ /* Add old data last in 'tmp' */
+ hdr->entry= hdr->header + start * hdr->entry_size;
+ data_size=
+ hdr_interval_length(hdr, hdr->header + end * hdr->entry_size);
+ if (data_size == DYNCOL_OFFSET_ERROR ||
+ (long) data_size < 0 ||
+ data_size > hdr->data_size - first_offset)
+ goto err;
+
+ memcpy(tmp.str + tmp.length, (char *)hdr->dtpool + first_offset,
+ data_size);
+ tmp.length+= data_size;
+ }
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ if ((*new_fmt->put_header_entry)(new_hdr, plan[i].key,
+ plan[i].val,
+ tmp.length - all_headers_size))
+ goto err;
+ data_store(&tmp, plan[i].val, new_hdr->format); /* Append new data */
+ }
+ }
+ }
+ dynamic_column_column_free(str);
+ *str= tmp;
+ return ER_DYNCOL_OK;
+err:
+ dynamic_column_column_free(&tmp);
+ return ER_DYNCOL_FORMAT;
+}
+
+static enum enum_dyncol_func_result
+dynamic_column_update_move_left(DYNAMIC_COLUMN *str, PLAN *plan,
+ size_t offset_size,
+ size_t entry_size,
+ size_t header_size,
+ size_t new_offset_size,
+ size_t new_entry_size,
+ size_t new_header_size,
+ uint column_count,
+ uint new_column_count,
+ uint add_column_count,
+ uchar *header_end,
+ size_t max_offset)
+{
+ uchar *write;
+ uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE;
+ uint i, j, k;
+ size_t curr_offset;
+
+ write= (uchar *)str->str + FIXED_HEADER_SIZE;
+ set_fixed_header(str, (uint)new_offset_size, new_column_count);
+
+ /*
+ Move headers first.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ for (curr_offset= 0, i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ size_t UNINIT_VAR(first_offset);
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ if (i == add_column_count)
+ j= end= column_count;
+ else
+ {
+ /*
+ old data portion. We don't need to check that j < column_count
+ as plan[i].place is guaranteed to have a pointer inside the
+ data.
+ */
+ while (header_base + j * entry_size < plan[i].place)
+ j++;
+ end= j;
+ if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++; /* data at 'j' will be removed */
+ }
+ plan[i].mv_end= end;
+
+ {
+ DYNAMIC_COLUMN_TYPE tp;
+ if (type_and_offset_read_num(&tp, &first_offset,
+ header_base + start * entry_size +
+ COLUMN_NUMBER_SIZE, offset_size))
+ return ER_DYNCOL_FORMAT;
+ }
+ /* find data to be moved */
+ if (start < end)
+ {
+ size_t data_size=
+ get_length_interval(header_base + start * entry_size,
+ header_base + end * entry_size,
+ header_end, offset_size, max_offset);
+ if (data_size == DYNCOL_OFFSET_ERROR ||
+ (long) data_size < 0 ||
+ data_size > max_offset - first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta);
+ plan[i].mv_offset= first_offset;
+ plan[i].mv_length= data_size;
+ curr_offset+= data_size;
+ }
+ else
+ {
+ plan[i].mv_length= 0;
+ plan[i].mv_offset= curr_offset;
+ }
+
+ if (plan[i].ddelta == 0 && offset_size == new_offset_size &&
+ plan[i].act != PLAN_DELETE)
+ write+= entry_size * (end - start);
+ else
+ {
+ /*
+ Adjust all headers since last loop.
+ We have to do this as the offset for data has moved
+ */
+ for (k= start; k < end; k++)
+ {
+ uchar *read= header_base + k * entry_size;
+ size_t offs;
+ uint nm;
+ DYNAMIC_COLUMN_TYPE tp;
+
+ nm= uint2korr(read); /* Column nummber */
+ if (type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE,
+ offset_size))
+ return ER_DYNCOL_FORMAT;
+
+ if (k > start && offs < first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+
+ offs+= (size_t)plan[i].ddelta;
+ int2store(write, nm);
+ /* write rest of data at write + COLUMN_NUMBER_SIZE */
+ type_and_offset_store_num(write, new_offset_size, tp, offs);
+ write+= new_entry_size;
+ }
+ }
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ int2store(write, *((uint *)plan[i].key));
+ type_and_offset_store_num(write, new_offset_size,
+ plan[i].val[0].type,
+ curr_offset);
+ write+= new_entry_size;
+ curr_offset+= plan[i].length;
+ }
+ }
+ }
+
+ /*
+ Move data.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ str->length= (FIXED_HEADER_SIZE + new_header_size);
+ for (i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ j= end= plan[i].mv_end;
+ if (i != add_column_count &&
+ (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++;
+
+ /* copy first the data that was not replaced in original packed data */
+ if (start < end && plan[i].mv_length)
+ {
+ memmove((header_base + new_header_size +
+ (size_t)plan[i].mv_offset + (size_t)plan[i].ddelta),
+ header_base + header_size + (size_t)plan[i].mv_offset,
+ (size_t)plan[i].mv_length);
+ }
+ str->length+= (size_t)plan[i].mv_length;
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ data_store(str, plan[i].val, dyncol_fmt_num);/* Append new data */
+ }
+ }
+ }
+ return ER_DYNCOL_OK;
+}
+
+#ifdef UNUSED
+static enum enum_dyncol_func_result
+dynamic_column_update_move_right(DYNAMIC_COLUMN *str, PLAN *plan,
+ size_t offset_size,
+ size_t entry_size,
+ size_t header_size,
+ size_t new_offset_size,
+ size_t new_entry_size,
+ size_t new_header_size,
+ uint column_count,
+ uint new_column_count,
+ uint add_column_count,
+ uchar *header_end,
+ size_t max_offset)
+{
+ uchar *write;
+ uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE;
+ uint i, j, k;
+ size_t curr_offset;
+
+ write= (uchar *)str->str + FIXED_HEADER_SIZE;
+ set_fixed_header(str, new_offset_size, new_column_count);
+
+ /*
+ Move data first.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ for (curr_offset= 0, i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ size_t UNINIT_VAR(first_offset);
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ if (i == add_column_count)
+ j= end= column_count;
+ else
+ {
+ /*
+ old data portion. We don't need to check that j < column_count
+ as plan[i].place is guaranteed to have a pointer inside the
+ data.
+ */
+ while (header_base + j * entry_size < plan[i].place)
+ j++;
+ end= j;
+ if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++; /* data at 'j' will be removed */
+ }
+ plan[i].mv_end= end;
+
+ {
+ DYNAMIC_COLUMN_TYPE tp;
+ type_and_offset_read_num(&tp, &first_offset,
+ header_base +
+ start * entry_size + COLUMN_NUMBER_SIZE,
+ offset_size);
+ }
+ /* find data to be moved */
+ if (start < end)
+ {
+ size_t data_size=
+ get_length_interval(header_base + start * entry_size,
+ header_base + end * entry_size,
+ header_end, offset_size, max_offset);
+ if (data_size == DYNCOL_OFFSET_ERROR ||
+ (long) data_size < 0 ||
+ data_size > max_offset - first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta);
+ plan[i].mv_offset= first_offset;
+ plan[i].mv_length= data_size;
+ curr_offset+= data_size;
+ }
+ else
+ {
+ plan[i].mv_length= 0;
+ plan[i].mv_offset= curr_offset;
+ }
+
+ if (plan[i].ddelta == 0 && offset_size == new_offset_size &&
+ plan[i].act != PLAN_DELETE)
+ write+= entry_size * (end - start);
+ else
+ {
+ /*
+ Adjust all headers since last loop.
+ We have to do this as the offset for data has moved
+ */
+ for (k= start; k < end; k++)
+ {
+ uchar *read= header_base + k * entry_size;
+ size_t offs;
+ uint nm;
+ DYNAMIC_COLUMN_TYPE tp;
+
+ nm= uint2korr(read); /* Column nummber */
+ type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE,
+ offset_size);
+ if (k > start && offs < first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+
+ offs+= plan[i].ddelta;
+ int2store(write, nm);
+ /* write rest of data at write + COLUMN_NUMBER_SIZE */
+ if (type_and_offset_store_num(write, new_offset_size, tp, offs))
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ write+= new_entry_size;
+ }
+ }
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ int2store(write, *((uint *)plan[i].key));
+ if (type_and_offset_store_num(write, new_offset_size,
+ plan[i].val[0].type,
+ curr_offset))
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ write+= new_entry_size;
+ curr_offset+= plan[i].length;
+ }
+ }
+ }
+
+ /*
+ Move headers.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ str->length= (FIXED_HEADER_SIZE + new_header_size);
+ for (i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ j= end= plan[i].mv_end;
+ if (i != add_column_count &&
+ (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++;
+
+ /* copy first the data that was not replaced in original packed data */
+ if (start < end && plan[i].mv_length)
+ {
+ memmove((header_base + new_header_size +
+ plan[i].mv_offset + plan[i].ddelta),
+ header_base + header_size + plan[i].mv_offset,
+ plan[i].mv_length);
+ }
+ str->length+= plan[i].mv_length;
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ data_store(str, plan[i].val, dyncol_fmt_num); /* Append new data */
+ }
+ }
+ }
+ return ER_DYNCOL_OK;
+}
+#endif
+
+/**
+ Update the packed string with the given columns
+
+ @param str String where to write the data
+ @param add_column_count Number of columns in the arrays
+ @param column_numbers Array of columns numbers
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+/* plan allocated on the stack */
+#define IN_PLACE_PLAN 4
+
+enum enum_dyncol_func_result
+dynamic_column_update_many(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return dynamic_column_update_many_fmt(str, add_column_count, column_numbers,
+ values, FALSE);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_update_many_num(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return dynamic_column_update_many_fmt(str, add_column_count, column_numbers,
+ values, FALSE);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_update_many_named(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ LEX_STRING *column_names,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return dynamic_column_update_many_fmt(str, add_column_count, column_names,
+ values, TRUE);
+}
+
+static uint numlen(uint val)
+{
+ uint res;
+ if (val == 0)
+ return 1;
+ res= 0;
+ while(val)
+ {
+ res++;
+ val/=10;
+ }
+ return res;
+}
+
+static enum enum_dyncol_func_result
+dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool string_keys)
+{
+ PLAN *plan, *alloc_plan= NULL, in_place_plan[IN_PLACE_PLAN];
+ uchar *element;
+ DYN_HEADER header, new_header;
+ struct st_service_funcs *fmt, *new_fmt;
+ long long data_delta= 0, name_delta= 0;
+ uint i;
+ uint not_null;
+ long long header_delta= 0;
+ long long header_delta_sign, data_delta_sign;
+ int copy= FALSE;
+ enum enum_dyncol_func_result rc;
+ my_bool convert;
+
+ if (add_column_count == 0)
+ return ER_DYNCOL_OK;
+
+ memset(&header, 0, sizeof(header));
+ memset(&new_header, 0, sizeof(new_header));
+ new_header.format= (string_keys ? dyncol_fmt_str : dyncol_fmt_num);
+ new_fmt= fmt_data + new_header.format;
+
+ /*
+ Get columns in column order. As the data in 'str' is already
+ in column order this allows to replace all columns in one loop.
+ */
+ if (IN_PLACE_PLAN > add_column_count)
+ plan= in_place_plan;
+ else if (!(alloc_plan= plan=
+ (PLAN *)malloc(sizeof(PLAN) * (add_column_count + 1))))
+ return ER_DYNCOL_RESOURCE;
+
+ not_null= add_column_count;
+ for (i= 0, element= (uchar *) column_keys;
+ i < add_column_count;
+ i++, element+= new_fmt->key_size_in_array)
+ {
+ if ((*new_fmt->check_limit)(&element))
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+
+ plan[i].val= values + i;
+ plan[i].key= element;
+ if (values[i].type == DYN_COL_NULL)
+ not_null--;
+
+ }
+
+ if (str->length == 0)
+ {
+ /*
+ Just add new columns. If there was no columns to add we return
+ an empty string.
+ */
+ goto create_new_string;
+ }
+
+ /* Check that header is ok */
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ goto end;
+ fmt= fmt_data + header.format;
+ /* new format can't be numeric if the old one is names */
+ DBUG_ASSERT(new_header.format == dyncol_fmt_str ||
+ header.format == dyncol_fmt_num);
+ if (header.column_count == 0)
+ goto create_new_string;
+
+ qsort(plan, (size_t)add_column_count, sizeof(PLAN), new_fmt->plan_sort);
+
+ new_header.column_count= header.column_count;
+ new_header.nmpool_size= header.nmpool_size;
+ if ((convert= (new_header.format == dyncol_fmt_str &&
+ header.format == dyncol_fmt_num)))
+ {
+ DBUG_ASSERT(new_header.nmpool_size == 0);
+ for(i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ new_header.nmpool_size+= numlen(uint2korr(header.entry));
+ }
+ }
+
+ if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+
+ /*
+ Calculate how many columns and data is added/deleted and make a 'plan'
+ for each of them.
+ */
+ for (i= 0; i < add_column_count; i++)
+ {
+ /*
+ For now we don't allow creating two columns with the same number
+ at the time of create. This can be fixed later to just use the later
+ by comparing the pointers.
+ */
+ if (i < add_column_count - 1 &&
+ new_fmt->column_sort(&plan[i].key, &plan[i + 1].key) == 0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+
+ /* Set common variables for all plans */
+ plan[i].ddelta= data_delta;
+ plan[i].ndelta= name_delta;
+ /* get header delta in entries */
+ plan[i].hdelta= header_delta;
+ plan[i].length= 0; /* Length if NULL */
+
+ if (find_place(&header, plan[i].key, string_keys))
+ {
+ size_t entry_data_size, entry_name_size= 0;
+
+ /* Data existed; We have to replace or delete it */
+
+ entry_data_size= hdr_interval_length(&header, header.entry +
+ header.entry_size);
+ if (entry_data_size == DYNCOL_OFFSET_ERROR ||
+ (long) entry_data_size < 0)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+
+ if (new_header.format == dyncol_fmt_str)
+ {
+ if (header.format == dyncol_fmt_str)
+ {
+ LEX_STRING name;
+ if (read_name(&header, header.entry, &name))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+ entry_name_size= name.length;
+ }
+ else
+ entry_name_size= numlen(uint2korr(header.entry));
+ }
+
+ if (plan[i].val->type == DYN_COL_NULL)
+ {
+ /* Inserting a NULL means delete the old data */
+
+ plan[i].act= PLAN_DELETE; /* Remove old value */
+ header_delta--; /* One row less in header */
+ data_delta-= entry_data_size; /* Less data to store */
+ name_delta-= entry_name_size;
+ }
+ else
+ {
+ /* Replace the value */
+
+ plan[i].act= PLAN_REPLACE;
+ /* get data delta in bytes */
+ if ((plan[i].length= dynamic_column_value_len(plan[i].val,
+ new_header.format)) ==
+ (size_t) ~0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+ data_delta+= plan[i].length - entry_data_size;
+ if (new_header.format == dyncol_fmt_str)
+ {
+ name_delta+= ((LEX_STRING *)(plan[i].key))->length - entry_name_size;
+ }
+ }
+ }
+ else
+ {
+ /* Data did not exists. Add if it it's not NULL */
+
+ if (plan[i].val->type == DYN_COL_NULL)
+ {
+ plan[i].act= PLAN_NOP; /* Mark entry to be skiped */
+ }
+ else
+ {
+ /* Add new value */
+
+ plan[i].act= PLAN_ADD;
+ header_delta++; /* One more row in header */
+ /* get data delta in bytes */
+ if ((plan[i].length= dynamic_column_value_len(plan[i].val,
+ new_header.format)) ==
+ (size_t) ~0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+ data_delta+= plan[i].length;
+ if (new_header.format == dyncol_fmt_str)
+ name_delta+= ((LEX_STRING *)plan[i].key)->length;
+ }
+ }
+ plan[i].place= header.entry;
+ }
+ plan[add_column_count].hdelta= header_delta;
+ plan[add_column_count].ddelta= data_delta;
+ plan[add_column_count].act= PLAN_NOP;
+ plan[add_column_count].place= header.dtpool;
+
+ new_header.column_count= (uint)(header.column_count + header_delta);
+
+ /*
+ Check if it is only "increasing" or only "decreasing" plan for (header
+ and data separately).
+ */
+ new_header.data_size= header.data_size + (size_t)data_delta;
+ new_header.nmpool_size= new_header.nmpool_size + (size_t)name_delta;
+ DBUG_ASSERT(new_header.format != dyncol_fmt_num ||
+ new_header.nmpool_size == 0);
+ if ((new_header.offset_size=
+ new_fmt->dynamic_column_offset_bytes(new_header.data_size)) >=
+ new_fmt->max_offset_size)
+ {
+ rc= ER_DYNCOL_LIMIT;
+ goto end;
+ }
+
+ copy= ((header.format != new_header.format) ||
+ (new_header.format == dyncol_fmt_str));
+ /* if (new_header.offset_size!=offset_size) then we have to rewrite header */
+ header_delta_sign=
+ ((int)new_header.offset_size + new_fmt->fixed_hdr_entry) -
+ ((int)header.offset_size + fmt->fixed_hdr_entry);
+ data_delta_sign= 0;
+ // plan[add_column_count] contains last deltas.
+ for (i= 0; i <= add_column_count && !copy; i++)
+ {
+ /* This is the check for increasing/decreasing */
+ DELTA_CHECK(header_delta_sign, plan[i].hdelta, copy);
+ DELTA_CHECK(data_delta_sign, plan[i].ddelta, copy);
+ }
+ calc_param(&new_header.entry_size, &new_header.header_size,
+ new_fmt->fixed_hdr_entry,
+ new_header.offset_size, new_header.column_count);
+
+ /*
+ Need copy because:
+ 1, Header/data parts moved in different directions.
+ 2. There is no enough allocated space in the string.
+ 3. Header and data moved in different directions.
+ */
+ if (copy || /*1.*/
+ str->max_length < str->length + header_delta + data_delta || /*2.*/
+ ((header_delta_sign < 0 && data_delta_sign > 0) ||
+ (header_delta_sign > 0 && data_delta_sign < 0))) /*3.*/
+ rc= dynamic_column_update_copy(str, plan, add_column_count,
+ &header, &new_header,
+ convert);
+ else
+ if (header_delta_sign < 0)
+ rc= dynamic_column_update_move_left(str, plan, header.offset_size,
+ header.entry_size,
+ header.header_size,
+ new_header.offset_size,
+ new_header.entry_size,
+ new_header.header_size,
+ header.column_count,
+ new_header.column_count,
+ add_column_count, header.dtpool,
+ header.data_size);
+ else
+ /*
+ rc= dynamic_column_update_move_right(str, plan, offset_size,
+ entry_size, header_size,
+ new_header.offset_size,
+ new_header.entry_size,
+ new_heder.header_size, column_count,
+ new_header.column_count,
+ add_column_count, header_end,
+ header.data_size);
+ */
+ rc= dynamic_column_update_copy(str, plan, add_column_count,
+ &header, &new_header,
+ convert);
+end:
+ free(alloc_plan);
+ return rc;
+
+create_new_string:
+ /* There is no columns from before, so let's just add the new ones */
+ rc= ER_DYNCOL_OK;
+ free(alloc_plan);
+ if (not_null != 0)
+ rc= dynamic_column_create_many_internal_fmt(str, add_column_count,
+ (uint*)column_keys, values,
+ str->str == NULL,
+ string_keys);
+ goto end;
+}
+
+
+/**
+ Update the packed string with the given column
+
+ @param str String where to write the data
+ @param column_number Array of columns number
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+
+
+int dynamic_column_update(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *value)
+{
+ return dynamic_column_update_many(str, 1, &column_nr, value);
+}
+
+
+enum enum_dyncol_func_result
+mariadb_dyncol_check(DYNAMIC_COLUMN *str)
+{
+ struct st_service_funcs *fmt;
+ enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
+ DYN_HEADER header;
+ uint i;
+ size_t data_offset= 0, name_offset= 0;
+ size_t prev_data_offset= 0, prev_name_offset= 0;
+ LEX_STRING name= {0,0}, prev_name= {0,0};
+ uint num= 0, prev_num= 0;
+ void *key, *prev_key;
+ enum enum_dynamic_column_type type= DYN_COL_NULL, prev_type= DYN_COL_NULL;
+
+ if (str->length == 0)
+ {
+ return(ER_DYNCOL_OK);
+ }
+
+ memset(&header, 0, sizeof(header));
+
+ /* Check that header is OK */
+ if (read_fixed_header(&header, str))
+ {
+ goto end;
+ }
+ fmt= fmt_data + header.format;
+ calc_param(&header.entry_size, &header.header_size,
+ fmt->fixed_hdr_entry, header.offset_size,
+ header.column_count);
+ /* headers are out of string length (no space for data and part of headers) */
+ if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length)
+ {
+ goto end;
+ }
+ header.header= (uchar*)str->str + fmt->fixed_hdr;
+ header.nmpool= header.header + header.header_size;
+ header.dtpool= header.nmpool + header.nmpool_size;
+ header.data_size= str->length - fmt->fixed_hdr -
+ header.header_size - header.nmpool_size;
+
+ /* read and check headers */
+ if (header.format == dyncol_fmt_num)
+ {
+ key= &num;
+ prev_key= &prev_num;
+ }
+ else
+ {
+ key= &name;
+ prev_key= &prev_name;
+ }
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+
+ if (header.format == dyncol_fmt_num)
+ {
+ num= uint2korr(header.entry);
+ }
+ else
+ {
+ DBUG_ASSERT(header.format == dyncol_fmt_str);
+ if (read_name(&header, header.entry, &name))
+ {
+ goto end;
+ }
+ name_offset= name.str - (char *)header.nmpool;
+ }
+ if ((*fmt->type_and_offset_read)(&type, &data_offset,
+ header.entry + fmt->fixed_hdr_entry,
+ header.offset_size))
+ goto end;
+
+ DBUG_ASSERT(type != DYN_COL_NULL);
+ if (data_offset > header.data_size)
+ {
+ goto end;
+ }
+ if (prev_type != DYN_COL_NULL)
+ {
+ /* It is not first entry */
+ if (prev_data_offset >= data_offset)
+ {
+ goto end;
+ }
+ if (prev_name_offset > name_offset)
+ {
+ goto end;
+ }
+ if ((*fmt->column_sort)(&prev_key, &key) >= 0)
+ {
+ goto end;
+ }
+ }
+ prev_num= num;
+ prev_name= name;
+ prev_data_offset= data_offset;
+ prev_name_offset= name_offset;
+ prev_type= type;
+ }
+
+ /* check data, which we can */
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ DYNAMIC_COLUMN_VALUE store;
+ // already checked by previouse pass
+ (*fmt->type_and_offset_read)(&header.type, &header.offset,
+ header.entry + fmt->fixed_hdr_entry,
+ header.offset_size);
+ header.length=
+ hdr_interval_length(&header, header.entry + header.entry_size);
+ header.data= header.dtpool + header.offset;
+ switch ((header.type)) {
+ case DYN_COL_INT:
+ rc= dynamic_column_sint_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_UINT:
+ rc= dynamic_column_uint_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_DOUBLE:
+ rc= dynamic_column_double_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_STRING:
+ rc= dynamic_column_string_read(&store, header.data, header.length);
+ break;
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ rc= dynamic_column_decimal_read(&store, header.data, header.length);
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ rc= dynamic_column_date_time_read(&store, header.data,
+ header.length);
+ break;
+ case DYN_COL_DATE:
+ rc= dynamic_column_date_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_TIME:
+ rc= dynamic_column_time_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_DYNCOL:
+ rc= dynamic_column_dyncol_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_NULL:
+ default:
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+ if (rc != ER_DYNCOL_OK)
+ {
+ DBUG_ASSERT(rc < 0);
+ goto end;
+ }
+ }
+
+ rc= ER_DYNCOL_OK;
+end:
+ return(rc);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_val_str(DYNAMIC_STRING *str, DYNAMIC_COLUMN_VALUE *val,
+ MARIADB_CHARSET_INFO *cs, char quote)
+{
+ char buff[40];
+ size_t len;
+ switch (val->type) {
+ case DYN_COL_INT:
+ len= snprintf(buff, sizeof(buff), "%lld", val->x.long_value);
+ if (ma_dynstr_append_mem(str, buff, len))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ case DYN_COL_UINT:
+ len= snprintf(buff, sizeof(buff), "%llu", val->x.ulong_value);
+ if (ma_dynstr_append_mem(str, buff, len))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ case DYN_COL_DOUBLE:
+ len= snprintf(buff, sizeof(buff), "%g", val->x.double_value);
+ if (ma_dynstr_realloc(str, len + (quote ? 2 : 0)))
+ return ER_DYNCOL_RESOURCE;
+ if (quote)
+ str->str[str->length++]= quote;
+ ma_dynstr_append_mem(str, buff, len);
+ if (quote)
+ str->str[str->length++]= quote;
+ break;
+ case DYN_COL_DYNCOL:
+ case DYN_COL_STRING:
+ {
+ char *alloc= NULL;
+ char *from= val->x.string.value.str;
+ ulong bufflen;
+ my_bool conv= ((val->x.string.charset == cs) ||
+ !strcmp(val->x.string.charset->name, cs->name));
+ my_bool rc;
+ len= val->x.string.value.length;
+ bufflen= (ulong)(len * (conv ? cs->char_maxlen : 1));
+ if (ma_dynstr_realloc(str, bufflen))
+ return ER_DYNCOL_RESOURCE;
+
+ // guaranty UTF-8 string for value
+ if (!conv)
+ {
+#ifndef LIBMARIADB
+ uint dumma_errors;
+#else
+ int dumma_errors;
+#endif
+ if (!quote)
+ {
+ /* convert to the destination */
+ str->length+=
+#ifndef LIBMARIADB
+ copy_and_convert_extended(str->str, bufflen,
+ cs,
+ from, (uint32)len,
+ val->x.string.charset,
+ &dumma_errors);
+#else
+ mariadb_convert_string(from, &len, val->x.string.charset,
+ str->str, (size_t *)&bufflen, cs, &dumma_errors);
+#endif
+ return ER_DYNCOL_OK;
+ }
+ if ((alloc= (char *)malloc(bufflen)))
+ {
+ len=
+#ifndef LIBMARIADB
+ copy_and_convert_extended(alloc, bufflen, cs,
+ from, (uint32)len,
+ val->x.string.charset,
+ &dumma_errors);
+#else
+ mariadb_convert_string(from, &len, val->x.string.charset,
+ alloc, (size_t *)&bufflen, cs, &dumma_errors);
+#endif
+ from= alloc;
+ }
+ else
+ return ER_DYNCOL_RESOURCE;
+ }
+ if (quote)
+ rc= ma_dynstr_append_mem(str, &quote, 1);
+ rc= ma_dynstr_append_mem(str, from, len);
+ if (quote)
+ rc= ma_dynstr_append_mem(str, &quote, 1);
+ if (alloc)
+ free(alloc);
+ if (rc)
+ return ER_DYNCOL_RESOURCE;
+ break;
+ }
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ {
+ int len= sizeof(buff);
+ decimal2string(&val->x.decimal.value, buff, &len,
+ 0, val->x.decimal.value.frac,
+ '0');
+ if (ma_dynstr_append_mem(str, buff, len))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ }
+#endif
+ case DYN_COL_DATETIME:
+ case DYN_COL_DATE:
+ case DYN_COL_TIME:
+#ifndef LIBMARIADB
+ len= my_TIME_to_str(&val->x.time_value, buff, AUTO_SEC_PART_DIGITS);
+#else
+ len= mariadb_time_to_string(&val->x.time_value, buff, 39, AUTO_SEC_PART_DIGITS);
+#endif
+ if (ma_dynstr_realloc(str, len + (quote ? 2 : 0)))
+ return ER_DYNCOL_RESOURCE;
+ if (quote)
+ str->str[str->length++]= '"';
+ ma_dynstr_append_mem(str, buff, len);
+ if (quote)
+ str->str[str->length++]= '"';
+ break;
+ case DYN_COL_NULL:
+ if (ma_dynstr_append_mem(str, "null", 4))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ default:
+ return(ER_DYNCOL_FORMAT);
+ }
+ return(ER_DYNCOL_OK);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_val_long(longlong *ll, DYNAMIC_COLUMN_VALUE *val)
+{
+ enum enum_dyncol_func_result rc= ER_DYNCOL_OK;
+ *ll= 0;
+ switch (val->type) {
+ case DYN_COL_INT:
+ *ll= val->x.long_value;
+ break;
+ case DYN_COL_UINT:
+ *ll= (longlong)val->x.ulong_value;
+ if (val->x.ulong_value > ULONGLONG_MAX)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_DOUBLE:
+ *ll= (longlong)val->x.double_value;
+ if (((double) *ll) != val->x.double_value)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_STRING:
+ {
+ char *src= val->x.string.value.str;
+ size_t len= val->x.string.value.length;
+ longlong i= 0, sign= 1;
+
+ while (len && isspace(*src)) src++,len--;
+
+ if (len)
+ {
+ if (*src == '-')
+ {
+ sign= -1;
+ src++;
+ }
+ while(len && isdigit(*src))
+ {
+ i= i * 10 + (*src - '0');
+ src++;
+ }
+ }
+ else
+ rc= ER_DYNCOL_TRUNCATED;
+ if (len)
+ rc= ER_DYNCOL_TRUNCATED;
+ *ll= i * sign;
+ break;
+ }
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ if (decimal2longlong(&val->x.decimal.value, ll) != E_DEC_OK)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ *ll= (val->x.time_value.year * 10000000000ull +
+ val->x.time_value.month * 100000000L +
+ val->x.time_value.day * 1000000 +
+ val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DATE:
+ *ll= (val->x.time_value.year * 10000 +
+ val->x.time_value.month * 100 +
+ val->x.time_value.day) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_TIME:
+ *ll= (val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DYNCOL:
+ case DYN_COL_NULL:
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ default:
+ return(ER_DYNCOL_FORMAT);
+ }
+ return(rc);
+}
+
+
+enum enum_dyncol_func_result
+mariadb_dyncol_val_double(double *dbl, DYNAMIC_COLUMN_VALUE *val)
+{
+ enum enum_dyncol_func_result rc= ER_DYNCOL_OK;
+ *dbl= 0;
+ switch (val->type) {
+ case DYN_COL_INT:
+ *dbl= (double)val->x.long_value;
+ if (((longlong) *dbl) != val->x.long_value)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_UINT:
+ *dbl= (double)val->x.ulong_value;
+ if (((ulonglong) *dbl) != val->x.ulong_value)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_DOUBLE:
+ *dbl= val->x.double_value;
+ break;
+ case DYN_COL_STRING:
+ {
+ char *str, *end;
+ if ((str= malloc(val->x.string.value.length + 1)))
+ return ER_DYNCOL_RESOURCE;
+ memcpy(str, val->x.string.value.str, val->x.string.value.length);
+ str[val->x.string.value.length]= '\0';
+ *dbl= strtod(str, &end);
+ if (*end != '\0')
+ rc= ER_DYNCOL_TRUNCATED;
+ free(str);
+ break;
+ }
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ if (decimal2double(&val->x.decimal.value, dbl) != E_DEC_OK)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ *dbl= (double)(val->x.time_value.year * 10000000000ull +
+ val->x.time_value.month * 100000000L +
+ val->x.time_value.day * 1000000 +
+ val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DATE:
+ *dbl= (double)(val->x.time_value.year * 10000 +
+ val->x.time_value.month * 100 +
+ val->x.time_value.day) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_TIME:
+ *dbl= (double)(val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DYNCOL:
+ case DYN_COL_NULL:
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ default:
+ return(ER_DYNCOL_FORMAT);
+ }
+ return(rc);
+}
+
+
+/**
+ Convert to JSON
+
+ @param str The packed string
+ @param json Where to put json result
+
+ @return ER_DYNCOL_* return code
+*/
+
+#define JSON_STACK_PROTECTION 10
+
+static enum enum_dyncol_func_result
+mariadb_dyncol_json_internal(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json,
+ uint lvl)
+{
+ DYN_HEADER header;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ if (lvl >= JSON_STACK_PROTECTION)
+ {
+ rc= ER_DYNCOL_RESOURCE;
+ goto err;
+ }
+
+
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ goto err;
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+
+ rc= ER_DYNCOL_RESOURCE;
+
+ if (ma_dynstr_append_mem(json, "{", 1))
+ goto err;
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ DYNAMIC_COLUMN_VALUE val;
+ if (i != 0 && ma_dynstr_append_mem(json, ",", 1))
+ goto err;
+ header.length=
+ hdr_interval_length(&header, header.entry + header.entry_size);
+ header.data= header.dtpool + header.offset;
+ /*
+ Check that the found data is withing the ranges. This can happen if
+ we get data with wrong offsets.
+ */
+ if (header.length == DYNCOL_OFFSET_ERROR ||
+ header.length > INT_MAX || header.offset > header.data_size)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ if ((rc= dynamic_column_get_value(&header, &val)) < 0)
+ goto err;
+ if (header.format == dyncol_fmt_num)
+ {
+ uint nm= uint2korr(header.entry);
+ if (ma_dynstr_realloc(json, DYNCOL_NUM_CHAR + 3))
+ goto err;
+ json->str[json->length++]= '"';
+ json->length+= snprintf(json->str + json->length,
+ DYNCOL_NUM_CHAR, "%u", nm);
+ }
+ else
+ {
+ LEX_STRING name;
+ if (read_name(&header, header.entry, &name))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ if (ma_dynstr_realloc(json, name.length + 3))
+ goto err;
+ json->str[json->length++]= '"';
+ memcpy(json->str + json->length, name.str, name.length);
+ json->length+= name.length;
+ }
+ json->str[json->length++]= '"';
+ json->str[json->length++]= ':';
+ if (val.type == DYN_COL_DYNCOL)
+ {
+ /* here we use it only for read so can cheat a bit */
+ DYNAMIC_COLUMN dc;
+ memset(&dc, 0, sizeof(dc));
+ dc.str= val.x.string.value.str;
+ dc.length= val.x.string.value.length;
+ if (mariadb_dyncol_json_internal(&dc, json, lvl + 1) < 0)
+ {
+ dc.str= NULL; dc.length= 0;
+ goto err;
+ }
+ dc.str= NULL; dc.length= 0;
+ }
+ else
+ {
+ if ((rc= mariadb_dyncol_val_str(json, &val,
+ ma_charset_utf8_general_ci, '"')) < 0)
+ goto err;
+ }
+ }
+ if (ma_dynstr_append_mem(json, "}", 1))
+ {
+ rc= ER_DYNCOL_RESOURCE;
+ goto err;
+ }
+ return ER_DYNCOL_OK;
+
+err:
+ json->length= 0;
+ return rc;
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_json(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json)
+{
+
+ if (ma_init_dynamic_string(json, NULL, str->length * 2, 100))
+ return ER_DYNCOL_RESOURCE;
+
+ return mariadb_dyncol_json_internal(str, json, 1);
+}
+
+/**
+ Convert to DYNAMIC_COLUMN_VALUE values and names (LEX_STING) dynamic array
+
+ @param str The packed string
+ @param count number of elements in the arrays
+ @param names Where to put names (should be free by user)
+ @param vals Where to put values (should be free by user)
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_unpack(DYNAMIC_COLUMN *str,
+ uint *count,
+ LEX_STRING **names, DYNAMIC_COLUMN_VALUE **vals)
+{
+ DYN_HEADER header;
+ char *nm;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ *count= 0; *names= 0; *vals= 0;
+
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ *vals= (DYNAMIC_COLUMN_VALUE *)malloc(sizeof(DYNAMIC_COLUMN_VALUE)* header.column_count);
+ if (header.format == dyncol_fmt_num)
+ {
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count +
+ DYNCOL_NUM_CHAR * header.column_count);
+ nm= (char *)((*names) + header.column_count);
+ }
+ else
+ {
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count);
+ nm= 0;
+ }
+ if (!(*vals) || !(*names))
+ {
+ rc= ER_DYNCOL_RESOURCE;
+ goto err;
+ }
+
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ header.length=
+ hdr_interval_length(&header, header.entry + header.entry_size);
+ header.data= header.dtpool + header.offset;
+ /*
+ Check that the found data is withing the ranges. This can happen if
+ we get data with wrong offsets.
+ */
+ if (header.length == DYNCOL_OFFSET_ERROR ||
+ header.length > INT_MAX || header.offset > header.data_size)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ if ((rc= dynamic_column_get_value(&header, (*vals) + i)) < 0)
+ goto err;
+
+ if (header.format == dyncol_fmt_num)
+ {
+ uint num= uint2korr(header.entry);
+ (*names)[i].str= nm;
+ (*names)[i].length= snprintf(nm, DYNCOL_NUM_CHAR, "%u", num);
+ nm+= (*names)[i].length + 1;
+ }
+ else
+ {
+ if (read_name(&header, header.entry, (*names) + i))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ }
+ }
+
+ *count= header.column_count;
+ return ER_DYNCOL_OK;
+
+err:
+ if (*vals)
+ {
+ free(*vals);
+ *vals= 0;
+ }
+ if (*names)
+ {
+ free(*names);
+ *names= 0;
+ }
+ return rc;
+}
+
+
+/**
+ Get not NULL column count
+
+ @param str The packed string
+ @param column_count Where to put column count
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_column_count(DYNAMIC_COLUMN *str, uint *column_count)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc;
+
+ (*column_count)= 0;
+ if (str->length == 0)
+ return ER_DYNCOL_OK;
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+ *column_count= header.column_count;
+ return rc;
+}
+
+/**
+ Release dynamic column memory
+
+ @param str dynamic column
+ @return void
+*/
+void mariadb_dyncol_free(DYNAMIC_COLUMN *str)
+{
+ ma_dynstr_free(str);
+}
diff --git a/mysql/libmariadb/mariadb_lib.c b/mysql/libmariadb/mariadb_lib.c
new file mode 100644
index 0000000..d8f3ada
--- /dev/null
+++ b/mysql/libmariadb/mariadb_lib.c
@@ -0,0 +1,4141 @@
+/************************************************************************************
+ 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
+*************************************************************************************/
+
+#include <ma_global.h>
+
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include <ma_common.h>
+#include "ma_context.h"
+#include "mysql.h"
+#include "mariadb_version.h"
+#include "ma_server_error.h"
+#include <mariadb/ma_io.h>
+#include "errmsg.h"
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <mariadb_dyncol.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#if !defined(MSDOS) && !defined(_WIN32)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef HAVE_SELECT_H
+# include <select.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+#include <ma_sha1.h>
+#ifndef _WIN32
+#include <poll.h>
+#endif
+#include <ma_pvio.h>
+#ifdef HAVE_TLS
+#include <ma_tls.h>
+#endif
+#include <mysql/client_plugin.h>
+#ifdef _WIN32
+#include "Shlwapi.h"
+#endif
+
+#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15)
+#define MA_RPL_VERSION_HACK "5.5.5-"
+
+#undef max_allowed_packet
+#undef net_buffer_length
+extern ulong max_allowed_packet; /* net.c */
+extern ulong net_buffer_length; /* net.c */
+
+static MYSQL_PARAMETERS mariadb_internal_parameters= {&max_allowed_packet, &net_buffer_length, 0};
+static my_bool mysql_client_init=0;
+static void mysql_close_options(MYSQL *mysql);
+extern void release_configuration_dirs();
+extern char **get_default_configuration_dirs();
+extern my_bool ma_init_done;
+extern my_bool mysql_ps_subsystem_initialized;
+extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename);
+extern const MARIADB_CHARSET_INFO * mysql_find_charset_nr(uint charsetnr);
+extern const MARIADB_CHARSET_INFO * mysql_find_charset_name(const char * const name);
+extern int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
+ const char *data_plugin, const char *db);
+extern int net_add_multi_command(NET *net, uchar command, const uchar *packet,
+ size_t length);
+
+extern LIST *pvio_callback;
+
+/* prepare statement methods from my_stmt.c */
+extern my_bool mthd_supported_buffer_type(enum enum_field_types type);
+extern my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt);
+extern my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt);
+extern my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt);
+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_read_all_rows(MYSQL_STMT *stmt);
+extern void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt);
+extern my_bool _mariadb_read_options(MYSQL *mysql, const char *config_file,
+ char *group);
+extern unsigned char *mysql_net_store_length(unsigned char *packet, size_t length);
+
+extern void
+my_context_install_suspend_resume_hook(struct mysql_async_context *b,
+ void (*hook)(my_bool, void *),
+ void *user_data);
+
+uint mysql_port=0;
+my_string mysql_unix_port=0;
+
+#define CONNECT_TIMEOUT 0
+
+struct st_mariadb_methods MARIADB_DEFAULT_METHODS;
+
+#if defined(MSDOS) || defined(_WIN32)
+// socket_errno is defined in ma_global.h for all platforms
+#define perror(A)
+#else
+#include <errno.h>
+#define SOCKET_ERROR -1
+#endif /* _WIN32 */
+
+#include <mysql/client_plugin.h>
+
+#define native_password_plugin_name "mysql_native_password"
+
+#define IS_CONNHDLR_ACTIVE(mysql)\
+ ((mysql)->extension && (mysql)->extension->conn_hdlr)
+
+static void end_server(MYSQL *mysql);
+static void mysql_close_memory(MYSQL *mysql);
+void read_user_name(char *name);
+my_bool STDCALL mariadb_reconnect(MYSQL *mysql);
+static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length);
+
+extern int mysql_client_plugin_init();
+extern void mysql_client_plugin_deinit();
+
+/* net_get_error */
+void net_get_error(char *buf, size_t buf_len,
+ char *error, size_t error_len,
+ unsigned int *error_no,
+ char *sqlstate)
+{
+ char *p= buf;
+ size_t error_msg_len= 0;
+
+ if (buf_len > 2)
+ {
+ *error_no= uint2korr(p);
+ p+= 2;
+
+ /* since 4.1 sqlstate is following */
+ if (*p == '#')
+ {
+ memcpy(sqlstate, ++p, SQLSTATE_LENGTH);
+ p+= SQLSTATE_LENGTH;
+ }
+ error_msg_len= buf_len - (p - buf);
+ error_msg_len= MIN(error_msg_len, error_len - 1);
+ memcpy(error, p, error_msg_len);
+ }
+ else
+ {
+ *error_no= CR_UNKNOWN_ERROR;
+ memcpy(sqlstate, SQLSTATE_UNKNOWN, SQLSTATE_LENGTH);
+ }
+}
+
+/*****************************************************************************
+** read a packet from server. Give error message if socket was down
+** or packet is an error message
+*****************************************************************************/
+
+ulong
+ma_net_safe_read(MYSQL *mysql)
+{
+ NET *net= &mysql->net;
+ ulong len=0;
+
+restart:
+ if (net->pvio != 0)
+ len=ma_net_read(net);
+
+ if (len == packet_error || len == 0)
+ {
+ end_server(mysql);
+ my_set_error(mysql, net->last_errno == ER_NET_PACKET_TOO_LARGE ?
+ CR_NET_PACKET_TOO_LARGE:
+ CR_SERVER_LOST,
+ SQLSTATE_UNKNOWN, 0, errno);
+ return(packet_error);
+ }
+ if (net->read_pos[0] == 255)
+ {
+ if (len > 3)
+ {
+ char *pos=(char*) net->read_pos+1;
+ uint last_errno=uint2korr(pos);
+ pos+=2;
+ len-=2;
+
+ if (last_errno== 65535 &&
+ ((mariadb_connection(mysql) && (mysql->server_capabilities & CLIENT_PROGRESS)) ||
+ (!(mysql->extension->mariadb_server_capabilities & MARIADB_CLIENT_PROGRESS << 32))))
+ {
+ if (cli_report_progress(mysql, (uchar *)pos, (uint) (len-1)))
+ {
+ /* Wrong packet */
+ my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0);
+ return (packet_error);
+ }
+ goto restart;
+ }
+ net->last_errno= last_errno;
+ if (pos[0]== '#')
+ {
+ ma_strmake(net->sqlstate, pos+1, SQLSTATE_LENGTH);
+ pos+= SQLSTATE_LENGTH + 1;
+ }
+ else
+ {
+ strcpy(net->sqlstate, SQLSTATE_UNKNOWN);
+ }
+ ma_strmake(net->last_error,(char*) pos,
+ min(len,sizeof(net->last_error)-1));
+ }
+ else
+ {
+ my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);
+ }
+
+ mysql->server_status&= ~SERVER_MORE_RESULTS_EXIST;
+
+ return(packet_error);
+ }
+ return len;
+}
+
+/*
+ Report progress to the client
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length)
+{
+ uint stage, max_stage, proc_length;
+ double progress;
+ uchar *start= packet;
+
+ if (length < 5)
+ return 1; /* Wrong packet */
+
+ if (!(mysql->options.extension && mysql->options.extension->report_progress))
+ return 0; /* No callback, ignore packet */
+
+ packet++; /* Ignore number of strings */
+ stage= (uint) *packet++;
+ max_stage= (uint) *packet++;
+ progress= uint3korr(packet)/1000.0;
+ packet+= 3;
+ proc_length= net_field_length(&packet);
+ if (packet + proc_length > start + length)
+ return 1; /* Wrong packet */
+ (*mysql->options.extension->report_progress)(mysql, stage, max_stage,
+ progress, (char*) packet,
+ proc_length);
+ return 0;
+}
+
+/* Get the length of next field. Change parameter to point at fieldstart */
+ulong
+net_field_length(uchar **packet)
+{
+ reg1 uchar *pos= *packet;
+ if (*pos < 251)
+ {
+ (*packet)++;
+ return (ulong) *pos;
+ }
+ if (*pos == 251)
+ {
+ (*packet)++;
+ return NULL_LENGTH;
+ }
+ if (*pos == 252)
+ {
+ (*packet)+=3;
+ return (ulong) uint2korr(pos+1);
+ }
+ if (*pos == 253)
+ {
+ (*packet)+=4;
+ return (ulong) uint3korr(pos+1);
+ }
+ (*packet)+=9; /* Must be 254 when here */
+ return (ulong) uint4korr(pos+1);
+}
+
+/* Same as above, but returns ulonglong values */
+
+static unsigned long long
+net_field_length_ll(uchar **packet)
+{
+ reg1 uchar *pos= *packet;
+ if (*pos < 251)
+ {
+ (*packet)++;
+ return (unsigned long long) *pos;
+ }
+ if (*pos == 251)
+ {
+ (*packet)++;
+ return (unsigned long long) NULL_LENGTH;
+ }
+ if (*pos == 252)
+ {
+ (*packet)+=3;
+ return (unsigned long long) uint2korr(pos+1);
+ }
+ if (*pos == 253)
+ {
+ (*packet)+=4;
+ return (unsigned long long) uint3korr(pos+1);
+ }
+ (*packet)+=9; /* Must be 254 when here */
+#ifdef NO_CLIENT_LONGLONG
+ return (unsigned long long) uint4korr(pos+1);
+#else
+ return (unsigned long long) uint8korr(pos+1);
+#endif
+}
+
+
+void free_rows(MYSQL_DATA *cur)
+{
+ if (cur)
+ {
+ ma_free_root(&cur->alloc,MYF(0));
+ free(cur);
+ }
+}
+
+int
+mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg,
+ size_t length, my_bool skipp_check, void *opt_arg)
+{
+ NET *net= &mysql->net;
+ int result= -1;
+ if (mysql->net.pvio == 0)
+ {
+ /* Do reconnect if possible */
+ if (mariadb_reconnect(mysql))
+ return(1);
+ }
+ if (mysql->status != MYSQL_STATUS_READY ||
+ mysql->server_status & SERVER_MORE_RESULTS_EXIST)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ if (IS_CONNHDLR_ACTIVE(mysql))
+ {
+ result= mysql->extension->conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg);
+ if (result== -1)
+ return(result);
+ }
+
+ CLEAR_CLIENT_ERROR(mysql);
+
+ mysql->info=0;
+ mysql->affected_rows= ~(unsigned long long) 0;
+ ma_net_clear(net); /* Clear receive buffer */
+ if (!arg)
+ arg="";
+
+ if (net->extension->multi_status== COM_MULTI_ENABLED)
+ {
+ return net_add_multi_command(net, command, (const uchar *)arg, length);
+ }
+
+ if (ma_net_write_command(net,(uchar) command,arg,
+ length ? length : (ulong) strlen(arg), 0))
+ {
+ if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
+ {
+ my_set_error(mysql, CR_NET_PACKET_TOO_LARGE, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ end_server(mysql);
+ if (mariadb_reconnect(mysql))
+ goto end;
+ if (ma_net_write_command(net,(uchar) command,arg,
+ length ? length : (ulong) strlen(arg), 0))
+ {
+ my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ result=0;
+
+ if (net->extension->multi_status > COM_MULTI_OFF)
+ skipp_check= 1;
+
+ if (!skipp_check)
+ {
+ result= ((mysql->packet_length=ma_net_safe_read(mysql)) == packet_error ?
+ 1 : 0);
+ }
+ end:
+ return(result);
+}
+
+int
+ma_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
+ size_t length, my_bool skipp_check, void *opt_arg)
+{
+ return mysql->methods->db_command(mysql, command, arg, length, skipp_check, opt_arg);
+}
+
+int ma_multi_command(MYSQL *mysql, enum enum_multi_status status)
+{
+ NET *net= &mysql->net;
+
+ switch (status) {
+ case COM_MULTI_OFF:
+ ma_net_clear(net);
+ net->extension->multi_status= status;
+ return 0;
+ case COM_MULTI_ENABLED:
+ if (net->extension->multi_status > COM_MULTI_DISABLED)
+ return 1;
+ ma_net_clear(net);
+ net->extension->multi_status= status;
+ return 0;
+ case COM_MULTI_DISABLED:
+ /* Opposite to COM_MULTI_OFF we don't clear net buffer,
+ next command or com_nulti_end will flush entire buffer */
+ net->extension->multi_status= status;
+ return 0;
+ case COM_MULTI_END:
+ {
+ size_t len= net->write_pos - net->buff - NET_HEADER_SIZE;
+
+ if (len < NET_HEADER_SIZE) /* don't send empty COM_MULTI */
+ {
+ ma_net_clear(net);
+ return 1;
+ }
+ net->extension->multi_status= COM_MULTI_OFF;
+ return ma_net_flush(net);
+ }
+ case COM_MULTI_CANCEL:
+ ma_net_clear(net);
+ net->extension->multi_status= COM_MULTI_OFF;
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static void free_old_query(MYSQL *mysql)
+{
+ if (mysql->fields)
+ ma_free_root(&mysql->field_alloc,MYF(0));
+ ma_init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */
+ mysql->fields=0;
+ mysql->field_count=0; /* For API */
+ mysql->info= 0;
+ return;
+}
+
+#if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL)
+struct passwd *getpwuid(uid_t);
+char* getlogin(void);
+#endif
+
+#if !defined(MSDOS) && ! defined(VMS) && !defined(_WIN32) && !defined(OS2)
+void read_user_name(char *name)
+{
+ if (geteuid() == 0)
+ strcpy(name,"root"); /* allow use of surun */
+ else
+ {
+#ifdef HAVE_GETPWUID
+ struct passwd *skr;
+ const char *str;
+ if ((str=getlogin()) == NULL)
+ {
+ if ((skr=getpwuid(geteuid())) != NULL)
+ str=skr->pw_name;
+ else if (!(str=getenv("USER")) && !(str=getenv("LOGNAME")) &&
+ !(str=getenv("LOGIN")))
+ str="UNKNOWN_USER";
+ }
+ ma_strmake(name,str,USERNAME_LENGTH);
+#elif HAVE_CUSERID
+ (void) cuserid(name);
+#else
+ ma_strmake(name,"UNKNOWN_USER", USERNAME_LENGTH);
+#endif
+ }
+ return;
+}
+
+#else /* If MSDOS || VMS */
+
+void read_user_name(char *name)
+{
+ char *str=getenv("USERNAME"); /* ODBC will send user variable */
+ ma_strmake(name,str ? str : "ODBC", USERNAME_LENGTH);
+}
+
+#endif
+
+#ifdef _WIN32
+static my_bool is_NT(void)
+{
+ char *os=getenv("OS");
+ return (os && !strcmp(os, "Windows_NT")) ? 1 : 0;
+}
+#endif
+
+/**************************************************************************
+** Shut down connection
+**************************************************************************/
+
+static void
+end_server(MYSQL *mysql)
+{
+ /* if net->error 2 and reconnect is activated, we need to inforn
+ connection handler */
+ if (mysql->net.pvio != 0)
+ {
+ ma_pvio_close(mysql->net.pvio);
+ mysql->net.pvio= 0; /* Marker */
+ }
+ ma_net_end(&mysql->net);
+ free_old_query(mysql);
+ return;
+}
+
+void mthd_my_skip_result(MYSQL *mysql)
+{
+ ulong pkt_len;
+
+ do {
+ pkt_len= ma_net_safe_read(mysql);
+ if (pkt_len == packet_error)
+ break;
+ } while (pkt_len > 8 || mysql->net.read_pos[0] != 254);
+ return;
+}
+
+void STDCALL
+mysql_free_result(MYSQL_RES *result)
+{
+ if (result)
+ {
+ if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT)
+ {
+ result->handle->methods->db_skip_result(result->handle);
+ result->handle->status=MYSQL_STATUS_READY;
+ }
+ free_rows(result->data);
+ if (result->fields)
+ ma_free_root(&result->field_alloc,MYF(0));
+ if (result->row)
+ free(result->row);
+ free(result);
+ }
+ return;
+}
+
+
+/****************************************************************************
+** Get options from my.cnf
+****************************************************************************/
+enum enum_option_type {
+ MARIADB_OPTION_NONE,
+ MARIADB_OPTION_BOOL,
+ MARIADB_OPTION_INT,
+ MARIADB_OPTION_SIZET,
+ MARIADB_OPTION_STR,
+};
+
+struct st_default_options {
+ enum mysql_option option;
+ enum enum_option_type type;
+ const char *conf_key;
+};
+
+struct st_default_options mariadb_defaults[] =
+{
+ {MARIADB_OPT_PORT, MARIADB_OPTION_INT,"port"},
+ {MARIADB_OPT_UNIXSOCKET, MARIADB_OPTION_STR, "socket"},
+ {MYSQL_OPT_COMPRESS, MARIADB_OPTION_BOOL, "compress"},
+ {MARIADB_OPT_PASSWORD, MARIADB_OPTION_STR, "password"},
+ {MYSQL_OPT_NAMED_PIPE, MARIADB_OPTION_BOOL, "pipe"},
+ {MYSQL_OPT_CONNECT_TIMEOUT, MARIADB_OPTION_INT, "timeout"},
+ {MARIADB_OPT_USER, MARIADB_OPTION_STR, "user"},
+ {MYSQL_INIT_COMMAND, MARIADB_OPTION_STR, "init-command"},
+ {MARIADB_OPT_HOST, MARIADB_OPTION_STR, "host"},
+ {MARIADB_OPT_SCHEMA, MARIADB_OPTION_STR, "database"},
+ {MARIADB_OPT_DEBUG, MARIADB_OPTION_STR, "debug"},
+ {MARIADB_OPT_FOUND_ROWS, MARIADB_OPTION_NONE, "return-found-rows"},
+ {MYSQL_OPT_SSL_KEY, MARIADB_OPTION_STR, "ssl-key"},
+ {MYSQL_OPT_SSL_CERT, MARIADB_OPTION_STR,"ssl-cert"},
+ {MYSQL_OPT_SSL_CA, MARIADB_OPTION_STR,"ssl-ca"},
+ {MYSQL_OPT_SSL_CAPATH, MARIADB_OPTION_STR,"ssl-capath"},
+ {MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MARIADB_OPTION_BOOL,"ssl-verify-server-cert"},
+ {MYSQL_SET_CHARSET_DIR, MARIADB_OPTION_STR, "character-sets-dir"},
+ {MYSQL_SET_CHARSET_NAME, MARIADB_OPTION_STR, "default-character-set"},
+ {MARIADB_OPT_INTERACTIVE, MARIADB_OPTION_NONE, "interactive-timeout"},
+ {MYSQL_OPT_CONNECT_TIMEOUT, MARIADB_OPTION_INT, "connect-timeout"},
+ {MYSQL_OPT_LOCAL_INFILE, MARIADB_OPTION_BOOL, "local-infile"},
+ {0, 0 ,"disable-local-infile",},
+ {MYSQL_OPT_SSL_CIPHER, MARIADB_OPTION_STR, "ssl-cipher"},
+ {MYSQL_OPT_MAX_ALLOWED_PACKET, MARIADB_OPTION_SIZET, "max-allowed-packet"},
+ {MYSQL_OPT_NET_BUFFER_LENGTH, MARIADB_OPTION_SIZET, "net-buffer-length"},
+ {MYSQL_OPT_PROTOCOL, MARIADB_OPTION_INT, "protocol"},
+ {MYSQL_SHARED_MEMORY_BASE_NAME, MARIADB_OPTION_STR,"shared-memory-base-name"},
+ {MARIADB_OPT_MULTI_RESULTS, MARIADB_OPTION_NONE, "multi-results"},
+ {MARIADB_OPT_MULTI_STATEMENTS, MARIADB_OPTION_STR, "multi-statements"},
+ {MARIADB_OPT_MULTI_STATEMENTS, MARIADB_OPTION_STR, "multi-queries"},
+ {MYSQL_SECURE_AUTH, MARIADB_OPTION_BOOL, "secure-auth"},
+ {MYSQL_REPORT_DATA_TRUNCATION, MARIADB_OPTION_BOOL, "report-data-truncation"},
+ {MYSQL_OPT_RECONNECT, MARIADB_OPTION_BOOL, "reconnect"},
+ {MYSQL_PLUGIN_DIR, MARIADB_OPTION_STR, "plugin-dir"},
+ {MYSQL_DEFAULT_AUTH, MARIADB_OPTION_STR, "default-auth"},
+ {MARIADB_OPT_SSL_FP, MARIADB_OPTION_STR, "ssl-fp"},
+ {MARIADB_OPT_SSL_FP_LIST, MARIADB_OPTION_STR, "ssl-fp-list"},
+ {MARIADB_OPT_SSL_FP_LIST, MARIADB_OPTION_STR, "ssl-fplist"},
+ {MARIADB_OPT_TLS_PASSPHRASE, MARIADB_OPTION_STR, "ssl-passphrase"},
+ {MARIADB_OPT_TLS_VERSION, MARIADB_OPTION_STR, "tls_version"},
+ {MYSQL_OPT_BIND, MARIADB_OPTION_STR, "bind-address"},
+ {0, 0, NULL}
+};
+
+#define CHECK_OPT_EXTENSION_SET(OPTS)\
+ if (!(OPTS)->extension) \
+ (OPTS)->extension= (struct st_mysql_options_extension *) \
+ calloc(1, sizeof(struct st_mysql_options_extension));
+
+#define OPT_SET_EXTENDED_VALUE_STR(OPTS, KEY, VAL) \
+ CHECK_OPT_EXTENSION_SET(OPTS) \
+ free((gptr)(OPTS)->extension->KEY); \
+ if((VAL)) \
+ (OPTS)->extension->KEY= strdup((char *)(VAL)); \
+ else \
+ (OPTS)->extension->KEY= NULL
+
+#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL) \
+ CHECK_OPT_EXTENSION_SET(OPTS) \
+ (OPTS)->extension->KEY= (VAL)
+
+#define OPT_SET_EXTENDED_VALUE_INT(A,B,C) OPT_SET_EXTENDED_VALUE(A,B,C)
+
+#define OPT_SET_VALUE_STR(OPTS, KEY, VAL) \
+ free((OPTS)->KEY); \
+ if((VAL)) \
+ (OPTS)->KEY= strdup((char *)(VAL)); \
+ else \
+ (OPTS)->KEY= NULL
+
+#define OPT_SET_VALUE_INT(OPTS, KEY, VAL) \
+ (OPTS)->KEY= (VAL)
+
+static void options_add_initcommand(struct st_mysql_options *options,
+ const char *init_cmd)
+{
+ char *insert= strdup(init_cmd);
+ if (!options->init_command)
+ {
+ options->init_command= (DYNAMIC_ARRAY*)malloc(sizeof(DYNAMIC_ARRAY));
+ ma_init_dynamic_array(options->init_command, sizeof(char*), 5, 5);
+ }
+
+ if (ma_insert_dynamic(options->init_command, (gptr)&insert))
+ free(insert);
+}
+my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value)
+{
+ if (config_option)
+ {
+ int i;
+
+ for (i=0; mariadb_defaults[i].conf_key; i++)
+ {
+ if (!strcmp(mariadb_defaults[i].conf_key, config_option))
+ {
+ my_bool val_bool;
+ int val_int;
+ size_t val_sizet;
+ int rc;
+ void *option_val= NULL;
+ switch (mariadb_defaults[i].type) {
+ case MARIADB_OPTION_BOOL:
+ val_bool= 0;
+ if (config_value)
+ val_bool= atoi(config_value);
+ option_val= &val_bool;
+ break;
+ case MARIADB_OPTION_INT:
+ val_int= 0;
+ if (config_value)
+ val_int= atoi(config_value);
+ option_val= &val_int;
+ break;
+ case MARIADB_OPTION_SIZET:
+ val_sizet= 0;
+ if (config_value)
+ val_sizet= strtol(config_value, NULL, 10);
+ option_val= &val_sizet;
+ break;
+ case MARIADB_OPTION_STR:
+ option_val= (void*)config_value;
+ break;
+ case MARIADB_OPTION_NONE:
+ break;
+ }
+ rc= mysql_optionsv(mysql, mariadb_defaults[i].option, option_val);
+ return(test(rc));
+ }
+ }
+ }
+ /* unknown key */
+ return 1;
+}
+
+/***************************************************************************
+** Change field rows to field structs
+***************************************************************************/
+
+static size_t rset_field_offsets[]= {
+ OFFSET(MYSQL_FIELD, catalog),
+ OFFSET(MYSQL_FIELD, catalog_length),
+ OFFSET(MYSQL_FIELD, db),
+ OFFSET(MYSQL_FIELD, db_length),
+ OFFSET(MYSQL_FIELD, table),
+ OFFSET(MYSQL_FIELD, table_length),
+ OFFSET(MYSQL_FIELD, org_table),
+ OFFSET(MYSQL_FIELD, org_table_length),
+ OFFSET(MYSQL_FIELD, name),
+ OFFSET(MYSQL_FIELD, name_length),
+ OFFSET(MYSQL_FIELD, org_name),
+ OFFSET(MYSQL_FIELD, org_name_length)
+};
+
+MYSQL_FIELD *
+unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields,
+ my_bool default_value, my_bool long_flag_protocol __attribute__((unused)))
+{
+ MYSQL_ROWS *row;
+ MYSQL_FIELD *field,*result;
+ char *p;
+ unsigned int i, field_count= sizeof(rset_field_offsets)/sizeof(size_t)/2;
+
+ field=result=(MYSQL_FIELD*) ma_alloc_root(alloc,sizeof(MYSQL_FIELD)*fields);
+ if (!result)
+ return(0);
+
+ for (row=data->data; row ; row = row->next,field++)
+ {
+ for (i=0; i < field_count; i++)
+ {
+ switch(row->data[i][0]) {
+ case 0:
+ *(char **)(((char *)field) + rset_field_offsets[i*2])= ma_strdup_root(alloc, "");
+ *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])= 0;
+ break;
+ default:
+ *(char **)(((char *)field) + rset_field_offsets[i*2])=
+ ma_strdup_root(alloc, (char *)row->data[i]);
+ *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])=
+ (uint)(row->data[i+1] - row->data[i] - 1);
+ break;
+ }
+ }
+
+ p= (char *)row->data[6];
+ /* filler */
+ field->charsetnr= uint2korr(p);
+ p+= 2;
+ field->length= (uint) uint4korr(p);
+ p+= 4;
+ field->type= (enum enum_field_types)uint1korr(p);
+ p++;
+ field->flags= uint2korr(p);
+ p+= 2;
+ field->decimals= (uint) p[0];
+ p++;
+
+ /* filler */
+ p+= 2;
+
+ if (INTERNAL_NUM_FIELD(field))
+ field->flags|= NUM_FLAG;
+
+ if (default_value && row->data[7])
+ {
+ field->def=ma_strdup_root(alloc,(char*) row->data[7]);
+ }
+ else
+ field->def=0;
+ field->max_length= 0;
+ }
+ free_rows(data); /* Free old data */
+ return(result);
+}
+
+
+/* Read all rows (fields or data) from server */
+
+MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+ uint fields)
+{
+ uint field;
+ ulong pkt_len;
+ ulong len;
+ uchar *cp;
+ char *to, *end_to;
+ MYSQL_DATA *result;
+ MYSQL_ROWS **prev_ptr,*cur;
+ NET *net = &mysql->net;
+
+ if ((pkt_len= ma_net_safe_read(mysql)) == packet_error)
+ return(0);
+ if (!(result=(MYSQL_DATA*) calloc(1, sizeof(MYSQL_DATA))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ ma_init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */
+ result->alloc.min_malloc=sizeof(MYSQL_ROWS);
+ prev_ptr= &result->data;
+ result->rows=0;
+ result->fields=fields;
+
+ while (*(cp=net->read_pos) != 254 || pkt_len >= 8)
+ {
+ result->rows++;
+ if (!(cur= (MYSQL_ROWS*) ma_alloc_root(&result->alloc,
+ sizeof(MYSQL_ROWS))) ||
+ !(cur->data= ((MYSQL_ROW)
+ ma_alloc_root(&result->alloc,
+ (fields+1)*sizeof(char *)+fields+pkt_len))))
+ {
+ free_rows(result);
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ *prev_ptr=cur;
+ prev_ptr= &cur->next;
+ to= (char*) (cur->data+fields+1);
+ end_to=to+fields+pkt_len-1;
+ for (field=0 ; field < fields ; field++)
+ {
+ if ((len=(ulong) net_field_length(&cp)) == NULL_LENGTH)
+ { /* null field */
+ cur->data[field] = 0;
+ }
+ else
+ {
+ cur->data[field] = to;
+ if (len > (ulong) (end_to - to))
+ {
+ free_rows(result);
+ SET_CLIENT_ERROR(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ memcpy(to,(char*) cp,len); to[len]=0;
+ to+=len+1;
+ cp+=len;
+ if (mysql_fields)
+ {
+ if (mysql_fields[field].max_length < len)
+ mysql_fields[field].max_length=len;
+ }
+ }
+ }
+ cur->data[field]=to; /* End of last field */
+ if ((pkt_len=ma_net_safe_read(mysql)) == packet_error)
+ {
+ free_rows(result);
+ return(0);
+ }
+ }
+ *prev_ptr=0; /* last pointer is null */
+ /* save status */
+ if (pkt_len > 1)
+ {
+ cp++;
+ mysql->warning_count= uint2korr(cp);
+ cp+= 2;
+ mysql->server_status= uint2korr(cp);
+ }
+ return(result);
+}
+
+
+/*
+** Read one row. Uses packet buffer as storage for fields.
+** When next packet is read, the previous field values are destroyed
+*/
+
+
+int mthd_my_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
+{
+ uint field;
+ ulong pkt_len,len;
+ uchar *pos,*prev_pos, *end_pos;
+
+ if ((pkt_len=(uint) ma_net_safe_read(mysql)) == packet_error)
+ return -1;
+
+ if (pkt_len <= 8 && mysql->net.read_pos[0] == 254)
+ {
+ mysql->warning_count= uint2korr(mysql->net.read_pos + 1);
+ mysql->server_status= uint2korr(mysql->net.read_pos + 3);
+ return 1; /* End of data */
+ }
+ prev_pos= 0; /* allowed to write at packet[-1] */
+ pos=mysql->net.read_pos;
+ end_pos=pos+pkt_len;
+ for (field=0 ; field < fields ; field++)
+ {
+ if ((len=(ulong) net_field_length(&pos)) == NULL_LENGTH)
+ { /* null field */
+ row[field] = 0;
+ *lengths++=0;
+ }
+ else
+ {
+ if (len > (ulong) (end_pos - pos))
+ {
+ mysql->net.last_errno=CR_UNKNOWN_ERROR;
+ strcpy(mysql->net.last_error,ER(mysql->net.last_errno));
+ return -1;
+ }
+ row[field] = (char*) pos;
+ pos+=len;
+ *lengths++=len;
+ }
+ if (prev_pos)
+ *prev_pos=0; /* Terminate prev field */
+ prev_pos=pos;
+ }
+ row[field]=(char*) prev_pos+1; /* End of last field */
+ *prev_pos=0; /* Terminate last field */
+ return 0;
+}
+
+/****************************************************************************
+** Init MySQL structure or allocate one
+****************************************************************************/
+
+MYSQL * STDCALL
+mysql_init(MYSQL *mysql)
+{
+ if (mysql_server_init(0, NULL, NULL))
+ return NULL;
+ if (!mysql)
+ {
+ if (!(mysql=(MYSQL*) calloc(1, sizeof(MYSQL))))
+ return 0;
+ mysql->free_me=1;
+ mysql->net.pvio= 0;
+ mysql->net.extension= 0;
+ }
+ else
+ {
+ memset((char*) (mysql), 0, sizeof(*(mysql)));
+ mysql->net.pvio= 0;
+ mysql->net.extension= 0;
+ }
+
+ if (!(mysql->net.extension= (struct st_mariadb_net_extension *)
+ calloc(1, sizeof(struct st_mariadb_net_extension))) ||
+ !(mysql->extension= (struct st_mariadb_extension *)
+ calloc(1, sizeof(struct st_mariadb_extension))))
+ goto error;
+ mysql->options.report_data_truncation= 1;
+ mysql->options.connect_timeout=CONNECT_TIMEOUT;
+ mysql->charset= ma_default_charset_info;
+ mysql->methods= &MARIADB_DEFAULT_METHODS;
+ strcpy(mysql->net.sqlstate, "00000");
+ mysql->net.last_error[0]= mysql->net.last_errno= 0;
+
+/*
+ Only enable LOAD DATA INFILE by default if configured with
+ --enable-local-infile
+*/
+#ifdef ENABLED_LOCAL_INFILE
+ mysql->options.client_flag|= CLIENT_LOCAL_FILES;
+#endif
+ mysql->options.reconnect= 0;
+ return mysql;
+error:
+ if (mysql->free_me)
+ free(mysql);
+ return 0;
+}
+
+int STDCALL
+mysql_ssl_set(MYSQL *mysql __attribute__((unused)),
+ const char *key __attribute__((unused)),
+ const char *cert __attribute__((unused)),
+ const char *ca __attribute__((unused)),
+ const char *capath __attribute__((unused)),
+ const char *cipher __attribute__((unused)))
+{
+#ifdef HAVE_TLS
+ char enable= 1;
+ return (mysql_optionsv(mysql, MYSQL_OPT_SSL_ENFORCE, &enable) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_KEY, key) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CERT, cert) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CA, ca) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CAPATH, capath) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CIPHER, cipher)) ? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+/**************************************************************************
+**************************************************************************/
+
+const char * STDCALL
+mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused)))
+{
+#ifdef HAVE_TLS
+ if (mysql->net.pvio && mysql->net.pvio->ctls)
+ {
+ return ma_pvio_tls_cipher(mysql->net.pvio->ctls);
+ }
+#endif
+ return(NULL);
+}
+
+/**************************************************************************
+** Free strings in the SSL structure and clear 'use_ssl' flag.
+** NB! Errors are not reported until you do mysql_real_connect.
+**************************************************************************/
+
+char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer)
+{
+ if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS)
+ {
+ buffer= (unsigned char *)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))
+ {
+ uint 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= strlen((char *)p);
+ buffer= mysql_net_store_length(buffer, len);
+ memcpy(buffer, p, len);
+ buffer+= (len);
+ p+= (len + 1);
+ len= strlen((char *)p);
+ buffer= mysql_net_store_length(buffer, len);
+ memcpy(buffer, p, len);
+ buffer+= len;
+ }
+ }
+ }
+ return (char *)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_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "libmariadb")
+ + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_version", MARIADB_PACKAGE_VERSION)
+ + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_os", MARIADB_SYSTEM_TYPE);
+
+#ifdef _WIN32
+ snprintf(buffer, 255, "%lu", (ulong) GetCurrentThreadId());
+ rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buffer);
+ snprintf(buffer, 255, "%lu", (ulong) GetCurrentProcessId());
+#else
+ snprintf(buffer, 255, "%lu", (ulong) getpid());
+#endif
+ rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buffer);
+
+ rc+= mysql_optionsv(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()
+** before calling mysql_real_connect !
+*/
+
+MYSQL * STDCALL
+mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
+ const char *passwd, const char *db,
+ uint port, const char *unix_socket,unsigned long client_flag)
+{
+ char *end= NULL;
+ char *connection_handler= (mysql->options.extension) ?
+ mysql->options.extension->connection_handler : 0;
+
+ if (!mysql->methods)
+ mysql->methods= &MARIADB_DEFAULT_METHODS;
+
+ if (connection_handler ||
+ (host && (end= strstr(host, "://"))))
+ {
+ MARIADB_CONNECTION_PLUGIN *plugin;
+ char plugin_name[64];
+
+ if (!connection_handler || !connection_handler[0])
+ {
+ memset(plugin_name, 0, 64);
+ ma_strmake(plugin_name, host, MIN(end - host, 63));
+ end+= 3;
+ }
+ else
+ ma_strmake(plugin_name, connection_handler, MIN(63, strlen(connection_handler)));
+
+ if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN)))
+ return NULL;
+
+ if (!(mysql->extension->conn_hdlr= (MA_CONNECTION_HANDLER *)calloc(1, sizeof(MA_CONNECTION_HANDLER))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return NULL;
+ }
+
+ /* save URL for reconnect */
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, url, host);
+
+ mysql->extension->conn_hdlr->plugin= plugin;
+
+ if (plugin && plugin->connect)
+ {
+ MYSQL *my= plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag);
+ if (!my)
+ {
+ free(mysql->extension->conn_hdlr);
+ mysql->extension->conn_hdlr= NULL;
+ }
+ return my;
+ }
+ }
+
+ return mysql->methods->db_connect(mysql, host, user, passwd,
+ db, port, unix_socket, client_flag);
+}
+
+MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
+ const char *passwd, const char *db,
+ uint port, const char *unix_socket, unsigned long client_flag)
+{
+ char buff[NAME_LEN+USERNAME_LENGTH+100];
+ char *end, *end_pkt, *host_info,
+ *charset_name= NULL;
+ MA_PVIO_CINFO cinfo= {NULL, NULL, 0, -1, NULL};
+ MARIADB_PVIO *pvio= NULL;
+ char *scramble_data;
+ my_bool is_maria= 0;
+ const char *scramble_plugin;
+ uint pkt_length, scramble_len, pkt_scramble_len= 0;
+ NET *net= &mysql->net;
+
+ if (!mysql->methods)
+ mysql->methods= &MARIADB_DEFAULT_METHODS;
+
+ ma_set_connect_attrs(mysql);
+
+ if (net->pvio) /* check if we are already connected */
+ {
+ SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+ /* use default options */
+ if (mysql->options.my_cnf_file || mysql->options.my_cnf_group)
+ {
+ _mariadb_read_options(mysql,
+ (mysql->options.my_cnf_file ?
+ mysql->options.my_cnf_file : NULL),
+ mysql->options.my_cnf_group);
+ free(mysql->options.my_cnf_file);
+ free(mysql->options.my_cnf_group);
+ mysql->options.my_cnf_file=mysql->options.my_cnf_group=0;
+ }
+
+#ifndef WIN32
+ if (mysql->options.protocol > MYSQL_PROTOCOL_SOCKET)
+ {
+ SET_CLIENT_ERROR(mysql, CR_CONN_UNKNOWN_PROTOCOL, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+#endif
+
+ /* Some empty-string-tests are done because of ODBC */
+ if (!host || !host[0])
+ host=mysql->options.host;
+ if (!user || !user[0])
+ user=mysql->options.user;
+ if (!passwd)
+ {
+ passwd=mysql->options.password;
+#ifndef DONT_USE_MYSQL_PWD
+ if (!passwd)
+ passwd=getenv("MYSQL_PWD"); /* get it from environment (haneke) */
+ if (!passwd)
+ passwd= "";
+#endif
+ }
+ if (!db || !db[0])
+ db=mysql->options.db;
+ if (!port)
+ port=mysql->options.port;
+ if (!unix_socket)
+ unix_socket=mysql->options.unix_socket;
+
+ mysql->server_status=SERVER_STATUS_AUTOCOMMIT;
+
+ /* try to connect via pvio_init */
+ cinfo.host= host;
+ cinfo.unix_socket= unix_socket;
+ cinfo.port= port;
+ cinfo.mysql= mysql;
+
+ /*
+ ** Grab a socket and connect it to the server
+ */
+#ifndef _WIN32
+#if defined(HAVE_SYS_UN_H)
+ if ((!host || strcmp(host,LOCAL_HOST) == 0) &&
+ mysql->options.protocol != MYSQL_PROTOCOL_TCP &&
+ (unix_socket || mysql_unix_port))
+ {
+ cinfo.host= LOCAL_HOST;
+ cinfo.unix_socket= (unix_socket) ? unix_socket : mysql_unix_port;
+ cinfo.type= PVIO_TYPE_UNIXSOCKET;
+ sprintf(host_info=buff,ER(CR_LOCALHOST_CONNECTION),cinfo.host);
+ }
+ else
+#endif
+#else
+ if (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY)
+ {
+ cinfo.host= mysql->options.shared_memory_base_name;
+ cinfo.type= PVIO_TYPE_SHAREDMEM;
+ sprintf(host_info=buff,ER(CR_SHARED_MEMORY_CONNECTION), cinfo.host ? cinfo.host : SHM_DEFAULT_NAME);
+ }
+ /* named pipe */
+ else if (mysql->options.protocol == MYSQL_PROTOCOL_PIPE ||
+ (host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0))
+ {
+ cinfo.type= PVIO_TYPE_NAMEDPIPE;
+ sprintf(host_info=buff,ER(CR_NAMEDPIPE_CONNECTION),cinfo.host);
+ }
+ else
+#endif
+ {
+ cinfo.unix_socket=0; /* This is not used */
+ if (!port)
+ port=mysql_port;
+ if (!host)
+ host=LOCAL_HOST;
+ cinfo.host= host;
+ cinfo.port= port;
+ cinfo.type= PVIO_TYPE_SOCKET;
+ sprintf(host_info=buff,ER(CR_TCP_CONNECTION), cinfo.host);
+ }
+ /* Initialize and load pvio plugin */
+ if (!(pvio= ma_pvio_init(&cinfo)))
+ goto error;
+
+ /* try to connect */
+ if (ma_pvio_connect(pvio, &cinfo) != 0)
+ {
+ ma_pvio_close(pvio);
+ goto error;
+ }
+
+ if (mysql->options.extension && mysql->options.extension->proxy_header)
+ {
+ char *hdr = mysql->options.extension->proxy_header;
+ size_t len = mysql->options.extension->proxy_header_len;
+ if (ma_pvio_write(pvio, (unsigned char *)hdr, len) <= 0)
+ {
+ ma_pvio_close(pvio);
+ goto error;
+ }
+ }
+
+ if (ma_net_init(net, pvio))
+ goto error;
+
+ if (mysql->options.max_allowed_packet)
+ net->max_packet_size= mysql->options.max_allowed_packet;
+
+ ma_pvio_keepalive(net->pvio);
+ strcpy(mysql->net.sqlstate, "00000");
+
+ /* Get version info */
+ mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */
+/*
+ if (ma_pvio_wait_io_or_timeout(net->pvio, FALSE, 0) < 1)
+ {
+ my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "handshake: waiting for inital communication packet",
+ errno);
+ goto error;
+ }
+ */
+ if ((pkt_length=ma_net_safe_read(mysql)) == packet_error)
+ {
+ if (mysql->net.last_errno == CR_SERVER_LOST)
+ my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "handshake: reading inital communication packet",
+ errno);
+
+ goto error;
+ }
+ end= (char *)net->read_pos;
+ end_pkt= (char *)net->read_pos + pkt_length;
+
+ /* Check if version of protocol matches current one */
+
+ mysql->protocol_version= end[0];
+ end++;
+
+ /* Check if server sends an error */
+ if (mysql->protocol_version == 0XFF)
+ {
+ net_get_error(end, pkt_length - 1, net->last_error, sizeof(net->last_error),
+ &net->last_errno, net->sqlstate);
+ /* fix for bug #26426 */
+ if (net->last_errno == 1040)
+ memcpy(net->sqlstate, "08004", SQLSTATE_LENGTH);
+ goto error;
+ }
+
+ if (mysql->protocol_version < PROTOCOL_VERSION)
+ {
+ net->last_errno= CR_VERSION_ERROR;
+ sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version,
+ PROTOCOL_VERSION);
+ goto error;
+ }
+ /* Save connection information */
+ if (!user) user="";
+ if (!passwd) passwd="";
+
+ if (!(mysql->host_info= strdup(host_info ? host_info : "")) ||
+ !(mysql->host= strdup(cinfo.host ? cinfo.host : "")) ||
+ !(mysql->user=strdup(user)) ||
+ !(mysql->passwd=strdup(passwd)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ if (cinfo.unix_socket)
+ mysql->unix_socket= strdup(cinfo.unix_socket);
+ else
+ mysql->unix_socket=0;
+ mysql->port=port;
+ client_flag|=mysql->options.client_flag;
+
+ if (strncmp(end, MA_RPL_VERSION_HACK, sizeof(MA_RPL_VERSION_HACK) - 1) == 0)
+ {
+ mysql->server_version= strdup(end + sizeof(MA_RPL_VERSION_HACK) - 1);
+ is_maria= 1;
+ }
+ else
+ {
+ if (!(mysql->server_version= strdup(end)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ }
+ end+= strlen(end) + 1;
+
+ mysql->thread_id=uint4korr(end);
+ end+=4;
+
+ /* This is the first part of scramble packet. In 4.1 and later
+ a second package will follow later */
+ scramble_data= end;
+ scramble_len= SCRAMBLE_LENGTH_323 + 1;
+ scramble_plugin= old_password_plugin_name;
+ end+= SCRAMBLE_LENGTH_323;
+
+ /* 1st pad */
+ end++;
+
+ if (end + 1<= end_pkt)
+ {
+ mysql->server_capabilities=uint2korr(end);
+ }
+
+ /* mysql 5.5 protocol */
+ if (end + 18 <= end_pkt)
+ {
+ mysql->server_language= uint1korr(end + 2);
+ mysql->server_status= uint2korr(end + 3);
+ mysql->server_capabilities|= (unsigned int)(uint2korr(end + 5) << 16);
+ pkt_scramble_len= uint1korr(end + 7);
+
+ /* check if MariaD2B specific capabilities are available */
+ if (is_maria && !(mysql->server_capabilities & CLIENT_MYSQL))
+ {
+ mysql->extension->mariadb_server_capabilities= (ulonglong) uint4korr(end + 14);
+ }
+ }
+
+ /* pad 2 */
+ end+= 18;
+
+ /* second scramble package */
+ if (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 <= end_pkt)
+ {
+ memcpy(end - SCRAMBLE_LENGTH_323, scramble_data, SCRAMBLE_LENGTH_323);
+ scramble_data= end - SCRAMBLE_LENGTH_323;
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ scramble_len= pkt_scramble_len;
+ scramble_plugin= scramble_data + scramble_len;
+ if (scramble_data + scramble_len > end_pkt)
+ scramble_len= (uint)(end_pkt - scramble_data);
+ } else
+ {
+ scramble_len= (uint)(end_pkt - scramble_data);
+ scramble_plugin= native_password_plugin_name;
+ }
+ } else
+ {
+ mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
+ if (mysql->options.secure_auth)
+ {
+ SET_CLIENT_ERROR(mysql, CR_SECURE_AUTH, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ }
+
+ /* Set character set */
+ if (mysql->options.charset_name)
+ mysql->charset= mysql_find_charset_name(mysql->options.charset_name);
+ else if (mysql->server_language)
+ mysql->charset= mysql_find_charset_nr(mysql->server_language);
+ else
+ mysql->charset=ma_default_charset_info;
+
+ if (!mysql->charset)
+ {
+ net->last_errno=CR_CANT_READ_CHARSET;
+ sprintf(net->last_error,ER(net->last_errno),
+ charset_name ? charset_name : "unknown",
+ "compiled_in");
+ goto error;
+ }
+
+ mysql->client_flag= client_flag;
+
+ if (run_plugin_auth(mysql, scramble_data, scramble_len,
+ scramble_plugin, db))
+ goto error;
+
+ if (mysql->client_flag & CLIENT_COMPRESS)
+ net->compress= 1;
+
+ /* last part: select default db */
+ if (!(mysql->server_capabilities & CLIENT_CONNECT_WITH_DB) &&
+ (db && !mysql->db))
+ {
+ if (mysql_select_db(mysql, db))
+ {
+ my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "Setting intital database",
+ errno);
+ goto error;
+ }
+ }
+
+ if (mysql->options.init_command)
+ {
+ char **begin= (char **)mysql->options.init_command->buffer;
+ char **end= begin + mysql->options.init_command->elements;
+
+ /* Avoid reconnect in mysql_real_connect */
+ my_bool save_reconnect= mysql->options.reconnect;
+ mysql->options.reconnect= 0;
+
+ for (;begin < end; begin++)
+ {
+ if (mysql_real_query(mysql, *begin, (unsigned long)strlen(*begin)))
+ goto error;
+
+ /* check if query produced a result set */
+ do {
+ MYSQL_RES *res;
+ if ((res= mysql_use_result(mysql)))
+ mysql_free_result(res);
+ } while (!mysql_next_result(mysql));
+ }
+ mysql->options.reconnect= save_reconnect;
+ }
+
+ strcpy(mysql->net.sqlstate, "00000");
+
+ /* connection established, apply timeouts */
+ ma_pvio_set_timeout(mysql->net.pvio, PVIO_READ_TIMEOUT, mysql->options.read_timeout);
+ ma_pvio_set_timeout(mysql->net.pvio, PVIO_WRITE_TIMEOUT, mysql->options.write_timeout);
+ return(mysql);
+
+error:
+ /* Free alloced memory */
+ end_server(mysql);
+ /* only free the allocated memory, user needs to call mysql_close */
+ mysql_close_memory(mysql);
+ if (!(client_flag & CLIENT_REMEMBER_OPTIONS) &&
+ !mysql->options.extension->async_context)
+ mysql_close_options(mysql);
+ return(0);
+}
+
+struct my_hook_data {
+ MYSQL *orig_mysql;
+ MYSQL *new_mysql;
+ /* This is always NULL currently, but restoring does not hurt just in case. */
+ MARIADB_PVIO *orig_pvio;
+};
+/*
+ Callback hook to make the new VIO accessible via the old MYSQL to calling
+ application when suspending a non-blocking call during automatic reconnect.
+*/
+static void
+my_suspend_hook(my_bool suspend, void *data)
+{
+ struct my_hook_data *hook_data= (struct my_hook_data *)data;
+ if (suspend)
+ {
+ hook_data->orig_pvio= hook_data->orig_mysql->net.pvio;
+ hook_data->orig_mysql->net.pvio= hook_data->new_mysql->net.pvio;
+ }
+ else
+ hook_data->orig_mysql->net.pvio= hook_data->orig_pvio;
+}
+
+my_bool STDCALL mariadb_reconnect(MYSQL *mysql)
+{
+ MYSQL tmp_mysql;
+ struct my_hook_data hook_data;
+ struct mysql_async_context *ctxt= NULL;
+ LIST *li_stmt= mysql->stmts;
+
+ /* check if connection handler is active */
+ if (IS_CONNHDLR_ACTIVE(mysql))
+ {
+ if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reconnect)
+ return(mysql->extension->conn_hdlr->plugin->reconnect(mysql));
+ }
+
+ if (!mysql->options.reconnect ||
+ (mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info)
+ {
+ /* Allow reconnect next time */
+ mysql->server_status&= ~SERVER_STATUS_IN_TRANS;
+ my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ mysql_init(&tmp_mysql);
+ tmp_mysql.options=mysql->options;
+ if (mysql->extension->conn_hdlr)
+ {
+ tmp_mysql.extension->conn_hdlr= mysql->extension->conn_hdlr;
+ mysql->extension->conn_hdlr= 0;
+ }
+
+ /* don't reread options from configuration files */
+ tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL;
+ if (IS_MYSQL_ASYNC_ACTIVE(mysql))
+ {
+ ctxt= mysql->options.extension->async_context;
+ hook_data.orig_mysql= mysql;
+ hook_data.new_mysql= &tmp_mysql;
+ hook_data.orig_pvio= mysql->net.pvio;
+ my_context_install_suspend_resume_hook(ctxt, my_suspend_hook, &hook_data);
+ }
+
+ if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd,
+ mysql->db, mysql->port, mysql->unix_socket,
+ mysql->client_flag | CLIENT_REMEMBER_OPTIONS) ||
+ mysql_set_character_set(&tmp_mysql, mysql->charset->csname))
+ {
+ if (ctxt)
+ my_context_install_suspend_resume_hook(ctxt, NULL, NULL);
+ /* don't free options (CONC-118) */
+ memset(&tmp_mysql.options, 0, sizeof(struct st_mysql_options));
+ my_set_error(mysql, tmp_mysql.net.last_errno,
+ tmp_mysql.net.sqlstate,
+ tmp_mysql.net.last_error);
+ mysql_close(&tmp_mysql);
+ return(1);
+ }
+
+ for (;li_stmt;li_stmt= li_stmt->next)
+ {
+ MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data;
+
+ if (stmt->state != MYSQL_STMT_INITTED)
+ {
+ stmt->state= MYSQL_STMT_INITTED;
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ }
+ }
+
+ tmp_mysql.free_me= mysql->free_me;
+ tmp_mysql.stmts= mysql->stmts;
+ mysql->stmts= NULL;
+
+ if (ctxt)
+ my_context_install_suspend_resume_hook(ctxt, NULL, NULL);
+ /* Don't free options, we moved them to tmp_mysql */
+ memset(&mysql->options, 0, sizeof(mysql->options));
+ mysql->free_me=0;
+ mysql_close(mysql);
+ *mysql=tmp_mysql;
+ mysql->net.pvio->mysql= mysql;
+ ma_net_clear(&mysql->net);
+ mysql->affected_rows= ~(unsigned long long) 0;
+ mysql->info= 0;
+ return(0);
+}
+
+void ma_invalidate_stmts(MYSQL *mysql, const char *function_name)
+{
+ if (mysql->stmts)
+ {
+ LIST *li_stmt= mysql->stmts;
+
+ for (; li_stmt; li_stmt= li_stmt->next)
+ {
+ MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data;
+ stmt->mysql= NULL;
+ SET_CLIENT_STMT_ERROR(stmt, CR_STMT_CLOSED, SQLSTATE_UNKNOWN, function_name);
+ }
+ mysql->stmts= NULL;
+ }
+}
+
+/*
+ Legacy support of the MariaDB 5.5 version, where timeouts where only in
+ seconds resolution. Applications that use this will be asked to set a timeout
+ at the nearest higher whole-seconds value.
+*/
+unsigned int STDCALL
+mysql_get_timeout_value(const MYSQL *mysql)
+{
+ unsigned int timeout= mysql->options.extension->async_context->timeout_value;
+ /* Avoid overflow. */
+ if (timeout > UINT_MAX - 999)
+ return (timeout - 1)/1000 + 1;
+ else
+ return (timeout+999)/1000;
+}
+
+
+unsigned int STDCALL
+mysql_get_timeout_value_ms(const MYSQL *mysql)
+{
+ return mysql->options.extension->async_context->timeout_value;
+}
+
+/**************************************************************************
+** Change user and database
+**************************************************************************/
+
+my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
+ const char *passwd, const char *db)
+{
+ const MARIADB_CHARSET_INFO *s_cs= mysql->charset;
+ char *s_user= mysql->user,
+ *s_passwd= mysql->passwd,
+ *s_db= mysql->db;
+ int rc;
+
+ if (!user)
+ user="";
+ if (!passwd)
+ passwd="";
+ if (!db)
+ db="";
+
+ if (mysql->options.charset_name)
+ mysql->charset =mysql_find_charset_name(mysql->options.charset_name);
+ else if (mysql->server_language)
+ mysql->charset=mysql_find_charset_nr(mysql->server_language);
+ else
+ mysql->charset=ma_default_charset_info;
+
+ mysql->user= strdup(user ? user : "");
+ mysql->passwd= strdup(passwd ? passwd : "");
+
+ /* db will be set in run_plugin_auth */
+ mysql->db= 0;
+ rc= run_plugin_auth(mysql, 0, 0, 0, db);
+
+ /* COM_CHANGE_USER always releases prepared statements, so we need to invalidate them */
+ ma_invalidate_stmts(mysql, "mysql_change_user()");
+
+ if (rc==0)
+ {
+ free(s_user);
+ free(s_passwd);
+ free(s_db);
+
+ if (db && !(mysql->db= strdup(db)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ rc= 1;
+ }
+ } else
+ {
+ free(mysql->user);
+ free(mysql->passwd);
+ free(mysql->db);
+
+ mysql->user= s_user;
+ mysql->passwd= s_passwd;
+ mysql->db= s_db;
+ mysql->charset= s_cs;
+ }
+ return(rc);
+}
+
+
+/**************************************************************************
+** Set current database
+**************************************************************************/
+
+int STDCALL
+mysql_select_db(MYSQL *mysql, const char *db)
+{
+ int error;
+
+ if ((error=ma_simple_command(mysql, COM_INIT_DB,db,(uint) strlen(db),0,0)))
+ return(error);
+ free(mysql->db);
+ mysql->db=strdup(db);
+ return(0);
+}
+
+
+/*************************************************************************
+** Send a QUIT to the server and close the connection
+** If handle is alloced by mysql connect free it.
+*************************************************************************/
+
+static void mysql_close_options(MYSQL *mysql)
+{
+ if (mysql->options.init_command)
+ {
+ char **begin= (char **)mysql->options.init_command->buffer;
+ char **end= begin + mysql->options.init_command->elements;
+
+ for (;begin < end; begin++)
+ free(*begin);
+ ma_delete_dynamic(mysql->options.init_command);
+ free(mysql->options.init_command);
+ }
+ free(mysql->options.user);
+ free(mysql->options.host);
+ free(mysql->options.password);
+ free(mysql->options.unix_socket);
+ free(mysql->options.db);
+ free(mysql->options.my_cnf_file);
+ free(mysql->options.my_cnf_group);
+ free(mysql->options.charset_dir);
+ free(mysql->options.charset_name);
+ free(mysql->options.bind_address);
+ free(mysql->options.ssl_key);
+ free(mysql->options.ssl_cert);
+ free(mysql->options.ssl_ca);
+ free(mysql->options.ssl_capath);
+ free(mysql->options.ssl_cipher);
+
+ if (mysql->options.extension)
+ {
+ struct mysql_async_context *ctxt;
+ if ((ctxt = mysql->options.extension->async_context))
+ {
+ my_context_destroy(&ctxt->async_context);
+ free(ctxt);
+ mysql->options.extension->async_context= 0;
+ }
+ free(mysql->options.extension->plugin_dir);
+ free(mysql->options.extension->default_auth);
+ free(mysql->options.extension->db_driver);
+ free(mysql->options.extension->ssl_crl);
+ free(mysql->options.extension->ssl_crlpath);
+ free(mysql->options.extension->tls_fp);
+ free(mysql->options.extension->tls_fp_list);
+ free(mysql->options.extension->tls_pw);
+ free(mysql->options.extension->tls_version);
+ free(mysql->options.extension->url);
+ free(mysql->options.extension->connection_handler);
+ if(hash_inited(&mysql->options.extension->connect_attrs))
+ hash_free(&mysql->options.extension->connect_attrs);
+ if (hash_inited(&mysql->options.extension->userdata))
+ hash_free(&mysql->options.extension->userdata);
+
+ }
+ free(mysql->options.extension);
+ /* clear all pointer */
+ memset(&mysql->options, 0, sizeof(mysql->options));
+}
+
+static void mysql_close_memory(MYSQL *mysql)
+{
+ free(mysql->host_info);
+ free(mysql->host);
+ free(mysql->user);
+ free(mysql->passwd);
+ free(mysql->db);
+ free(mysql->unix_socket);
+ free(mysql->server_version);
+ mysql->host_info= mysql->host= mysql->unix_socket=
+ mysql->server_version=mysql->user=mysql->passwd=mysql->db=0;
+}
+
+void my_set_error(MYSQL *mysql,
+ unsigned int error_nr,
+ const char *sqlstate,
+ const char *format,
+ ...)
+{
+ va_list ap;
+
+ mysql->net.last_errno= error_nr;
+ ma_strmake(mysql->net.sqlstate, sqlstate, SQLSTATE_LENGTH);
+ va_start(ap, format);
+ vsnprintf(mysql->net.last_error, MYSQL_ERRMSG_SIZE,
+ format ? format : ER(error_nr), ap);
+ va_end(ap);
+ return;
+}
+
+void mysql_close_slow_part(MYSQL *mysql)
+{
+ if (mysql->net.pvio)
+ {
+ free_old_query(mysql);
+ mysql->status=MYSQL_STATUS_READY; /* Force command */
+ mysql->options.reconnect=0;
+ if (mysql->net.pvio && mysql->net.buff)
+ ma_simple_command(mysql, COM_QUIT,NullS,0,1,0);
+ end_server(mysql);
+ }
+}
+
+void ma_clear_session_state(MYSQL *mysql)
+{
+ uint i;
+
+ if (!mysql || !mysql->extension)
+ return;
+
+ for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++)
+ {
+ /* we acquired memory via ma_multi_alloc, so we don't need to free data */
+ list_free(mysql->extension->session_state[i].list, 0);
+ }
+ memset(mysql->extension->session_state, 0, sizeof(struct st_mariadb_session_state) * SESSION_TRACK_TYPES);
+}
+
+void STDCALL
+mysql_close(MYSQL *mysql)
+{
+ if (mysql) /* Some simple safety */
+ {
+ if (mysql->extension && mysql->extension->conn_hdlr)
+ {
+ MA_CONNECTION_HANDLER *p= mysql->extension->conn_hdlr;
+ p->plugin->close(mysql);
+ free(p);
+ }
+
+ if (mysql->methods)
+ mysql->methods->db_close(mysql);
+
+ /* reset the connection in all active statements */
+ ma_invalidate_stmts(mysql, "mysql_close()");
+
+ mysql_close_memory(mysql);
+ mysql_close_options(mysql);
+ ma_clear_session_state(mysql);
+
+ if (mysql->net.extension)
+ free(mysql->net.extension);
+
+ mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
+
+ /* Clear pointers for better safety */
+ memset((char*) &mysql->options, 0, sizeof(mysql->options));
+
+ if (mysql->extension)
+ free(mysql->extension);
+
+ mysql->net.pvio= 0;
+ if (mysql->free_me)
+ free(mysql);
+ }
+ return;
+}
+
+
+/**************************************************************************
+** Do a query. If query returned rows, free old rows.
+** Read data by mysql_store_result or by repeating calls to mysql_fetch_row
+**************************************************************************/
+
+int STDCALL
+mysql_query(MYSQL *mysql, const char *query)
+{
+ return mysql_real_query(mysql,query, (unsigned long) strlen(query));
+}
+
+/*
+ Send the query and return so we can do something else.
+ Needs to be followed by mysql_read_query_result() when we want to
+ finish processing it.
+*/
+
+int STDCALL
+mysql_send_query(MYSQL* mysql, const char* query, unsigned long length)
+{
+ return ma_simple_command(mysql, COM_QUERY, query, length, 1,0);
+}
+
+int mthd_my_read_query_result(MYSQL *mysql)
+{
+ uchar *pos;
+ ulong field_count;
+ MYSQL_DATA *fields;
+ ulong length;
+
+ if (!mysql || (length = ma_net_safe_read(mysql)) == packet_error)
+ {
+ return(1);
+ }
+ free_old_query(mysql); /* Free old result */
+get_info:
+ pos=(uchar*) mysql->net.read_pos;
+ if ((field_count= net_field_length(&pos)) == 0)
+ {
+ size_t item_len;
+ mysql->affected_rows= net_field_length_ll(&pos);
+ mysql->insert_id= net_field_length_ll(&pos);
+ mysql->server_status=uint2korr(pos);
+ pos+=2;
+ mysql->warning_count=uint2korr(pos);
+ pos+=2;
+ if (pos < mysql->net.read_pos+length)
+ {
+ if ((item_len= net_field_length(&pos)))
+ mysql->info=(char*) pos;
+
+ /* check if server supports session tracking */
+ if (mysql->server_capabilities & CLIENT_SESSION_TRACKING)
+ {
+ ma_clear_session_state(mysql);
+ pos+= item_len;
+
+ if (mysql->server_status & SERVER_SESSION_STATE_CHANGED)
+ {
+ int i;
+ if (pos < mysql->net.read_pos + length)
+ {
+ LIST *session_item;
+ MYSQL_LEX_STRING *str= NULL;
+ enum enum_session_state_type si_type;
+ uchar *old_pos= pos;
+ size_t item_len= net_field_length(&pos); /* length for all items */
+
+ /* length was already set, so make sure that info will be zero terminated */
+ if (mysql->info)
+ *old_pos= 0;
+
+ while (item_len > 0)
+ {
+ size_t plen;
+ char *data;
+ old_pos= pos;
+ si_type= (enum enum_session_state_type)net_field_length(&pos);
+ switch(si_type) {
+ case SESSION_TRACK_SCHEMA:
+ case SESSION_TRACK_STATE_CHANGE:
+ case SESSION_TRACK_TRANSACTION_CHARACTERISTICS:
+ case SESSION_TRACK_SYSTEM_VARIABLES:
+ net_field_length(&pos); /* ignore total length, item length will follow next */
+ plen= net_field_length(&pos);
+ if (!ma_multi_malloc(0,
+ &session_item, sizeof(LIST),
+ &str, sizeof(MYSQL_LEX_STRING),
+ &data, plen,
+ NULL))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return -1;
+ }
+ str->length= plen;
+ str->str= data;
+ memcpy(str->str, (char *)pos, plen);
+ pos+= plen;
+ session_item->data= str;
+ mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item);
+
+ /* in case schema has changed, we have to update mysql->db */
+ if (si_type == SESSION_TRACK_SCHEMA)
+ {
+ free(mysql->db);
+ mysql->db= malloc(plen + 1);
+ memcpy(mysql->db, str->str, plen);
+ mysql->db[plen]= 0;
+ }
+ else if (si_type == SESSION_TRACK_SYSTEM_VARIABLES)
+ {
+ my_bool set_charset= 0;
+ /* make sure that we update charset in case it has changed */
+ if (!strncmp(str->str, "character_set_client", str->length))
+ set_charset= 1;
+ plen= net_field_length(&pos);
+ if (!ma_multi_malloc(0,
+ &session_item, sizeof(LIST),
+ &str, sizeof(MYSQL_LEX_STRING),
+ &data, plen,
+ NULL))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return -1;
+ }
+ str->length= plen;
+ str->str= data;
+ memcpy(str->str, (char *)pos, plen);
+ pos+= plen;
+ session_item->data= str;
+ mysql->extension->session_state[si_type].list= list_add(mysql->extension->session_state[si_type].list, session_item);
+ if (set_charset &&
+ strncmp(mysql->charset->csname, str->str, str->length) != 0)
+ {
+ char cs_name[64];
+ MARIADB_CHARSET_INFO *cs_info;
+ memcpy(cs_name, str->str, str->length);
+ cs_name[str->length]= 0;
+ if ((cs_info = (MARIADB_CHARSET_INFO *)mysql_find_charset_name(cs_name)))
+ mysql->charset= cs_info;
+ }
+ }
+ break;
+ default:
+ /* not supported yet */
+ plen= net_field_length(&pos);
+ pos+= plen;
+ break;
+ }
+ item_len-= (pos - old_pos);
+ }
+ }
+ for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++)
+ {
+ mysql->extension->session_state[i].list= list_reverse(mysql->extension->session_state[i].list);
+ mysql->extension->session_state[i].current= mysql->extension->session_state[i].list;
+ }
+ }
+ }
+ }
+ return(0);
+ }
+ if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */
+ {
+ int error=mysql_handle_local_infile(mysql, (char *)pos);
+
+ if ((length=ma_net_safe_read(mysql)) == packet_error || error)
+ return(-1);
+ goto get_info; /* Get info packet */
+ }
+ if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
+ mysql->server_status|= SERVER_STATUS_IN_TRANS;
+
+ mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */
+ if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8)))
+ return(-1);
+ if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,
+ (uint) field_count,1,
+ (my_bool) test(mysql->server_capabilities &
+ CLIENT_LONG_FLAG))))
+ return(-1);
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ mysql->field_count=field_count;
+ return(0);
+}
+
+int STDCALL mysql_session_track_get_next(MYSQL *mysql, enum enum_session_state_type type,
+ const char **data, size_t *length)
+{
+ MYSQL_LEX_STRING *str;
+ if (!mysql->extension->session_state[type].current)
+ return 1;
+
+ str= (MYSQL_LEX_STRING *)mysql->extension->session_state[type].current->data;
+ mysql->extension->session_state[type].current= mysql->extension->session_state[type].current->next;
+
+ *data= str->str ? str->str : NULL;
+ *length= str->str ? str->length : 0;
+ return 0;
+}
+
+int STDCALL mysql_session_track_get_first(MYSQL *mysql, enum enum_session_state_type type,
+ const char **data, size_t *length)
+{
+ mysql->extension->session_state[type].current= mysql->extension->session_state[type].list;
+ return mysql_session_track_get_next(mysql, type, data, length);
+}
+
+my_bool STDCALL
+mysql_read_query_result(MYSQL *mysql)
+{
+ return test(mysql->methods->db_read_query_result(mysql)) ? 1 : 0;
+}
+
+int STDCALL
+mysql_real_query(MYSQL *mysql, const char *query, unsigned long length)
+{
+ my_bool skip_result= OPT_EXT_VAL(mysql, multi_command);
+
+ if (length == (unsigned long)-1)
+ length= strlen(query);
+
+ free_old_query(mysql);
+
+ if (ma_simple_command(mysql, COM_QUERY,query,length,1,0))
+ return(-1);
+ if (!skip_result)
+ return(mysql->methods->db_read_query_result(mysql));
+ return(0);
+}
+
+/**************************************************************************
+** Alloc result struct for buffered results. All rows are read to buffer.
+** mysql_data_seek may be used.
+**************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_store_result(MYSQL *mysql)
+{
+ MYSQL_RES *result;
+
+ if (!mysql->fields)
+ return(0);
+ if (mysql->status != MYSQL_STATUS_GET_RESULT)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ mysql->status=MYSQL_STATUS_READY; /* server is ready */
+ if (!(result=(MYSQL_RES*) calloc(1, sizeof(MYSQL_RES)+
+ sizeof(ulong)*mysql->field_count)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ result->eof=1; /* Marker for buffered */
+ result->lengths=(ulong*) (result+1);
+ if (!(result->data=mysql->methods->db_read_rows(mysql,mysql->fields,mysql->field_count)))
+ {
+ free(result);
+ return(0);
+ }
+ mysql->affected_rows= result->row_count= result->data->rows;
+ result->data_cursor= result->data->data;
+ result->fields= mysql->fields;
+ result->field_alloc= mysql->field_alloc;
+ result->field_count= mysql->field_count;
+ result->current_field=0;
+ result->current_row=0; /* Must do a fetch first */
+ mysql->fields=0; /* fields is now in result */
+ return(result); /* Data fetched */
+}
+
+
+/**************************************************************************
+** Alloc struct for use with unbuffered reads. Data is fetched by domand
+** when calling to mysql_fetch_row.
+** mysql_data_seek is a noop.
+**
+** No other queries may be specified with the same MYSQL handle.
+** There shouldn't be much processing per row because mysql server shouldn't
+** have to wait for the client (and will not wait more than 30 sec/packet).
+**************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_use_result(MYSQL *mysql)
+{
+ MYSQL_RES *result;
+
+ if (!mysql->fields)
+ return(0);
+ if (mysql->status != MYSQL_STATUS_GET_RESULT)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ if (!(result=(MYSQL_RES*) calloc(1, sizeof(*result)+
+ sizeof(ulong)*mysql->field_count)))
+ return(0);
+ result->lengths=(ulong*) (result+1);
+ if (!(result->row=(MYSQL_ROW)
+ malloc(sizeof(result->row[0])*(mysql->field_count+1))))
+ { /* Ptrs: to one row */
+ free(result);
+ return(0);
+ }
+ result->fields= mysql->fields;
+ result->field_alloc= mysql->field_alloc;
+ result->field_count= mysql->field_count;
+ result->current_field=0;
+ result->handle= mysql;
+ result->current_row= 0;
+ mysql->fields=0; /* fields is now in result */
+ mysql->status=MYSQL_STATUS_USE_RESULT;
+ return(result); /* Data is read to be fetched */
+}
+
+/**************************************************************************
+** Return next field of the query results
+**************************************************************************/
+MYSQL_FIELD * STDCALL
+mysql_fetch_field(MYSQL_RES *result)
+{
+ if (result->current_field >= result->field_count)
+ return(NULL);
+ return &result->fields[result->current_field++];
+}
+
+/**************************************************************************
+** Return next row of the query results
+**************************************************************************/
+MYSQL_ROW STDCALL
+mysql_fetch_row(MYSQL_RES *res)
+{
+ if (!res)
+ return 0;
+ if (res->handle)
+ if (res->handle->status != MYSQL_STATUS_USE_RESULT &&
+ res->handle->status != MYSQL_STATUS_GET_RESULT)
+ return 0;
+ if (!res->data)
+ { /* Unbufferred fetch */
+ if (!res->eof)
+ {
+ if (!(res->handle->methods->db_read_one_row(res->handle,res->field_count,res->row, res->lengths)))
+ {
+ res->row_count++;
+ return(res->current_row=res->row);
+ }
+ res->eof=1;
+ res->handle->status=MYSQL_STATUS_READY;
+ /* Don't clear handle in mysql_free_results */
+ res->handle=0;
+ }
+ return((MYSQL_ROW) NULL);
+ }
+ {
+ MYSQL_ROW tmp;
+ if (!res->data_cursor)
+ {
+ return(res->current_row=(MYSQL_ROW) NULL);
+ }
+ tmp = res->data_cursor->data;
+ res->data_cursor = res->data_cursor->next;
+ return(res->current_row=tmp);
+ }
+}
+
+/**************************************************************************
+** Get column lengths of the current row
+** If one uses mysql_use_result, res->lengths contains the length information,
+** else the lengths are calculated from the offset between pointers.
+**************************************************************************/
+
+ulong * STDCALL
+mysql_fetch_lengths(MYSQL_RES *res)
+{
+ ulong *lengths,*prev_length;
+ char *start;
+ MYSQL_ROW column,end;
+
+ if (!(column=res->current_row))
+ return 0; /* Something is wrong */
+ if (res->data)
+ {
+ start=0;
+ prev_length=0; /* Keep gcc happy */
+ lengths=res->lengths;
+ for (end=column+res->field_count+1 ; column != end ; column++,lengths++)
+ {
+ if (!*column)
+ {
+ *lengths=0; /* Null */
+ continue;
+ }
+ if (start) /* Found end of prev string */
+ *prev_length= (uint) (*column-start-1);
+ start= *column;
+ prev_length=lengths;
+ }
+ }
+ return res->lengths;
+}
+
+/**************************************************************************
+** Move to a specific row and column
+**************************************************************************/
+
+void STDCALL
+mysql_data_seek(MYSQL_RES *result, unsigned long long row)
+{
+ MYSQL_ROWS *tmp=0;
+ if (result->data)
+ for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ;
+ result->current_row=0;
+ result->data_cursor = tmp;
+}
+
+/*************************************************************************
+** put the row or field cursor one a position one got from mysql_row_tell()
+** This doesn't restore any data. The next mysql_fetch_row or
+** mysql_fetch_field will return the next row or field after the last used
+*************************************************************************/
+
+MYSQL_ROW_OFFSET STDCALL
+mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET row)
+{
+ MYSQL_ROW_OFFSET return_value=result->data_cursor;
+ result->current_row= 0;
+ result->data_cursor= row;
+ return return_value;
+}
+
+
+MYSQL_FIELD_OFFSET STDCALL
+mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET field_offset)
+{
+ MYSQL_FIELD_OFFSET return_value=result->current_field;
+ result->current_field=field_offset;
+ return return_value;
+}
+
+/*****************************************************************************
+** List all databases
+*****************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_list_dbs(MYSQL *mysql, const char *wild)
+{
+ char buff[255];
+ snprintf(buff, 255, "SHOW DATABASES LIKE '%s'", wild ? wild : "%");
+ if (mysql_query(mysql,buff))
+ return(0);
+ return (mysql_store_result(mysql));
+}
+
+
+/*****************************************************************************
+** List all tables in a database
+** If wild is given then only the tables matching wild are returned
+*****************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_list_tables(MYSQL *mysql, const char *wild)
+{
+ char buff[255];
+
+ snprintf(buff, 255, "SHOW TABLES LIKE '%s'", wild ? wild : "%");
+ if (mysql_query(mysql,buff))
+ return(0);
+ return (mysql_store_result(mysql));
+}
+
+
+/**************************************************************************
+** List all fields in a table
+** If wild is given then only the fields matching wild are returned
+** Instead of this use query:
+** show fields in 'table' like "wild"
+**************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
+{
+ MYSQL_RES *result;
+ MYSQL_DATA *query;
+ char buff[255];
+ int length= 0;
+
+ LINT_INIT(query);
+
+ length= snprintf(buff, 128, "%s%c%s", table, '\0', wild ? wild : "");
+
+ if (ma_simple_command(mysql, COM_FIELD_LIST,buff,length,1,0) ||
+ !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8)))
+ return(NULL);
+
+ free_old_query(mysql);
+ if (!(result = (MYSQL_RES *) calloc(1, sizeof(MYSQL_RES))))
+ {
+ free_rows(query);
+ return(NULL);
+ }
+ result->field_alloc=mysql->field_alloc;
+ mysql->fields=0;
+ result->field_count = (uint) query->rows;
+ result->fields= unpack_fields(query,&result->field_alloc,
+ result->field_count,1,
+ (my_bool) test(mysql->server_capabilities &
+ CLIENT_LONG_FLAG));
+ result->eof=1;
+ return(result);
+}
+
+/* List all running processes (threads) in server */
+
+MYSQL_RES * STDCALL
+mysql_list_processes(MYSQL *mysql)
+{
+ MYSQL_DATA *fields;
+ uint field_count;
+ uchar *pos;
+
+ LINT_INIT(fields);
+ if (ma_simple_command(mysql, COM_PROCESS_INFO,0,0,0,0))
+ return(0);
+ free_old_query(mysql);
+ pos=(uchar*) mysql->net.read_pos;
+ field_count=(uint) net_field_length(&pos);
+ if (!(fields = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,5)))
+ return(NULL);
+ if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0,
+ (my_bool) test(mysql->server_capabilities &
+ CLIENT_LONG_FLAG))))
+ return(0);
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ mysql->field_count=field_count;
+ return(mysql_store_result(mysql));
+}
+
+/* In 5.0 this version became an additional parameter shutdown_level */
+int STDCALL
+mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level)
+{
+ uchar s_level[2];
+ s_level[0]= (uchar)shutdown_level;
+ return(ma_simple_command(mysql, COM_SHUTDOWN, (char *)s_level, 1, 0, 0));
+}
+
+int STDCALL
+mysql_refresh(MYSQL *mysql,uint options)
+{
+ uchar bits[1];
+ bits[0]= (uchar) options;
+ return(ma_simple_command(mysql, COM_REFRESH,(char*) bits,1,0,0));
+}
+
+int STDCALL
+mysql_kill(MYSQL *mysql,ulong pid)
+{
+ char buff[12];
+ int4store(buff,pid);
+ /* if we kill our own thread, reading the response packet will fail */
+ return(ma_simple_command(mysql, COM_PROCESS_KILL,buff,4,0,0));
+}
+
+
+int STDCALL
+mysql_dump_debug_info(MYSQL *mysql)
+{
+ return(ma_simple_command(mysql, COM_DEBUG,0,0,0,0));
+}
+
+char * STDCALL
+mysql_stat(MYSQL *mysql)
+{
+ if (ma_simple_command(mysql, COM_STATISTICS,0,0,0,0))
+ return mysql->net.last_error;
+ mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */
+ if (!mysql->net.read_pos[0])
+ {
+ SET_CLIENT_ERROR(mysql, CR_WRONG_HOST_INFO , SQLSTATE_UNKNOWN, 0);
+ return mysql->net.last_error;
+ }
+ return((char*) mysql->net.read_pos);
+}
+
+int STDCALL
+mysql_ping(MYSQL *mysql)
+{
+ int rc;
+ rc= ma_simple_command(mysql, COM_PING,0,0,0,0);
+
+ /* if connection was terminated and reconnect is true, try again */
+ if (rc!=0 && mysql->options.reconnect)
+ rc= ma_simple_command(mysql, COM_PING,0,0,0,0);
+ return rc;
+}
+
+char * STDCALL
+mysql_get_server_info(MYSQL *mysql)
+{
+ return((char*) mysql->server_version);
+}
+
+static size_t mariadb_server_version_id(MYSQL *mysql)
+{
+ size_t major, minor, patch;
+ char *p;
+
+ if (!(p = mysql->server_version)) {
+ return 0;
+ }
+
+ major = strtol(p, &p, 10);
+ p += 1; /* consume the dot */
+ minor = strtol(p, &p, 10);
+ p += 1; /* consume the dot */
+ patch = strtol(p, &p, 10);
+
+ return (major * 10000L + (unsigned long)(minor * 100L + patch));
+}
+
+unsigned long STDCALL mysql_get_server_version(MYSQL *mysql)
+{
+ return (unsigned long)mariadb_server_version_id(mysql);
+}
+
+char * STDCALL
+mysql_get_host_info(MYSQL *mysql)
+{
+ return(mysql->host_info);
+}
+
+uint STDCALL
+mysql_get_proto_info(MYSQL *mysql)
+{
+ return (mysql->protocol_version);
+}
+
+const char * STDCALL
+mysql_get_client_info(void)
+{
+ return (char*) MARIADB_CLIENT_VERSION_STR;
+}
+
+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;
+}
+
+uchar *ma_get_hash_keyval(const uchar *hash_entry,
+ unsigned int *length,
+ my_bool not_used __attribute__((unused)))
+{
+ /* Hash entry has the following format:
+ Offset: 0 key (\0 terminated)
+ key_length + 1 value (\0 terminated)
+ */
+ uchar *p= (uchar *)hash_entry;
+ size_t len= strlen((char *)p);
+ *length= (unsigned int)len;
+ return p;
+}
+
+void ma_hash_free(void *p)
+{
+ free(p);
+}
+
+int STDCALL
+mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
+{
+ va_list ap;
+ void *arg1;
+ size_t stacksize;
+ struct mysql_async_context *ctxt;
+
+ va_start(ap, option);
+
+ arg1= va_arg(ap, void *);
+
+ switch (option) {
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ mysql->options.connect_timeout= *(uint*) arg1;
+ break;
+ case MYSQL_OPT_COMPRESS:
+ mysql->options.compress= 1; /* Remember for connect */
+ mysql->options.client_flag|= CLIENT_COMPRESS;
+ break;
+ case MYSQL_OPT_NAMED_PIPE:
+ mysql->options.named_pipe=1; /* Force named pipe */
+ break;
+ case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/
+ if (!arg1 || test(*(my_bool*) arg1))
+ mysql->options.client_flag|= CLIENT_LOCAL_FILES;
+ else
+ mysql->options.client_flag&= ~CLIENT_LOCAL_FILES;
+ break;
+ case MYSQL_INIT_COMMAND:
+ options_add_initcommand(&mysql->options, (char *)arg1);
+ break;
+ case MYSQL_READ_DEFAULT_FILE:
+ OPT_SET_VALUE_STR(&mysql->options, my_cnf_file, (char *)arg1);
+ break;
+ case MYSQL_READ_DEFAULT_GROUP:
+ if (!arg1 || !((char *)arg1)[0])
+ {
+#if defined(__APPLE__) || defined(__FreeBSD__)
+ const char * appname = getprogname();
+#elif defined(_GNU_SOURCE)
+ const char * appname = program_invocation_short_name;
+#elif defined(WIN32)
+ char appname[FN_REFLEN]= "";
+
+ if (GetModuleFileName(NULL, appname, FN_REFLEN))
+ {
+ PathStripPath(appname);
+ PathRemoveExtension(appname);
+ }
+#else
+ const char * appname = "";
+#endif
+ OPT_SET_VALUE_STR(&mysql->options, my_cnf_group, appname);
+ break;
+ }
+ OPT_SET_VALUE_STR(&mysql->options, my_cnf_group, (char *)arg1);
+ break;
+ case MYSQL_SET_CHARSET_DIR:
+ OPT_SET_VALUE_STR(&mysql->options, charset_dir, arg1);
+ break;
+ case MYSQL_SET_CHARSET_NAME:
+ OPT_SET_VALUE_STR(&mysql->options, charset_name, arg1);
+ break;
+ case MYSQL_OPT_RECONNECT:
+ mysql->options.reconnect= *(my_bool *)arg1;
+ break;
+ case MYSQL_OPT_PROTOCOL:
+ mysql->options.protocol= *((uint *)arg1);
+ break;
+#ifdef _WIN32
+ case MYSQL_SHARED_MEMORY_BASE_NAME:
+ OPT_SET_VALUE_STR(&mysql->options, shared_memory_base_name, arg1);
+ break;
+#endif
+ case MYSQL_OPT_READ_TIMEOUT:
+ mysql->options.read_timeout= *(uint *)arg1;
+ break;
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ mysql->options.write_timeout= *(uint *)arg1;
+ break;
+ case MYSQL_REPORT_DATA_TRUNCATION:
+ mysql->options.report_data_truncation= *(my_bool *)arg1;
+ break;
+ case MYSQL_PROGRESS_CALLBACK:
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (mysql->options.extension)
+ mysql->options.extension->report_progress=
+ (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg1;
+ break;
+ case MYSQL_SERVER_PUBLIC_KEY:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, server_public_key, (char *)arg1);
+ break;
+ case MYSQL_PLUGIN_DIR:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, plugin_dir, (char *)arg1);
+ break;
+ case MYSQL_DEFAULT_AUTH:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, default_auth, (char *)arg1);
+ break;
+ case MYSQL_OPT_NONBLOCK:
+ if (mysql->options.extension &&
+ (ctxt = mysql->options.extension->async_context) != 0)
+ {
+ /*
+ We must not allow changing the stack size while a non-blocking call is
+ suspended (as the stack is then in use).
+ */
+ if (ctxt->suspended)
+ goto end;
+ my_context_destroy(&ctxt->async_context);
+ free(ctxt);
+ }
+ if (!(ctxt= (struct mysql_async_context *)
+ calloc(1, sizeof(*ctxt))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ stacksize= 0;
+ if (arg1)
+ stacksize= *(const size_t *)arg1;
+ if (!stacksize)
+ stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE;
+ if (my_context_init(&ctxt->async_context, stacksize))
+ {
+ free(ctxt);
+ goto end;
+ }
+ if (!mysql->options.extension)
+ if(!(mysql->options.extension= (struct st_mysql_options_extension *)
+ calloc(1, sizeof(struct st_mysql_options_extension))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ mysql->options.extension->async_context= ctxt;
+ break;
+ case MYSQL_OPT_MAX_ALLOWED_PACKET:
+ if (mysql)
+ mysql->options.max_allowed_packet= (unsigned long)(*(size_t *)arg1);
+ else
+ max_allowed_packet= (unsigned long)(*(size_t *)arg1);
+ break;
+ case MYSQL_OPT_NET_BUFFER_LENGTH:
+ net_buffer_length= (unsigned long)(*(size_t *)arg1);
+ break;
+ case MYSQL_OPT_SSL_ENFORCE:
+ mysql->options.use_ssl= (*(my_bool *)arg1);
+ break;
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ if (*(my_bool *)arg1)
+ mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT;
+ else
+ mysql->options.client_flag &= ~CLIENT_SSL_VERIFY_SERVER_CERT;
+ break;
+ case MYSQL_OPT_SSL_KEY:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_key, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CERT:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_cert, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CA:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_ca, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CAPATH:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_capath, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CIPHER:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_cipher, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CRL:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CRLPATH:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1);
+ break;
+ case MYSQL_OPT_CONNECT_ATTR_DELETE:
+ {
+ uchar *h;
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (hash_inited(&mysql->options.extension->connect_attrs) &&
+ (h= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1,
+ arg1 ? (uint)strlen((char *)arg1) : 0)))
+ {
+ uchar *p= h;
+ size_t key_len= strlen((char *)p);
+ mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len);
+ p+= key_len + 1;
+ key_len= strlen((char *)p);
+ mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len);
+ hash_delete(&mysql->options.extension->connect_attrs, h);
+ }
+
+ }
+ 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;
+ case MARIADB_OPT_CONNECTION_HANDLER:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, connection_handler, (char *)arg1);
+ break;
+ case MARIADB_OPT_PORT:
+ OPT_SET_VALUE_INT(&mysql->options, port, *((uint *)arg1));
+ break;
+ case MARIADB_OPT_UNIXSOCKET:
+ OPT_SET_VALUE_STR(&mysql->options, unix_socket, arg1);
+ break;
+ case MARIADB_OPT_USER:
+ OPT_SET_VALUE_STR(&mysql->options, user, arg1);
+ break;
+ case MARIADB_OPT_HOST:
+ OPT_SET_VALUE_STR(&mysql->options, host, arg1);
+ break;
+ case MARIADB_OPT_SCHEMA:
+ OPT_SET_VALUE_STR(&mysql->options, db, arg1);
+ break;
+ case MARIADB_OPT_DEBUG:
+ break;
+ case MARIADB_OPT_FOUND_ROWS:
+ mysql->options.client_flag|= CLIENT_FOUND_ROWS;
+ break;
+ case MARIADB_OPT_INTERACTIVE:
+ mysql->options.client_flag|= CLIENT_INTERACTIVE;
+ break;
+ case MARIADB_OPT_MULTI_RESULTS:
+ mysql->options.client_flag|= CLIENT_MULTI_RESULTS;
+ break;
+ case MARIADB_OPT_MULTI_STATEMENTS:
+ mysql->options.client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS;
+ break;
+ case MARIADB_OPT_PASSWORD:
+ OPT_SET_VALUE_STR(&mysql->options, password, arg1);
+ break;
+ case MARIADB_OPT_USERDATA:
+ {
+ void *data= va_arg(ap, void *);
+ uchar *buffer, *p;
+ char *key= (char *)arg1;
+
+ if (!key || !data)
+ {
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (!hash_inited(&mysql->options.extension->userdata))
+ {
+ if (_hash_init(&mysql->options.extension->userdata,
+ 0, 0, 0, ma_get_hash_keyval, ma_hash_free, 0))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ /* check if key is already in buffer */
+ p= (uchar *)hash_search(&mysql->options.extension->userdata,
+ (uchar *)key,
+ (uint)strlen(key));
+ if (p)
+ {
+ p+= strlen(key) + 1;
+ memcpy(p, &data, sizeof(void *));
+ break;
+ }
+
+ if (!(buffer= (uchar *)malloc(strlen(key) + 1 + sizeof(void *))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ p= buffer;
+ strcpy((char *)p, key);
+ p+= strlen(key) + 1;
+ memcpy(p, &data, sizeof(void *));
+
+ if (hash_insert(&mysql->options.extension->userdata, buffer))
+ {
+ free(buffer);
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ break;
+ case MYSQL_OPT_CONNECT_ATTR_ADD:
+ {
+ uchar *buffer;
+ void *arg2= va_arg(ap, void *);
+ size_t key_len= arg1 ? strlen((char *)arg1) : 0,
+ value_len= arg2 ? strlen((char *)arg2) : 0;
+ size_t storage_len= key_len + value_len +
+ get_store_length(key_len) +
+ get_store_length(value_len);
+
+ /* since we store terminating zero character in hash, we need
+ * to increase lengths */
+ key_len++;
+ 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, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ if (!hash_inited(&mysql->options.extension->connect_attrs))
+ {
+ if (_hash_init(&mysql->options.extension->connect_attrs,
+ 0, 0, 0, ma_get_hash_keyval, ma_hash_free, 0))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ if ((buffer= (uchar *)malloc(key_len + value_len)))
+ {
+ uchar *p= buffer;
+ strcpy((char *)p, arg1);
+ p+= (strlen(arg1) + 1);
+ if (arg2)
+ strcpy((char *)p, arg2);
+
+ if (hash_insert(&mysql->options.extension->connect_attrs, buffer))
+ {
+ free(buffer);
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ mysql->options.extension->connect_attrs_len+= storage_len;
+ }
+ else
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ break;
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
+ break;
+ case MYSQL_SECURE_AUTH:
+ mysql->options.secure_auth= *(my_bool *)arg1;
+ break;
+ case MYSQL_OPT_BIND:
+ OPT_SET_VALUE_STR(&mysql->options, bind_address, arg1);
+ break;
+ case MARIADB_OPT_TLS_CIPHER_STRENGTH:
+ OPT_SET_EXTENDED_VALUE_INT(&mysql->options, tls_cipher_strength, *((unsigned int *)arg1));
+ break;
+ case MARIADB_OPT_SSL_FP:
+ case MARIADB_OPT_TLS_PEER_FP:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp, (char *)arg1);
+ mysql->options.use_ssl= 1;
+ break;
+ case MARIADB_OPT_SSL_FP_LIST:
+ case MARIADB_OPT_TLS_PEER_FP_LIST:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp_list, (char *)arg1);
+ mysql->options.use_ssl= 1;
+ break;
+ case MARIADB_OPT_TLS_PASSPHRASE:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_pw, (char *)arg1);
+ break;
+ case MARIADB_OPT_CONNECTION_READ_ONLY:
+ OPT_SET_EXTENDED_VALUE_INT(&mysql->options, read_only, *(my_bool *)arg1);
+ break;
+ case MARIADB_OPT_PROXY_HEADER:
+ {
+ size_t arg2 = va_arg(ap, size_t);
+ OPT_SET_EXTENDED_VALUE(&mysql->options, proxy_header, (char *)arg1);
+ OPT_SET_EXTENDED_VALUE(&mysql->options, proxy_header_len, arg2);
+ }
+ break;
+ case MARIADB_OPT_TLS_VERSION:
+ case MYSQL_OPT_TLS_VERSION:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_version, (char *)arg1);
+ break;
+ default:
+ va_end(ap);
+ return(-1);
+ }
+ va_end(ap);
+ return(0);
+end:
+ va_end(ap);
+ return(1);
+}
+
+int STDCALL
+mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...)
+{
+ va_list ap;
+
+ va_start(ap, arg);
+
+ switch(option) {
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ *((uint *)arg)= mysql->options.connect_timeout;
+ break;
+ case MYSQL_OPT_COMPRESS:
+ *((my_bool *)arg)= mysql->options.compress;
+ break;
+ case MYSQL_OPT_NAMED_PIPE:
+ *((my_bool *)arg)= mysql->options.named_pipe;
+ break;
+ case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/
+ *((uint *)arg)= test(mysql->options.client_flag & CLIENT_LOCAL_FILES);
+ break;
+ case MYSQL_INIT_COMMAND:
+ /* mysql_get_optionsv(mysql, MYSQL_INIT_COMMAND, commands, elements) */
+ {
+ unsigned int *elements;
+ if (arg)
+ *((char **)arg)= mysql->options.init_command ? mysql->options.init_command->buffer : NULL;
+ if ((elements= va_arg(ap, unsigned int *)))
+ *elements= mysql->options.init_command ? mysql->options.init_command->elements : 0;
+ }
+ break;
+ case MYSQL_READ_DEFAULT_FILE:
+ *((char **)arg)= mysql->options.my_cnf_file;
+ break;
+ case MYSQL_READ_DEFAULT_GROUP:
+ *((char **)arg)= mysql->options.my_cnf_group;
+ break;
+ case MYSQL_SET_CHARSET_DIR:
+ /* not supported in this version. Since all character sets
+ are internally available, we don't throw an error */
+ *((char **)arg)= NULL;
+ break;
+ case MYSQL_SET_CHARSET_NAME:
+ if (mysql->charset)
+ *((const char **)arg)= mysql->charset->csname;
+ else
+ *((char **)arg)= mysql->options.charset_name;
+ break;
+ case MYSQL_OPT_RECONNECT:
+ *((my_bool *)arg)= mysql->options.reconnect;
+ break;
+ case MYSQL_OPT_PROTOCOL:
+ *((uint *)arg)= mysql->options.protocol;
+ break;
+ case MYSQL_OPT_READ_TIMEOUT:
+ *((uint *)arg)= mysql->options.read_timeout;
+ break;
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ *((uint *)arg)= mysql->options.write_timeout;
+ break;
+ case MYSQL_REPORT_DATA_TRUNCATION:
+ *((my_bool *)arg)= mysql->options.report_data_truncation;
+ break;
+ case MYSQL_PROGRESS_CALLBACK:
+ *((void (**)(const MYSQL *, uint, uint, double, const char *, uint))arg)=
+ mysql->options.extension ? mysql->options.extension->report_progress : NULL;
+ break;
+ case MYSQL_SERVER_PUBLIC_KEY:
+ *((char **)arg)= mysql->options.extension ?
+ mysql->options.extension->server_public_key : NULL;
+ break;
+ case MYSQL_PLUGIN_DIR:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->plugin_dir : NULL;
+ break;
+ case MYSQL_DEFAULT_AUTH:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->default_auth : NULL;
+ break;
+ case MYSQL_OPT_NONBLOCK:
+ *((my_bool *)arg)= test(mysql->options.extension && mysql->options.extension->async_context);
+ break;
+ case MYSQL_OPT_SSL_ENFORCE:
+ *((my_bool *)arg)= mysql->options.use_ssl;
+ break;
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ *((my_bool *)arg)= test(mysql->options.client_flag & CLIENT_SSL_VERIFY_SERVER_CERT);
+ break;
+ case MYSQL_OPT_SSL_KEY:
+ *((char **)arg)= mysql->options.ssl_key;
+ break;
+ case MYSQL_OPT_SSL_CERT:
+ *((char **)arg)= mysql->options.ssl_cert;
+ break;
+ case MYSQL_OPT_SSL_CA:
+ *((char **)arg)= mysql->options.ssl_ca;
+ break;
+ case MYSQL_OPT_SSL_CAPATH:
+ *((char **)arg)= mysql->options.ssl_capath;
+ break;
+ case MYSQL_OPT_SSL_CIPHER:
+ *((char **)arg)= mysql->options.ssl_cipher;
+ break;
+ case MYSQL_OPT_SSL_CRL:
+ *((char **)arg)= mysql->options.extension ? mysql->options.ssl_cipher : NULL;
+ break;
+ case MYSQL_OPT_SSL_CRLPATH:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL;
+ break;
+ case MYSQL_OPT_CONNECT_ATTRS:
+ /* mysql_get_optionsv(mysql, MYSQL_OPT_CONNECT_ATTRS, keys, vals, elements) */
+ {
+ unsigned int i, *elements;
+ char **key= NULL;
+ void *arg1;
+ char **val= NULL;
+
+ if (arg)
+ key= *(char ***)arg;
+
+ arg1= va_arg(ap, char **);
+ if (arg1)
+ val= *(char ***)arg1;
+
+ if (!(elements= va_arg(ap, unsigned int *)))
+ goto error;
+
+ if (!elements)
+ goto error;
+
+ *elements= 0;
+
+ if (!mysql->options.extension ||
+ !hash_inited(&mysql->options.extension->connect_attrs))
+ break;
+
+ *elements= mysql->options.extension->connect_attrs.records;
+
+ if (val || key)
+ {
+ for (i=0; i < *elements; i++)
+ {
+ uchar *p= hash_element(&mysql->options.extension->connect_attrs, i);
+ if (key)
+ key[i]= (char *)p;
+ p+= strlen((char *)p) + 1;
+ if (val)
+ val[i]= (char *)p;
+ }
+ }
+ }
+ break;
+ case MYSQL_OPT_MAX_ALLOWED_PACKET:
+ *((unsigned long *)arg)= (mysql) ? mysql->options.max_allowed_packet :
+ max_allowed_packet;
+ break;
+ case MYSQL_OPT_NET_BUFFER_LENGTH:
+ *((unsigned long *)arg)= net_buffer_length;
+ break;
+ case MYSQL_SECURE_AUTH:
+ *((my_bool *)arg)= mysql->options.secure_auth;
+ break;
+ case MYSQL_OPT_BIND:
+ *((char **)arg)= mysql->options.bind_address;
+ break;
+ case MARIADB_OPT_TLS_CIPHER_STRENGTH:
+ *((unsigned int *)arg) = mysql->options.extension ? mysql->options.extension->tls_cipher_strength : 0;
+ break;
+ case MARIADB_OPT_SSL_FP:
+ case MARIADB_OPT_TLS_PEER_FP:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_fp : NULL;
+ break;
+ case MARIADB_OPT_SSL_FP_LIST:
+ case MARIADB_OPT_TLS_PEER_FP_LIST:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_fp_list : NULL;
+ break;
+ case MARIADB_OPT_TLS_PASSPHRASE:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_pw : NULL;
+ break;
+ case MARIADB_OPT_CONNECTION_READ_ONLY:
+ *((my_bool *)arg)= mysql->options.extension ? mysql->options.extension->read_only : 0;
+ break;
+ case MARIADB_OPT_USERDATA:
+ /* nysql_get_optionv(mysql, MARIADB_OPT_USERDATA, key, value) */
+ {
+ uchar *p;
+ void *data= va_arg(ap, void *);
+ char *key= (char *)arg;
+ if (key && data && mysql->options.extension && hash_inited(&mysql->options.extension->userdata) &&
+ (p= (uchar *)hash_search(&mysql->options.extension->userdata, (uchar *)key,
+ (uint)strlen((char *)key))))
+ {
+ p+= strlen(key) + 1;
+ *((void **)data)= *((void **)p);
+ break;
+ }
+ if (data)
+ *((void **)data)= NULL;
+ }
+ break;
+ case MARIADB_OPT_CONNECTION_HANDLER:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->connection_handler : NULL;
+ break;
+ default:
+ va_end(ap);
+ return(-1);
+ }
+ va_end(ap);
+ return(0);
+error:
+ va_end(ap);
+ return(-1);
+}
+
+int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option, void *arg)
+{
+ return mysql_get_optionv(mysql, option, arg);
+}
+
+int STDCALL
+mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
+{
+ return mysql_optionsv(mysql, option, arg);
+}
+
+int STDCALL
+mysql_options4(MYSQL *mysql,enum mysql_option option, const void *arg1, const void *arg2)
+{
+ return mysql_optionsv(mysql, option, arg1, arg2);
+}
+/****************************************************************************
+** Functions to get information from the MySQL structure
+** These are functions to make shared libraries more usable.
+****************************************************************************/
+
+/* MYSQL_RES */
+my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res)
+{
+ return res->row_count;
+}
+
+unsigned int STDCALL mysql_num_fields(MYSQL_RES *res)
+{
+ return res->field_count;
+}
+
+/* deprecated */
+my_bool STDCALL mysql_eof(MYSQL_RES *res)
+{
+ return res->eof;
+}
+
+MYSQL_FIELD * STDCALL mysql_fetch_field_direct(MYSQL_RES *res,uint fieldnr)
+{
+ return &(res)->fields[fieldnr];
+}
+
+MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res)
+{
+ return (res)->fields;
+}
+
+MYSQL_ROWS * STDCALL mysql_row_tell(MYSQL_RES *res)
+{
+ return res->data_cursor;
+}
+
+uint STDCALL mysql_field_tell(MYSQL_RES *res)
+{
+ return (res)->current_field;
+}
+
+/* MYSQL */
+
+unsigned int STDCALL mysql_field_count(MYSQL *mysql)
+{
+ return mysql->field_count;
+}
+
+my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql)
+{
+ return (mysql)->affected_rows;
+}
+
+my_bool STDCALL mysql_autocommit(MYSQL *mysql, my_bool mode)
+{
+ return((my_bool) mysql_real_query(mysql, (mode) ? "SET autocommit=1" :
+ "SET autocommit=0", 16));
+}
+
+my_bool STDCALL mysql_commit(MYSQL *mysql)
+{
+ return((my_bool)mysql_real_query(mysql, "COMMIT", (unsigned long) sizeof("COMMIT")));
+}
+
+my_bool STDCALL mysql_rollback(MYSQL *mysql)
+{
+ return((my_bool)mysql_real_query(mysql, "ROLLBACK", (unsigned long)sizeof("ROLLBACK")));
+}
+
+my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql)
+{
+ return (mysql)->insert_id;
+}
+
+uint STDCALL mysql_errno(MYSQL *mysql)
+{
+ return mysql ? mysql->net.last_errno : 0;
+}
+
+const char * STDCALL mysql_error(MYSQL *mysql)
+{
+ return mysql ? (mysql)->net.last_error : (char *)"";
+}
+
+const char *STDCALL mysql_info(MYSQL *mysql)
+{
+ return (mysql)->info;
+}
+
+my_bool STDCALL mysql_more_results(MYSQL *mysql)
+{
+ return(test(mysql->server_status & SERVER_MORE_RESULTS_EXIST));
+}
+
+int STDCALL mysql_next_result(MYSQL *mysql)
+{
+
+ /* make sure communication is not blocking */
+ if (mysql->status != MYSQL_STATUS_READY)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* clear error, and mysql status variables */
+ CLEAR_CLIENT_ERROR(mysql);
+ mysql->affected_rows = (ulonglong) ~0;
+
+ if (mysql->server_status & SERVER_MORE_RESULTS_EXIST)
+ {
+ return(mysql->methods->db_read_query_result(mysql));
+ }
+
+ return(-1);
+}
+
+ulong STDCALL mysql_thread_id(MYSQL *mysql)
+{
+ return (mysql)->thread_id;
+}
+
+const char * STDCALL mysql_character_set_name(MYSQL *mysql)
+{
+ return mysql->charset->csname;
+}
+
+
+uint STDCALL mysql_thread_safe(void)
+{
+#ifdef THREAD
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+/****************************************************************************
+** Some support functions
+****************************************************************************/
+
+/*
+** Add escape characters to a string (blob?) to make it suitable for a insert
+** to should at least have place for length*2+1 chars
+** Returns the length of the to string
+*/
+
+ulong STDCALL
+mysql_escape_string(char *to,const char *from, ulong length)
+{
+ return (ulong)mysql_cset_escape_slashes(ma_default_charset_info, to, from, length);
+}
+
+ulong STDCALL
+mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
+ ulong length)
+{
+ if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
+ return (ulong)mysql_cset_escape_quotes(mysql->charset, to, from, length);
+ else
+ return (ulong)mysql_cset_escape_slashes(mysql->charset, to, from, length);
+}
+
+static void mariadb_get_charset_info(MYSQL *mysql, MY_CHARSET_INFO *cs)
+{
+ if (!cs)
+ return;
+
+ cs->number= mysql->charset->nr;
+ cs->csname= mysql->charset->csname;
+ cs->name= mysql->charset->name;
+ cs->state= 0;
+ cs->comment= NULL;
+ cs->dir= NULL;
+ cs->mbminlen= mysql->charset->char_minlen;
+ cs->mbmaxlen= mysql->charset->char_maxlen;
+
+ return;
+}
+
+void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs)
+{
+ mariadb_get_charset_info(mysql, cs);
+}
+
+int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname)
+{
+ const MARIADB_CHARSET_INFO *cs;
+
+ if (!csname)
+ goto error;
+
+ if ((cs= mysql_find_charset_name(csname)))
+ {
+ char buff[64];
+
+ snprintf(buff, 63, "SET NAMES %s", cs->csname);
+ if (!mysql_real_query(mysql, buff, (unsigned long)strlen(buff)))
+ {
+ mysql->charset= cs;
+ return(0);
+ }
+ }
+
+error:
+ my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN,
+ 0, csname, "compiled_in");
+ return(mysql->net.last_errno);
+}
+
+unsigned int STDCALL mysql_warning_count(MYSQL *mysql)
+{
+ return mysql->warning_count;
+}
+
+const char * STDCALL mysql_sqlstate(MYSQL *mysql)
+{
+ return mysql->net.sqlstate;
+}
+
+#ifndef _WIN32
+#include <signal.h>
+static void ignore_sigpipe()
+{
+ signal(SIGPIPE, SIG_IGN);
+}
+#else
+#define ignore_sigpipe()
+#endif
+
+#ifdef _WIN32
+static int mysql_once_init()
+#else
+static void mysql_once_init()
+#endif
+{
+ ma_init(); /* Will init threads */
+ init_client_errs();
+ get_default_configuration_dirs();
+ if (mysql_client_plugin_init())
+ {
+#ifdef _WIN32
+ return 1;
+#else
+ return;
+#endif
+ }
+ if (!mysql_port)
+ {
+ struct servent *serv_ptr;
+ char *env;
+
+ mysql_port = MARIADB_PORT;
+ if ((serv_ptr = getservbyname("mysql", "tcp")))
+ mysql_port = (uint)ntohs((ushort)serv_ptr->s_port);
+ if ((env = getenv("MYSQL_TCP_PORT")))
+ mysql_port =(uint)atoi(env);
+ }
+ if (!mysql_unix_port)
+ {
+ char *env;
+#ifdef _WIN32
+ mysql_unix_port = (char*)MARIADB_NAMEDPIPE;
+#else
+ mysql_unix_port = (char*)MARIADB_UNIX_ADDR;
+#endif
+ if ((env = getenv("MYSQL_UNIX_PORT")) ||
+ (env = getenv("MARIADB_UNIX_PORT")))
+ mysql_unix_port = env;
+ }
+ if (!mysql_ps_subsystem_initialized)
+ mysql_init_ps_subsystem();
+#ifdef HAVE_TLS
+ ma_tls_start(0, 0);
+#endif
+ ignore_sigpipe();
+ mysql_client_init = 1;
+#ifdef _WIN32
+ return 0;
+#endif
+}
+
+#ifdef _WIN32
+BOOL CALLBACK win_init_once(
+ PINIT_ONCE InitOnce,
+ PVOID Parameter,
+ PVOID *lpContext)
+{
+ return !mysql_once_init();
+ return TRUE;
+}
+#endif
+
+int STDCALL mysql_server_init(int argc __attribute__((unused)),
+ char **argv __attribute__((unused)),
+ char **groups __attribute__((unused)))
+{
+#ifdef _WIN32
+ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
+ BOOL ret = InitOnceExecuteOnce(&init_once, win_init_once, NULL, NULL);
+ return ret? 0: 1;
+#else
+ static pthread_once_t init_once = PTHREAD_ONCE_INIT;
+ return pthread_once(&init_once, mysql_once_init);
+#endif
+}
+
+void STDCALL mysql_server_end(void)
+{
+ if (!mysql_client_init)
+ return;
+
+ release_configuration_dirs();
+ mysql_client_plugin_deinit();
+
+ list_free(pvio_callback, 0);
+ if (ma_init_done)
+ ma_end(0);
+#ifdef HAVE_TLS
+ ma_pvio_tls_end();
+#endif
+ mysql_client_init= 0;
+ ma_init_done= 0;
+}
+
+my_bool STDCALL mysql_thread_init(void)
+{
+ return 0;
+}
+
+void STDCALL mysql_thread_end(void)
+{
+}
+
+int STDCALL mysql_set_server_option(MYSQL *mysql,
+ enum enum_mysql_set_option option)
+{
+ char buffer[2];
+ int2store(buffer, (uint)option);
+ return(ma_simple_command(mysql, COM_SET_OPTION, buffer, sizeof(buffer), 0, 0));
+}
+
+ulong STDCALL mysql_get_client_version(void)
+{
+ return MARIADB_VERSION_ID;
+}
+
+ulong STDCALL mysql_hex_string(char *to, const char *from, unsigned long len)
+{
+ char *start= to;
+ char hexdigits[]= "0123456789ABCDEF";
+
+ while (len--)
+ {
+ *to++= hexdigits[((unsigned char)*from) >> 4];
+ *to++= hexdigits[((unsigned char)*from) & 0x0F];
+ from++;
+ }
+ *to= 0;
+ return (ulong)(to - start);
+}
+
+my_bool STDCALL mariadb_connection(MYSQL *mysql)
+{
+ return (strstr(mysql->server_version, "MariaDB") ||
+ strstr(mysql->server_version, "-maria-"));
+}
+
+const char * STDCALL
+mysql_get_server_name(MYSQL *mysql)
+{
+ if (mysql->options.extension &&
+ mysql->options.extension->db_driver != NULL)
+ return mysql->options.extension->db_driver->name;
+ return mariadb_connection(mysql) ? "MariaDB" : "MySQL";
+}
+
+static my_socket mariadb_get_socket(MYSQL *mysql)
+{
+ my_socket sock= INVALID_SOCKET;
+ if (mysql->net.pvio)
+ {
+ ma_pvio_get_handle(mysql->net.pvio, &sock);
+
+ }
+ /* if an asynchronous connect is in progress, we need to obtain
+ pvio handle from async_context until the connection was
+ successfully established.
+ */
+ else if (mysql->options.extension && mysql->options.extension->async_context &&
+ mysql->options.extension->async_context->pvio)
+ {
+ ma_pvio_get_handle(mysql->options.extension->async_context->pvio, &sock);
+ }
+ return sock;
+}
+
+my_socket STDCALL
+mysql_get_socket(MYSQL *mysql)
+{
+ return mariadb_get_socket(mysql);
+}
+
+MARIADB_CHARSET_INFO * STDCALL mariadb_get_charset_by_name(const char *csname)
+{
+ return (MARIADB_CHARSET_INFO *)mysql_find_charset_name(csname);
+}
+
+MARIADB_CHARSET_INFO * STDCALL mariadb_get_charset_by_nr(unsigned int csnr)
+{
+ return (MARIADB_CHARSET_INFO *)mysql_find_charset_nr(csnr);
+}
+
+my_bool STDCALL mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ...)
+{
+ va_list ap;
+
+ va_start(ap, arg);
+
+ switch(value) {
+ case MARIADB_MAX_ALLOWED_PACKET:
+ *((size_t *)arg)= (size_t)max_allowed_packet;
+ break;
+ case MARIADB_NET_BUFFER_LENGTH:
+ *((size_t *)arg)= (size_t)net_buffer_length;
+ break;
+ case MARIADB_CONNECTION_ERROR_ID:
+ if (!mysql)
+ goto error;
+ *((unsigned int *)arg)= mysql->net.last_errno;
+ break;
+ case MARIADB_CONNECTION_ERROR:
+ if (!mysql)
+ goto error;
+ *((char **)arg)= mysql->net.last_error;
+ break;
+ case MARIADB_CONNECTION_SQLSTATE:
+ if (!mysql)
+ goto error;
+ *((char **)arg)= mysql->net.sqlstate;
+ break;
+ case MARIADB_CONNECTION_TLS_VERSION:
+ #ifdef HAVE_TLS
+ if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
+ *((char **)arg)= (char *)ma_pvio_tls_get_protocol_version(mysql->net.pvio->ctls);
+ else
+ #endif
+ goto error;
+ break;
+ case MARIADB_CONNECTION_TLS_VERSION_ID:
+ #ifdef HAVE_TLS
+ if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
+ *((unsigned int *)arg)= ma_pvio_tls_get_protocol_version_id(mysql->net.pvio->ctls);
+ else
+ #endif
+ goto error;
+ break;
+ case MARIADB_TLS_LIBRARY:
+#ifdef HAVE_TLS
+ *((const char **)arg)= tls_library_version;
+#else
+ *((char **)arg)= "Off";
+#endif
+ break;
+ case MARIADB_CLIENT_VERSION:
+ *((const char **)arg)= MARIADB_CLIENT_VERSION_STR;
+ break;
+ case MARIADB_CLIENT_VERSION_ID:
+ *((size_t *)arg)= MARIADB_VERSION_ID;
+ break;
+ case MARIADB_CONNECTION_SERVER_VERSION:
+ if (mysql)
+ *((char **)arg)= mysql->server_version;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_TYPE:
+ if (mysql)
+ *((const char **)arg)= mariadb_connection(mysql) ? "MariaDB" : "MySQL";
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_VERSION_ID:
+ if (mysql)
+ *((size_t *)arg)= mariadb_server_version_id(mysql);
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_PROTOCOL_VERSION_ID:
+ if (mysql)
+ *((unsigned int *)arg)= mysql->protocol_version;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_MARIADB_CHARSET_INFO:
+ if (mysql)
+ mariadb_get_charset_info(mysql, (MY_CHARSET_INFO *)arg);
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SOCKET:
+ if (mysql)
+ *((my_socket *)arg)= mariadb_get_socket(mysql);
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_TYPE:
+ if (mysql && mysql->net.pvio)
+ *((int *)arg)= (int)mysql->net.pvio->type;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_ASYNC_TIMEOUT_MS:
+ if (mysql && mysql->options.extension && mysql->options.extension->async_context)
+ *((unsigned int *)arg)= mysql->options.extension->async_context->timeout_value;
+ break;
+ case MARIADB_CONNECTION_ASYNC_TIMEOUT:
+ if (mysql && mysql->options.extension && mysql->options.extension->async_context)
+ {
+ unsigned int timeout= mysql->options.extension->async_context->timeout_value;
+ if (timeout > UINT_MAX - 999)
+ *((unsigned int *)arg)= (timeout - 1)/1000 + 1;
+ else
+ *((unsigned int *)arg)= (timeout+999)/1000;
+ }
+ break;
+ case MARIADB_CHARSET_NAME:
+ {
+ char *name;
+ name= va_arg(ap, char *);
+ if (name)
+ *((MARIADB_CHARSET_INFO **)arg)= (MARIADB_CHARSET_INFO *)mysql_find_charset_name(name);
+ else
+ goto error;
+ }
+ break;
+ case MARIADB_CHARSET_ID:
+ {
+ unsigned int nr;
+ nr= va_arg(ap, unsigned int);
+ *((MARIADB_CHARSET_INFO **)arg)= (MARIADB_CHARSET_INFO *)mysql_find_charset_nr(nr);
+ }
+ break;
+ case MARIADB_CONNECTION_SSL_CIPHER:
+ #ifdef HAVE_TLS
+ if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
+ *((char **)arg)= (char *)ma_pvio_tls_cipher(mysql->net.pvio->ctls);
+ else
+ #endif
+ goto error;
+ break;
+ case MARIADB_CLIENT_ERRORS:
+ *((char ***)arg)= (char **)client_errors;
+ break;
+ case MARIADB_CONNECTION_INFO:
+ if (mysql)
+ *((char **)arg)= (char *)mysql->info;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_PVIO_TYPE:
+ if (mysql && mysql->net.pvio)
+ *((unsigned int *)arg)= (unsigned int)mysql->net.pvio->type;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SCHEMA:
+ if (mysql)
+ *((char **)arg)= mysql->db;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_USER:
+ if (mysql)
+ *((char **)arg)= mysql->user;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_PORT:
+ if (mysql)
+ *((unsigned int *)arg)= mysql->port;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_UNIX_SOCKET:
+ if (mysql)
+ *((char **)arg)= mysql->unix_socket;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_HOST:
+ if (mysql)
+ *((char **)arg)= mysql->host;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_STATUS:
+ if (mysql)
+ *((unsigned int *)arg)= mysql->server_status;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_CAPABILITIES:
+ if (mysql)
+ *((unsigned long *)arg)= mysql->server_capabilities;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES:
+ if (mysql)
+ *((unsigned long *)arg)= mysql->extension->mariadb_server_capabilities;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_CLIENT_CAPABILITIES:
+ if (mysql)
+ *((unsigned long *)arg)= mysql->client_flag;
+ else
+ goto error;
+ break;
+ default:
+ va_end(ap);
+ return(-1);
+ }
+ va_end(ap);
+ return(0);
+error:
+ va_end(ap);
+ return(-1);
+}
+
+my_bool STDCALL mariadb_get_info(MYSQL *mysql, enum mariadb_value value, void *arg)
+{
+ return mariadb_get_infov(mysql, value, arg);
+}
+
+/*
+ Immediately aborts connection, making all subsequent read/write operations fail.
+ Does not invalidate memory used for mysql structure, nor closes any communication
+ channels - mysql_close is still needed.
+ Useful to break long query, in situation sending KILL is not possible.
+*/
+int STDCALL mariadb_cancel(MYSQL *mysql)
+{
+ if (!mysql || !mysql->net.pvio || !mysql->net.pvio->methods || !mysql->net.pvio->methods->shutdown)
+ {
+ return 1;
+ }
+ else
+ {
+ MARIADB_PVIO *pvio = mysql->net.pvio;
+ return pvio->methods->shutdown(pvio);
+ }
+}
+
+/* compatibility functions for MariaDB */
+void STDCALL
+mysql_debug(const char *debug __attribute__((unused)))
+{
+ return;
+}
+
+/********************************************************************
+ mysql_net_ functions - low-level API to MySQL protocol
+*********************************************************************/
+ulong STDCALL mysql_net_read_packet(MYSQL *mysql)
+{
+ return ma_net_safe_read(mysql);
+}
+
+ulong STDCALL mysql_net_field_length(uchar **packet)
+{
+ return net_field_length(packet);
+}
+
+my_bool STDCALL mysql_embedded(void)
+{
+#ifdef EMBEDDED_LIBRARY
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+MYSQL_PARAMETERS *STDCALL
+mysql_get_parameters(void)
+{
+ return &mariadb_internal_parameters;
+}
+
+int STDCALL mysql_reset_connection(MYSQL *mysql)
+{
+ int rc;
+
+ /* check if connection handler is active */
+ if (IS_CONNHDLR_ACTIVE(mysql))
+ {
+ if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reset)
+ return(mysql->extension->conn_hdlr->plugin->reset(mysql));
+ }
+
+ /* skip result sets */
+ if (mysql->status == MYSQL_STATUS_USE_RESULT ||
+ mysql->status == MYSQL_STATUS_GET_RESULT ||
+ mysql->status & SERVER_MORE_RESULTS_EXIST)
+ {
+ mthd_my_skip_result(mysql);
+ mysql->status= MYSQL_STATUS_READY;
+ }
+
+ rc= ma_simple_command(mysql, COM_RESET_CONNECTION, 0, 0, 0, 0);
+ if (rc && mysql->options.reconnect)
+ {
+ /* There is no big sense in resetting but we need reconnect */
+ rc= ma_simple_command(mysql, COM_RESET_CONNECTION,0,0,0,0);
+ }
+ if (rc)
+ return 1;
+
+ /* reset the connection in all active statements */
+ ma_invalidate_stmts(mysql, "mysql_reset_connection()");
+ free_old_query(mysql);
+ mysql->status= MYSQL_STATUS_READY;
+ mysql->affected_rows= ~(my_ulonglong)0;
+ mysql->insert_id= 0;
+ return 0;
+}
+
+#undef STDCALL
+/* API functions for usage in dynamic plugins */
+struct st_mariadb_api MARIADB_API=
+{
+ mysql_num_rows,
+ mysql_num_fields,
+ mysql_eof,
+ mysql_fetch_field_direct,
+ mysql_fetch_fields,
+ mysql_row_tell,
+ mysql_field_tell,
+ mysql_field_count,
+ mysql_more_results,
+ mysql_next_result,
+ mysql_affected_rows,
+ mysql_autocommit,
+ mysql_commit,
+ mysql_rollback,
+ mysql_insert_id,
+ mysql_errno,
+ mysql_error,
+ mysql_info,
+ mysql_thread_id,
+ mysql_character_set_name,
+ mysql_get_character_set_info,
+ mysql_set_character_set,
+ mariadb_get_infov,
+ mariadb_get_info,
+ mysql_init,
+ mysql_ssl_set,
+ mysql_get_ssl_cipher,
+ mysql_change_user,
+ mysql_real_connect,
+ mysql_close,
+ mysql_select_db,
+ mysql_query,
+ mysql_send_query,
+ mysql_read_query_result,
+ mysql_real_query,
+ mysql_shutdown,
+ mysql_dump_debug_info,
+ mysql_refresh,
+ mysql_kill,
+ mysql_ping,
+ mysql_stat,
+ mysql_get_server_info,
+ mysql_get_server_version,
+ mysql_get_host_info,
+ mysql_get_proto_info,
+ mysql_list_dbs,
+ mysql_list_tables,
+ mysql_list_fields,
+ mysql_list_processes,
+ mysql_store_result,
+ mysql_use_result,
+ mysql_options,
+ mysql_free_result,
+ mysql_data_seek,
+ mysql_row_seek,
+ mysql_field_seek,
+ mysql_fetch_row,
+ mysql_fetch_lengths,
+ mysql_fetch_field,
+ mysql_escape_string,
+ mysql_real_escape_string,
+ mysql_thread_safe,
+ mysql_warning_count,
+ mysql_sqlstate,
+ mysql_server_init,
+ mysql_server_end,
+ mysql_thread_end,
+ mysql_thread_init,
+ mysql_set_server_option,
+ mysql_get_client_info,
+ mysql_get_client_version,
+ mariadb_connection,
+ mysql_get_server_name,
+ mariadb_get_charset_by_name,
+ mariadb_get_charset_by_nr,
+ mariadb_convert_string,
+ mysql_optionsv,
+ mysql_get_optionv,
+ mysql_get_option,
+ mysql_hex_string,
+ mysql_get_socket,
+ mysql_get_timeout_value,
+ mysql_get_timeout_value_ms,
+ mariadb_reconnect,
+ mysql_stmt_init,
+ mysql_stmt_prepare,
+ mysql_stmt_execute,
+ mysql_stmt_fetch,
+ mysql_stmt_fetch_column,
+ mysql_stmt_store_result,
+ mysql_stmt_param_count,
+ mysql_stmt_attr_set,
+ mysql_stmt_attr_get,
+ mysql_stmt_bind_param,
+ mysql_stmt_bind_result,
+ mysql_stmt_close,
+ mysql_stmt_reset,
+ mysql_stmt_free_result,
+ mysql_stmt_send_long_data,
+ mysql_stmt_result_metadata,
+ mysql_stmt_param_metadata,
+ mysql_stmt_errno,
+ mysql_stmt_error,
+ mysql_stmt_sqlstate,
+ mysql_stmt_row_seek,
+ mysql_stmt_row_tell,
+ mysql_stmt_data_seek,
+ mysql_stmt_num_rows,
+ mysql_stmt_affected_rows,
+ mysql_stmt_insert_id,
+ mysql_stmt_field_count,
+ mysql_stmt_next_result,
+ mysql_stmt_more_results,
+ mariadb_stmt_execute_direct,
+ mysql_reset_connection
+};
+
+/*
+ * Default methods for a connection. These methods are
+ * stored in mysql->methods and can be overwritten by
+ * a plugin, e.g. for using another database
+ */
+struct st_mariadb_methods MARIADB_DEFAULT_METHODS = {
+ /* open a connection */
+ mthd_my_real_connect,
+ /* close connection */
+ mysql_close_slow_part,
+ /* send command to server */
+ mthd_my_send_cmd,
+ /* skip result set */
+ mthd_my_skip_result,
+ /* read response packet */
+ mthd_my_read_query_result,
+ /* read all rows from a result set */
+ mthd_my_read_rows,
+ /* read one/next row */
+ mthd_my_read_one_row,
+ /* check if datatype is supported */
+ mthd_supported_buffer_type,
+ /* read response packet from prepare */
+ mthd_stmt_read_prepare_response,
+ /* read response from stmt execute */
+ mthd_my_read_query_result,
+ /* get result set metadata for a prepared statement */
+ mthd_stmt_get_result_metadata,
+ /* get param metadata for a prepared statement */
+ mthd_stmt_get_param_metadata,
+ /* read all rows (buffered) */
+ mthd_stmt_read_all_rows,
+ /* fetch one row (unbuffered) */
+ mthd_stmt_fetch_row,
+ /* store values in bind buffer */
+ mthd_stmt_fetch_to_bind,
+ /* skip unbuffered stmt result */
+ mthd_stmt_flush_unbuffered,
+ /* set error */
+ my_set_error,
+ /* invalidate statements */
+ ma_invalidate_stmts,
+ &MARIADB_API
+};
diff --git a/mysql/libmariadb/mariadb_stmt.c b/mysql/libmariadb/mariadb_stmt.c
new file mode 100644
index 0000000..d3eb042
--- /dev/null
+++ b/mysql/libmariadb/mariadb_stmt.c
@@ -0,0 +1,2419 @@
+/****************************************************************************
+ Copyright (C) 2012 Monty Program AB
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not see <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ Part of this code includes code from the PHP project which
+ is freely available from http://www.php.net
+ *****************************************************************************/
+
+/* The implementation for prepared statements was ported from PHP's mysqlnd
+ extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
+
+ Original file header:
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "ma_global.h"
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include "mysql.h"
+#include "errmsg.h"
+#include <ma_pvio.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <mysql/client_plugin.h>
+#include <ma_common.h>
+
+#define STMT_NUM_OFS(type, a,r) ((type *)(a))[r]
+#define MADB_RESET_ERROR 1
+#define MADB_RESET_LONGDATA 2
+#define MADB_RESET_SERVER 4
+#define MADB_RESET_BUFFER 8
+#define MADB_RESET_STORED 16
+
+#define MAX_TIME_STR_LEN 13
+#define MAX_DATE_STR_LEN 5
+#define MAX_DATETIME_STR_LEN 12
+
+typedef struct
+{
+ MA_MEM_ROOT fields_ma_alloc_root;
+} MADB_STMT_EXTENSION;
+
+MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, uint fields);
+void free_rows(MYSQL_DATA *cur);
+int ma_multi_command(MYSQL *mysql, enum enum_multi_status status);
+MYSQL_FIELD * unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields, my_bool default_value, my_bool long_flag_protocol);
+static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove);
+
+static my_bool is_not_null= 0;
+static my_bool is_null= 1;
+
+void stmt_set_error(MYSQL_STMT *stmt,
+ unsigned int error_nr,
+ const char *sqlstate,
+ const char *format,
+ ...)
+{
+ va_list ap;
+
+ stmt->last_errno= error_nr;
+ ma_strmake(stmt->sqlstate, sqlstate, SQLSTATE_LENGTH);
+ va_start(ap, format);
+ vsnprintf(stmt->last_error, MYSQL_ERRMSG_SIZE,
+ format ? format : ER(error_nr), ap);
+ va_end(ap);
+ return;
+}
+
+my_bool mthd_supported_buffer_type(enum enum_field_types type)
+{
+ switch (type) {
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_YEAR:
+ return 1;
+ break;
+ default:
+ return 0;
+ break;
+ }
+}
+
+static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags);
+static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close);
+static int stmt_unbuffered_eof(MYSQL_STMT *stmt __attribute__((unused)),
+ uchar **row __attribute__((unused)))
+{
+ return MYSQL_NO_DATA;
+}
+
+static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row)
+{
+ ulong pkt_len;
+
+ pkt_len= ma_net_safe_read(stmt->mysql);
+
+ if (pkt_len == packet_error)
+ {
+ stmt->fetch_row_func= stmt_unbuffered_eof;
+ return(1);
+ }
+
+ if (stmt->mysql->net.read_pos[0] == 254)
+ {
+ *row = NULL;
+ stmt->fetch_row_func= stmt_unbuffered_eof;
+ return(MYSQL_NO_DATA);
+ }
+ else
+ *row = stmt->mysql->net.read_pos;
+ stmt->result.rows++;
+ return(0);
+}
+
+static int stmt_buffered_fetch(MYSQL_STMT *stmt, uchar **row)
+{
+ if (!stmt->result_cursor)
+ {
+ *row= NULL;
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ return MYSQL_NO_DATA;
+ }
+ stmt->state= MYSQL_STMT_USER_FETCHING;
+ *row= (uchar *)stmt->result_cursor->data;
+
+ stmt->result_cursor= stmt->result_cursor->next;
+ return 0;
+}
+
+int mthd_stmt_read_all_rows(MYSQL_STMT *stmt)
+{
+ MYSQL_DATA *result= &stmt->result;
+ MYSQL_ROWS *current, **pprevious;
+ ulong packet_len;
+ unsigned char *p;
+
+ pprevious= &result->data;
+
+ while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error)
+ {
+ p= stmt->mysql->net.read_pos;
+ if (packet_len > 7 || p[0] != 254)
+ {
+ /* allocate space for rows */
+ if (!(current= (MYSQL_ROWS *)ma_alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + packet_len)))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ current->data= (MYSQL_ROW)(current + 1);
+ *pprevious= current;
+ pprevious= &current->next;
+
+ /* copy binary row, we will encode it during mysql_stmt_fetch */
+ memcpy((char *)current->data, (char *)p, packet_len);
+
+ if (stmt->update_max_length)
+ {
+ uchar *null_ptr, bit_offset= 4;
+ uchar *cp= p;
+ unsigned int i;
+
+ cp++; /* skip first byte */
+ null_ptr= cp;
+ cp+= (stmt->field_count + 9) / 8;
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ if (!(*null_ptr & bit_offset))
+ {
+ if (mysql_ps_fetch_functions[stmt->fields[i].type].pack_len < 0)
+ {
+ /* We need to calculate the sizes for date and time types */
+ size_t len= net_field_length(&cp);
+ switch(stmt->fields[i].type) {
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ stmt->fields[i].max_length= mysql_ps_fetch_functions[stmt->fields[i].type].max_len;
+ break;
+ default:
+ if (len > stmt->fields[i].max_length)
+ stmt->fields[i].max_length= (ulong)len;
+ break;
+ }
+ cp+= len;
+ }
+ else
+ {
+ if (!stmt->fields[i].max_length)
+ stmt->fields[i].max_length= mysql_ps_fetch_functions[stmt->fields[i].type].max_len;
+ cp+= mysql_ps_fetch_functions[stmt->fields[i].type].pack_len;
+ }
+ }
+ if (!((bit_offset <<=1) & 255))
+ {
+ bit_offset= 1; /* To next byte */
+ null_ptr++;
+ }
+ }
+ }
+ current->length= packet_len;
+ result->rows++;
+ } else /* end of stream */
+ {
+ *pprevious= 0;
+ /* sace status info */
+ p++;
+ stmt->upsert_status.warning_count= stmt->mysql->warning_count= uint2korr(p);
+ p+=2;
+ stmt->upsert_status.server_status= stmt->mysql->server_status= uint2korr(p);
+ stmt->result_cursor= result->data;
+ return(0);
+ }
+ }
+ stmt->result_cursor= 0;
+ SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate,
+ stmt->mysql->net.last_error);
+ return(1);
+}
+
+static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row)
+{
+ uchar buf[STMT_ID_LENGTH + 4];
+ MYSQL_DATA *result= &stmt->result;
+
+ if (stmt->state < MYSQL_STMT_USE_OR_STORE_CALLED)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* do we have some prefetched rows available ? */
+ if (stmt->result_cursor)
+ return(stmt_buffered_fetch(stmt, row));
+ if (stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT)
+ stmt->upsert_status.server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
+ else
+ {
+ int4store(buf, stmt->stmt_id);
+ int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows);
+
+ if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt))
+ return(1);
+
+ /* free previously allocated buffer */
+ ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
+ result->data= 0;
+ result->rows= 0;
+
+ if (stmt->mysql->methods->db_stmt_read_all_rows(stmt))
+ return(1);
+
+ return(stmt_buffered_fetch(stmt, row));
+ }
+ /* no more cursor data available */
+ *row= NULL;
+ return(MYSQL_NO_DATA);
+}
+
+/* flush one result set */
+void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt)
+{
+ ulong packet_len;
+ int in_resultset= stmt->state > MYSQL_STMT_EXECUTED &&
+ stmt->state < MYSQL_STMT_FETCH_DONE;
+ while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error)
+ {
+ uchar *pos= stmt->mysql->net.read_pos;
+ if (!in_resultset && *pos == 0) /* OK */
+ {
+ pos++;
+ net_field_length(&pos);
+ net_field_length(&pos);
+ stmt->mysql->server_status= uint2korr(pos);
+ goto end;
+ }
+ if (packet_len < 8 && *pos == 254) /* EOF */
+ {
+ stmt->mysql->server_status= uint2korr(pos + 3);
+ if (in_resultset)
+ goto end;
+ in_resultset= 1;
+ }
+ }
+end:
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+}
+
+int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row)
+{
+ uint i;
+ size_t truncations= 0;
+ unsigned char *null_ptr, bit_offset= 4;
+ row++; /* skip status byte */
+ null_ptr= row;
+ row+= (stmt->field_count + 9) / 8;
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ /* save row position for fetching values in pieces */
+ if (*null_ptr & bit_offset)
+ {
+ if (!stmt->bind[i].is_null)
+ stmt->bind[i].is_null= &stmt->bind[i].is_null_value;
+ *stmt->bind[i].is_null= 1;
+ stmt->bind[i].u.row_ptr= NULL;
+ } else
+ {
+ stmt->bind[i].u.row_ptr= row;
+ if (!stmt->bind_result_done ||
+ stmt->bind[i].flags & MADB_BIND_DUMMY)
+ {
+ unsigned long length;
+
+ if (mysql_ps_fetch_functions[stmt->fields[i].type].pack_len >= 0)
+ length= mysql_ps_fetch_functions[stmt->fields[i].type].pack_len;
+ else
+ length= net_field_length(&row);
+ row+= length;
+ if (!stmt->bind[i].length)
+ stmt->bind[i].length= &stmt->bind[i].length_value;
+ *stmt->bind[i].length= stmt->bind[i].length_value= length;
+ }
+ else
+ {
+ if (!stmt->bind[i].length)
+ stmt->bind[i].length= &stmt->bind[i].length_value;
+ if (!stmt->bind[i].is_null)
+ stmt->bind[i].is_null= &stmt->bind[i].is_null_value;
+ *stmt->bind[i].is_null= 0;
+ mysql_ps_fetch_functions[stmt->fields[i].type].func(&stmt->bind[i], &stmt->fields[i], &row);
+ if (stmt->mysql->options.report_data_truncation)
+ truncations+= *stmt->bind[i].error;
+ }
+ }
+
+ if (!((bit_offset <<=1) & 255)) {
+ bit_offset= 1; /* To next byte */
+ null_ptr++;
+ }
+ }
+ return((truncations) ? MYSQL_DATA_TRUNCATED : 0);
+}
+
+MYSQL_RES *_mysql_stmt_use_result(MYSQL_STMT *stmt)
+{
+ MYSQL *mysql= stmt->mysql;
+
+ if (!stmt->field_count ||
+ (!stmt->cursor_exists && mysql->status != MYSQL_STATUS_STMT_RESULT) ||
+ (stmt->cursor_exists && mysql->status != MYSQL_STATUS_READY) ||
+ (stmt->state != MYSQL_STMT_WAITING_USE_OR_STORE))
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ stmt->state = MYSQL_STMT_USE_OR_STORE_CALLED;
+ if (!stmt->cursor_exists)
+ stmt->fetch_row_func= stmt_unbuffered_fetch; //mysql_stmt_fetch_unbuffered_row;
+ else
+ stmt->fetch_row_func= stmt_cursor_fetch;
+
+ return(NULL);
+}
+
+unsigned char *mysql_net_store_length(unsigned char *packet, size_t length)
+{
+ if (length < (unsigned long long) L64(251)) {
+ *packet = (unsigned char) length;
+ return packet + 1;
+ }
+
+ if (length < (unsigned long long) L64(65536)) {
+ *packet++ = 252;
+ int2store(packet,(uint) length);
+ return packet + 2;
+ }
+
+ if (length < (unsigned long long) L64(16777216)) {
+ *packet++ = 253;
+ int3store(packet,(ulong) length);
+ return packet + 3;
+ }
+ *packet++ = 254;
+ int8store(packet, length);
+ return packet + 8;
+}
+
+static long ma_get_length(MYSQL_STMT *stmt, unsigned int param_nr, unsigned long row_nr)
+{
+ if (!stmt->params[param_nr].length)
+ return 0;
+ if (stmt->row_size)
+ return *(long *)((char *)stmt->params[param_nr].length + row_nr * stmt->row_size);
+ else
+ return stmt->params[param_nr].length[row_nr];
+}
+
+static signed char ma_get_indicator(MYSQL_STMT *stmt, unsigned int param_nr, unsigned long row_nr)
+{
+ if (!MARIADB_STMT_BULK_SUPPORTED(stmt) ||
+ !stmt->array_size ||
+ !stmt->params[param_nr].u.indicator)
+ return 0;
+ if (stmt->row_size)
+ return *((char *)stmt->params[param_nr].u.indicator + (row_nr * stmt->row_size));
+ return stmt->params[param_nr].u.indicator[row_nr];
+}
+
+static void *ma_get_buffer_offset(MYSQL_STMT *stmt, enum enum_field_types type,
+ void *buffer, unsigned long row_nr)
+{
+ if (stmt->array_size)
+ {
+ int len;
+ if (stmt->row_size)
+ return (void *)((char *)buffer + stmt->row_size * row_nr);
+ len= mysql_ps_fetch_functions[type].pack_len;
+ if (len > 0)
+ return (void *)((char *)buffer + len * row_nr);
+ return ((void **)buffer)[row_nr];
+ }
+ return buffer;
+}
+
+int store_param(MYSQL_STMT *stmt, int column, unsigned char **p, unsigned long row_nr)
+{
+ void *buf= ma_get_buffer_offset(stmt, stmt->params[column].buffer_type,
+ stmt->params[column].buffer, row_nr);
+ signed char indicator= ma_get_indicator(stmt, column, row_nr);
+
+ switch (stmt->params[column].buffer_type) {
+ case MYSQL_TYPE_TINY:
+ int1store(*p, (*(uchar *)buf));
+ (*p) += 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ int2store(*p, (*(short *)buf));
+ (*p) += 2;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ float4store(*p, (*(float *)buf));
+ (*p) += 4;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ float8store(*p, (*(double *)buf));
+ (*p) += 8;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ int8store(*p, (*(ulonglong *)buf));
+ (*p) += 8;
+ break;
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_INT24:
+ int4store(*p, (*(int32 *)buf));
+ (*p)+= 4;
+ break;
+ case MYSQL_TYPE_TIME:
+ {
+ /* binary encoding:
+ Offset Length Field
+ 0 1 Length
+ 1 1 negative
+ 2-5 4 day
+ 6 1 hour
+ 7 1 ninute
+ 8 1 second;
+ 9-13 4 second_part
+ */
+ MYSQL_TIME *t= (MYSQL_TIME *)ma_get_buffer_offset(stmt, stmt->params[column].buffer_type,
+ stmt->params[column].buffer, row_nr);
+ char t_buffer[MAX_TIME_STR_LEN];
+ uint len= 0;
+
+ t_buffer[1]= t->neg ? 1 : 0;
+ int4store(t_buffer + 2, t->day);
+ t_buffer[6]= (uchar) t->hour;
+ t_buffer[7]= (uchar) t->minute;
+ t_buffer[8]= (uchar) t->second;
+ if (t->second_part)
+ {
+ int4store(t_buffer + 9, t->second_part);
+ len= 12;
+ }
+ else if (t->day || t->hour || t->minute || t->second)
+ len= 8;
+ t_buffer[0]= len++;
+ memcpy(*p, t_buffer, len);
+ (*p)+= len;
+ break;
+ }
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATETIME:
+ {
+ /* binary format for date, timestamp and datetime
+ Offset Length Field
+ 0 1 Length
+ 1-2 2 Year
+ 3 1 Month
+ 4 1 Day
+ 5 1 Hour
+ 6 1 minute
+ 7 1 second
+ 8-11 4 secondpart
+ */
+ MYSQL_TIME *t= (MYSQL_TIME *)ma_get_buffer_offset(stmt, stmt->params[column].buffer_type,
+ stmt->params[column].buffer, row_nr);
+ char t_buffer[MAX_DATETIME_STR_LEN];
+ uint len= 0;
+
+ int2store(t_buffer + 1, t->year);
+ t_buffer[3]= (char) t->month;
+ t_buffer[4]= (char) t->day;
+ t_buffer[5]= (char) t->hour;
+ t_buffer[6]= (char) t->minute;
+ t_buffer[7]= (char) t->second;
+ if (t->second_part)
+ {
+ int4store(t_buffer + 8, t->second_part);
+ len= 11;
+ }
+ else if (t->hour || t->minute || t->second)
+ len= 7;
+ else if (t->year || t->month || t->day)
+ len= 4;
+ else
+ len=0;
+ t_buffer[0]= len++;
+ memcpy(*p, t_buffer, len);
+ (*p)+= len;
+ break;
+ }
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ ulong len;
+ /* to is after p. The latter hasn't been moved */
+ uchar *to;
+
+ if (indicator == STMT_INDICATOR_NTS)
+ len= -1;
+ else
+ len= ma_get_length(stmt, column, row_nr);
+
+ if (len == (ulong)-1)
+ len= (ulong)strlen((char *)buf);
+
+ to = mysql_net_store_length(*p, len);
+
+ if (len)
+ memcpy(to, buf, len);
+ (*p) = to + len;
+ break;
+ }
+
+ default:
+ /* unsupported parameter type */
+ SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return 1;
+ }
+ return 0;
+}
+
+/* {{{ mysqlnd_stmt_execute_generate_simple_request */
+unsigned char* mysql_stmt_execute_generate_simple_request(MYSQL_STMT *stmt, size_t *request_len)
+{
+ /* execute packet has the following format:
+ Offset Length Description
+ -----------------------------------------
+ 0 4 Statement id
+ 4 1 Flags (cursor type)
+ 5 4 Iteration count
+ -----------------------------------------
+ if (stmt->param_count):
+ 6 (paramcount+7)/8 null bitmap
+ ------------------------------------------
+ if (stmt->send_types_to_server):
+ param_count*2 parameter types
+ 1st byte: parameter type
+ 2nd byte flag:
+ unsigned flag (32768)
+ indicator variable exists (16384)
+ ------------------------------------------
+ n data from bind_buffer
+
+ */
+
+ size_t length= 1024;
+ size_t free_bytes= 0;
+ size_t null_byte_offset= 0;
+ uint i;
+
+ uchar *start= NULL, *p;
+
+ /* preallocate length bytes */
+ /* check: gr */
+ if (!(start= p= (uchar *)malloc(length)))
+ goto mem_error;
+
+ int4store(p, stmt->stmt_id);
+ p += STMT_ID_LENGTH;
+
+ /* flags is 4 bytes, we store just 1 */
+ int1store(p, (unsigned char) stmt->flags);
+ p++;
+
+ int4store(p, 1);
+ p+= 4;
+
+ if (stmt->param_count)
+ {
+ size_t null_count= (stmt->param_count + 7) / 8;
+
+ free_bytes= length - (p - start);
+ if (null_count + 20 > free_bytes)
+ {
+ size_t offset= p - start;
+ length+= offset + null_count + 20;
+ if (!(start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ p= start + offset;
+ }
+
+ null_byte_offset= p - start;
+ memset(p, 0, null_count);
+ p += null_count;
+
+ int1store(p, stmt->send_types_to_server);
+ p++;
+
+ free_bytes= length - (p - start);
+
+ /* Store type information:
+ 2 bytes per type
+ */
+ if (stmt->send_types_to_server)
+ {
+ if (free_bytes < stmt->param_count * 2 + 20)
+ {
+ size_t offset= p - start;
+ length= offset + stmt->param_count * 2 + 20;
+ if (!(start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ p= start + offset;
+ }
+ for (i = 0; i < stmt->param_count; i++)
+ {
+ /* this differs from mysqlnd, c api supports unsinged !! */
+ uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0);
+ /* check if parameter requires indicator variable */
+ int2store(p, buffer_type);
+ p+= 2;
+ }
+ }
+
+ /* calculate data size */
+ for (i=0; i < stmt->param_count; i++)
+ {
+ size_t size= 0;
+ my_bool has_data= TRUE;
+
+ if (stmt->params[i].long_data_used)
+ {
+ has_data= FALSE;
+ stmt->params[i].long_data_used= 0;
+ }
+
+ if (has_data)
+ {
+ switch (stmt->params[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ has_data= FALSE;
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_SET:
+ size+= 5; /* max 8 bytes for size */
+ size+= (size_t)ma_get_length(stmt, i, 0);
+ break;
+ default:
+ size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len;
+ break;
+ }
+ }
+ free_bytes= length - (p - start);
+ if (free_bytes < size + 20)
+ {
+ size_t offset= p - start;
+ length= MAX(2 * length, offset + size + 20);
+ if (!(start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ p= start + offset;
+ }
+ if (((stmt->params[i].is_null && *stmt->params[i].is_null) ||
+ stmt->params[i].buffer_type == MYSQL_TYPE_NULL ||
+ !stmt->params[i].buffer))
+ {
+ has_data= FALSE;
+ (start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7));
+ }
+
+ if (has_data)
+ {
+ store_param(stmt, i, &p, 0);
+ }
+ }
+ }
+ stmt->send_types_to_server= 0;
+ *request_len = (size_t)(p - start);
+ return start;
+mem_error:
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ free(start);
+ *request_len= 0;
+ return NULL;
+}
+/* }}} */
+
+/* {{{ mysql_stmt_skip_paramset */
+my_bool mysql_stmt_skip_paramset(MYSQL_STMT *stmt, uint row)
+{
+ uint i;
+ for (i=0; i < stmt->param_count; i++)
+ {
+ if (ma_get_indicator(stmt, i, row) == STMT_INDICATOR_IGNORE_ROW)
+ return '\1';
+ }
+
+ return '\0';
+}
+/* }}} */
+
+/* {{{ mysql_stmt_execute_generate_bulk_request */
+unsigned char* mysql_stmt_execute_generate_bulk_request(MYSQL_STMT *stmt, size_t *request_len)
+{
+ /* execute packet has the following format:
+ Offset Length Description
+ -----------------------------------------
+ 0 4 Statement id
+ 4 2 Flags (cursor type):
+ STMT_BULK_FLAG_CLIENT_SEND_TYPES = 128
+ STMT_BULK_FLAG_INSERT_ID_REQUEST = 64
+ -----------------------------------------
+ if (stmt->send_types_to_server):
+ for (i=0; i < param_count; i++)
+ 1st byte: parameter type
+ 2nd byte flag:
+ unsigned flag (32768)
+ ------------------------------------------
+ for (i=0; i < param_count; i++)
+ 1 indicator variable
+ STMT_INDICATOR_NONE 0
+ STMT_INDICATOR_NULL 1
+ STMT_INDICATOR_DEFAULT 2
+ STMT_INDICATOR_IGNORE 3
+ STMT_INDICATOR_SKIP_SET 4
+ n data from bind buffer
+
+ */
+
+ size_t length= 1024;
+ size_t free_bytes= 0;
+ ushort flags= 0;
+ uint i, j;
+
+ uchar *start= NULL, *p;
+
+ if (!MARIADB_STMT_BULK_SUPPORTED(stmt))
+ {
+ stmt_set_error(stmt, CR_FUNCTION_NOT_SUPPORTED, "IM001",
+ CER(CR_FUNCTION_NOT_SUPPORTED), "Bulk operation");
+ return NULL;
+ }
+
+ if (!stmt->param_count)
+ {
+ stmt_set_error(stmt, CR_BULK_WITHOUT_PARAMETERS, "IM001",
+ CER(CR_BULK_WITHOUT_PARAMETERS), "Bulk operation");
+ return NULL;
+ }
+
+ /* preallocate length bytes */
+ if (!(start= p= (uchar *)malloc(length)))
+ goto mem_error;
+
+ int4store(p, stmt->stmt_id);
+ p += STMT_ID_LENGTH;
+
+ /* todo: request to return auto generated ids */
+ if (stmt->send_types_to_server)
+ flags|= STMT_BULK_FLAG_CLIENT_SEND_TYPES;
+ int2store(p, flags);
+ p+=2;
+
+ /* When using mariadb_stmt_execute_direct stmt->paran_count is
+ not knowm, so we need to assign prebind_params, which was previously
+ set by mysql_stmt_attr_set
+ */
+ if (!stmt->param_count && stmt->prebind_params)
+ stmt->param_count= stmt->prebind_params;
+
+ if (stmt->param_count)
+ {
+ free_bytes= length - (p - start);
+
+ /* Store type information:
+ 2 bytes per type
+ */
+ if (stmt->send_types_to_server)
+ {
+ if (free_bytes < stmt->param_count * 2 + 20)
+ {
+ size_t offset= p - start;
+ length= offset + stmt->param_count * 2 + 20;
+ if (!(start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ p= start + offset;
+ }
+ for (i = 0; i < stmt->param_count; i++)
+ {
+ /* this differs from mysqlnd, c api supports unsinged !! */
+ uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0);
+ int2store(p, buffer_type);
+ p+= 2;
+ }
+ }
+
+ /* calculate data size */
+ for (j=0; j < stmt->array_size; j++)
+ {
+ if (mysql_stmt_skip_paramset(stmt, j))
+ continue;
+
+ for (i=0; i < stmt->param_count; i++)
+ {
+ size_t size= 0;
+ my_bool has_data= TRUE;
+ signed char indicator= ma_get_indicator(stmt, i, j);
+ /* check if we need to send data */
+ if (indicator > 0)
+ has_data= FALSE;
+ size= 1;
+
+ /* Please note that mysql_stmt_send_long_data is not supported
+ current when performing bulk execute */
+
+ if (has_data)
+ {
+ switch (stmt->params[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ has_data= FALSE;
+ indicator= STMT_INDICATOR_NULL;
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_SET:
+ size+= 5; /* max 8 bytes for size */
+ if (indicator == STMT_INDICATOR_NTS ||
+ (!stmt->row_size && ma_get_length(stmt,i,j) == -1))
+ {
+ size+= strlen(ma_get_buffer_offset(stmt,
+ stmt->params[i].buffer_type,
+ stmt->params[i].buffer,j));
+ }
+ else
+ size+= (size_t)ma_get_length(stmt, i, j);
+ break;
+ default:
+ size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len;
+ break;
+ }
+ }
+ free_bytes= length - (p - start);
+ if (free_bytes < size + 20)
+ {
+ size_t offset= p - start;
+ length= MAX(2 * length, offset + size + 20);
+ if (!(start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ p= start + offset;
+ }
+
+ int1store(p, indicator > 0 ? indicator : 0);
+ p++;
+ if (has_data)
+ store_param(stmt, i, &p, j);
+ }
+ }
+
+ }
+ stmt->send_types_to_server= 0;
+ *request_len = (size_t)(p - start);
+ return start;
+mem_error:
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ free(start);
+ *request_len= 0;
+ return NULL;
+}
+/* }}} */
+/*!
+ *******************************************************************************
+
+ \fn unsigned long long mysql_stmt_affected_rows
+ \brief returns the number of affected rows from last mysql_stmt_execute
+ call
+
+ \param[in] stmt The statement handle
+ *******************************************************************************
+ */
+unsigned long long STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt)
+{
+ return stmt->upsert_status.affected_rows;
+}
+
+my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, void *value)
+{
+ switch (attr_type) {
+ case STMT_ATTR_UPDATE_MAX_LENGTH:
+ *(my_bool *)value= stmt->update_max_length;
+ break;
+ case STMT_ATTR_CURSOR_TYPE:
+ *(unsigned long *)value= stmt->flags;
+ break;
+ case STMT_ATTR_PREFETCH_ROWS:
+ *(unsigned long *)value= stmt->prefetch_rows;
+ break;
+ case STMT_ATTR_PREBIND_PARAMS:
+ *(unsigned int *)value= stmt->prebind_params;
+ break;
+ case STMT_ATTR_ARRAY_SIZE:
+ *(unsigned int *)value= stmt->array_size;
+ break;
+ case STMT_ATTR_ROW_SIZE:
+ *(size_t *)value= stmt->row_size;
+ break;
+ default:
+ return(1);
+ }
+ return(0);
+}
+
+my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *value)
+{
+ switch (attr_type) {
+ case STMT_ATTR_UPDATE_MAX_LENGTH:
+ stmt->update_max_length= *(my_bool *)value;
+ break;
+ case STMT_ATTR_CURSOR_TYPE:
+ if (*(ulong *)value > (unsigned long) CURSOR_TYPE_READ_ONLY)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ stmt->flags = *(ulong *)value;
+ break;
+ case STMT_ATTR_PREFETCH_ROWS:
+ if (*(ulong *)value == 0)
+ *(long *)value= MYSQL_DEFAULT_PREFETCH_ROWS;
+ else
+ stmt->prefetch_rows= *(long *)value;
+ break;
+ case STMT_ATTR_PREBIND_PARAMS:
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ mysql_stmt_internal_reset(stmt, 1);
+ net_stmt_close(stmt, 0);
+ stmt->state= MYSQL_STMT_INITTED;
+ stmt->params= 0;
+ }
+ stmt->prebind_params= *(unsigned int *)value;
+ break;
+ case STMT_ATTR_ARRAY_SIZE:
+ stmt->array_size= *(unsigned int *)value;
+ break;
+ case STMT_ATTR_ROW_SIZE:
+ stmt->row_size= *(size_t *)value;
+ break;
+ default:
+ SET_CLIENT_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ return(0);
+}
+
+my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind)
+{
+ MYSQL *mysql= stmt->mysql;
+
+ if (!mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* If number of parameters was specified via mysql_stmt_attr_set we need to realloc
+ them, e.g. for mariadb_stmt_execute_direct()
+ */
+ if ((stmt->state < MYSQL_STMT_PREPARED || stmt->state >= MYSQL_STMT_EXECUTED) &&
+ stmt->prebind_params > 0)
+ {
+ if (!stmt->params && stmt->prebind_params)
+ {
+ if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->prebind_params * sizeof(MYSQL_BIND))))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ memset(stmt->params, '\0', stmt->prebind_params * sizeof(MYSQL_BIND));
+ }
+ stmt->param_count= stmt->prebind_params;
+ }
+ else if (stmt->state < MYSQL_STMT_PREPARED) {
+ SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->param_count && bind)
+ {
+ uint i;
+
+ memcpy(stmt->params, bind, sizeof(MYSQL_BIND) * stmt->param_count);
+ stmt->send_types_to_server= 1;
+
+ for (i=0; i < stmt->param_count; i++)
+ {
+ if (stmt->mysql->methods->db_supported_buffer_type &&
+ !stmt->mysql->methods->db_supported_buffer_type(stmt->params[i].buffer_type))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ if (!stmt->params[i].is_null)
+ stmt->params[i].is_null= &is_not_null;
+
+ if (stmt->params[i].long_data_used)
+ stmt->params[i].long_data_used= 0;
+
+ if (!stmt->params[i].length)
+ stmt->params[i].length= &stmt->params[i].buffer_length;
+
+ switch(stmt->params[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ stmt->params[i].is_null= &is_null;
+ break;
+ case MYSQL_TYPE_TINY:
+ stmt->params[i].buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ stmt->params[i].buffer_length= 2;
+ break;
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ stmt->params[i].buffer_length= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_DOUBLE:
+ stmt->params[i].buffer_length= 8;
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ stmt->params[i].buffer_length= 12;
+ break;
+ case MYSQL_TYPE_TIME:
+ stmt->params[i].buffer_length= 13;
+ break;
+ case MYSQL_TYPE_DATE:
+ stmt->params[i].buffer_length= 5;
+ break;
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ break;
+ default:
+ SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ break;
+ }
+ }
+ }
+ stmt->bind_param_done= stmt->send_types_to_server= 1;
+
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ return(0);
+}
+
+my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
+{
+ uint i;
+
+ if (stmt->state < MYSQL_STMT_PREPARED)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->field_count)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_NO_STMT_METADATA, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!bind)
+ return(1);
+
+ /* In case of a stored procedure we don't allocate memory for bind
+ in mysql_stmt_prepare
+ */
+
+ if (stmt->field_count && !stmt->bind)
+ {
+ MA_MEM_ROOT *fields_ma_alloc_root=
+ &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ }
+
+ memcpy(stmt->bind, bind, sizeof(MYSQL_BIND) * stmt->field_count);
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ if (stmt->mysql->methods->db_supported_buffer_type &&
+ !stmt->mysql->methods->db_supported_buffer_type(bind[i].buffer_type))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->bind[i].is_null)
+ stmt->bind[i].is_null= &stmt->bind[i].is_null_value;
+ if (!stmt->bind[i].length)
+ stmt->bind[i].length= &stmt->bind[i].length_value;
+ if (!stmt->bind[i].error)
+ stmt->bind[i].error= &stmt->bind[i].error_value;
+
+ /* set length values for numeric types */
+ switch(bind[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 0;
+ break;
+ case MYSQL_TYPE_TINY:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 2;
+ break;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_DOUBLE:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 8;
+ break;
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ *stmt->bind[i].length= stmt->bind[i].length_value= sizeof(MYSQL_TIME);
+ break;
+ default:
+ break;
+ }
+ }
+ stmt->bind_result_done= 1;
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ return(0);
+}
+
+static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove)
+{
+ char stmt_id[STMT_ID_LENGTH];
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+
+ /* clear memory */
+ ma_free_root(&stmt->result.alloc, MYF(0)); /* allocated in mysql_stmt_store_result */
+ ma_free_root(&stmt->mem_root,MYF(0));
+ ma_free_root(fields_ma_alloc_root, MYF(0));
+
+ if (stmt->mysql)
+ {
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+
+ /* remove from stmt list */
+ if (remove)
+ stmt->mysql->stmts= list_delete(stmt->mysql->stmts, &stmt->list);
+
+ /* check if all data are fetched */
+ if (stmt->mysql->status != MYSQL_STATUS_READY)
+ {
+ do {
+ stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
+ } while(mysql_stmt_more_results(stmt));
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ }
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ int4store(stmt_id, stmt->stmt_id);
+ if (stmt->mysql->methods->db_command(stmt->mysql,COM_STMT_CLOSE, stmt_id,
+ sizeof(stmt_id), 1, stmt))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, stmt->mysql->net.last_error);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
+{
+ my_bool rc;
+ if (stmt && stmt->mysql && stmt->mysql->net.pvio)
+ mysql_stmt_internal_reset(stmt, 1);
+
+ rc= net_stmt_close(stmt, 1);
+
+ free(stmt->extension);
+ free(stmt);
+
+ return(rc);
+}
+
+void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, unsigned long long offset)
+{
+ unsigned long long i= offset;
+ MYSQL_ROWS *ptr= stmt->result.data;
+
+ while(i-- && ptr)
+ ptr= ptr->next;
+
+ stmt->result_cursor= ptr;
+ stmt->state= MYSQL_STMT_USER_FETCHING;
+
+ return;
+}
+
+unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT *stmt)
+{
+ return stmt->last_errno;
+}
+
+const char * STDCALL mysql_stmt_error(MYSQL_STMT *stmt)
+{
+ return (const char *)stmt->last_error;
+}
+
+int mthd_stmt_fetch_row(MYSQL_STMT *stmt, unsigned char **row)
+{
+ return stmt->fetch_row_func(stmt, row);
+}
+
+int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt)
+{
+ unsigned char *row;
+ int rc;
+
+ if (stmt->state <= MYSQL_STMT_EXECUTED)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state < MYSQL_STMT_WAITING_USE_OR_STORE || !stmt->field_count)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ } else if (stmt->state== MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler(stmt);
+ }
+
+ if (stmt->state == MYSQL_STMT_FETCH_DONE)
+ return(MYSQL_NO_DATA);
+
+ if ((rc= stmt->mysql->methods->db_stmt_fetch(stmt, &row)))
+ {
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ /* to fetch data again, stmt must be executed again */
+ return(rc);
+ }
+
+ if ((rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row)))
+ {
+ return(rc);
+ }
+
+ stmt->state= MYSQL_STMT_USER_FETCHING;
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ return(0);
+}
+
+int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset)
+{
+ if (stmt->state < MYSQL_STMT_USER_FETCHING || column >= stmt->field_count ||
+ stmt->state == MYSQL_STMT_FETCH_DONE) {
+ SET_CLIENT_STMT_ERROR(stmt, CR_NO_DATA, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->bind[column].u.row_ptr)
+ {
+ /* we set row_ptr only for columns which contain data, so this must be a NULL column */
+ if (bind[0].is_null)
+ *bind[0].is_null= 1;
+ }
+ else
+ {
+ unsigned char *save_ptr;
+ if (bind[0].length)
+ *bind[0].length= *stmt->bind[column].length;
+ else
+ bind[0].length= &stmt->bind[column].length_value;
+ if (bind[0].is_null)
+ *bind[0].is_null= 0;
+ else
+ bind[0].is_null= &bind[0].is_null_value;
+ if (!bind[0].error)
+ bind[0].error= &bind[0].error_value;
+ *bind[0].error= 0;
+ bind[0].offset= offset;
+ save_ptr= stmt->bind[column].u.row_ptr;
+ mysql_ps_fetch_functions[stmt->fields[column].type].func(&bind[0], &stmt->fields[column], &stmt->bind[column].u.row_ptr);
+ stmt->bind[column].u.row_ptr= save_ptr;
+ }
+ return(0);
+}
+
+unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt)
+{
+ return stmt->field_count;
+}
+
+my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
+{
+ return madb_reset_stmt(stmt, MADB_RESET_LONGDATA | MADB_RESET_STORED |
+ MADB_RESET_BUFFER | MADB_RESET_ERROR);
+}
+
+MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql)
+{
+
+ MYSQL_STMT *stmt= NULL;
+
+ if (!(stmt= (MYSQL_STMT *)calloc(1, sizeof(MYSQL_STMT))) ||
+ !(stmt->extension= (MADB_STMT_EXTENSION *)calloc(1, sizeof(MADB_STMT_EXTENSION))))
+ {
+ free(stmt);
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+
+ /* fill mysql's stmt list */
+ stmt->list.data= stmt;
+ stmt->mysql= mysql;
+ stmt->stmt_id= 0;
+ mysql->stmts= list_add(mysql->stmts, &stmt->list);
+
+
+ /* clear flags */
+ strcpy(stmt->sqlstate, "00000");
+
+ stmt->state= MYSQL_STMT_INITTED;
+
+ /* set default */
+ stmt->prefetch_rows= 1;
+
+ ma_init_alloc_root(&stmt->mem_root, 2048, 2048);
+ ma_init_alloc_root(&stmt->result.alloc, 4096, 4096);
+ ma_init_alloc_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, 2048, 2048);
+
+ return(stmt);
+}
+
+my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt)
+{
+ ulong packet_length;
+ uchar *p;
+
+ if ((packet_length= ma_net_safe_read(stmt->mysql)) == packet_error)
+ return(1);
+
+ p= (uchar *)stmt->mysql->net.read_pos;
+
+ if (0xFF == p[0]) /* Error occured */
+ {
+ return(1);
+ }
+
+ p++;
+ stmt->stmt_id= uint4korr(p);
+ p+= 4;
+ stmt->field_count= uint2korr(p);
+ p+= 2;
+ stmt->param_count= uint2korr(p);
+ p+= 2;
+
+ /* filler */
+ p++;
+ /* for backward compatibility we also update mysql->warning_count */
+ stmt->mysql->warning_count= stmt->upsert_status.warning_count= uint2korr(p);
+ return(0);
+}
+
+my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt)
+{
+ MYSQL_DATA *result;
+
+ if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7)))
+ return(1);
+
+ free_rows(result);
+ return(0);
+}
+
+my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt)
+{
+ MYSQL_DATA *result;
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+
+ if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7)))
+ return(1);
+ if (!(stmt->fields= unpack_fields(result,fields_ma_alloc_root,
+ stmt->field_count, 0,
+ stmt->mysql->server_capabilities & CLIENT_LONG_FLAG)))
+ return(1);
+ return(0);
+}
+
+int STDCALL mysql_stmt_warning_count(MYSQL_STMT *stmt)
+{
+ return stmt->upsert_status.warning_count;
+}
+
+int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length)
+{
+ MYSQL *mysql= stmt->mysql;
+ int rc= 1;
+ my_bool is_multi= 0;
+
+ if (!stmt->mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (length == (unsigned long) -1)
+ length= (unsigned long)strlen(query);
+
+ /* clear flags */
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~0;
+
+ /* check if we have to clear results */
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ char stmt_id[STMT_ID_LENGTH];
+ is_multi= (mysql->net.extension->multi_status > COM_MULTI_OFF);
+ /* We need to semi-close the prepared statement:
+ reset stmt and free all buffers and close the statement
+ on server side. Statment handle will get a new stmt_id */
+
+ if (!is_multi)
+ ma_multi_command(mysql, COM_MULTI_ENABLED);
+
+ if (mysql_stmt_internal_reset(stmt, 1))
+ goto fail;
+
+ ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
+ ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0));
+
+ stmt->param_count= 0;
+ stmt->field_count= 0;
+ stmt->params= 0;
+
+ int4store(stmt_id, stmt->stmt_id);
+ if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id,
+ sizeof(stmt_id), 1, stmt))
+ goto fail;
+ }
+ if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, query, length, 1, stmt))
+ goto fail;
+
+ if (!is_multi && mysql->net.extension->multi_status == COM_MULTI_ENABLED)
+ ma_multi_command(mysql, COM_MULTI_END);
+
+ if (mysql->net.extension->multi_status > COM_MULTI_OFF)
+ return 0;
+
+ if (mysql->methods->db_read_prepare_response &&
+ mysql->methods->db_read_prepare_response(stmt))
+ goto fail;
+
+ /* metadata not supported yet */
+
+ if (stmt->param_count &&
+ stmt->mysql->methods->db_stmt_get_param_metadata(stmt))
+ {
+ goto fail;
+ }
+
+ /* allocated bind buffer for parameters */
+ if (stmt->field_count &&
+ stmt->mysql->methods->db_stmt_get_result_metadata(stmt))
+ {
+ goto fail;
+ }
+ if (stmt->param_count)
+ {
+ if (stmt->prebind_params)
+ {
+ if (stmt->prebind_params != stmt->param_count)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto fail;
+ }
+ } else {
+ if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND))))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto fail;
+ }
+ memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND));
+ }
+ }
+ /* allocated bind buffer for result */
+ if (stmt->field_count)
+ {
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto fail;
+ }
+ memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count);
+ }
+ stmt->state = MYSQL_STMT_PREPARED;
+ return(0);
+
+fail:
+ stmt->state= MYSQL_STMT_INITTED;
+ SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
+ mysql->net.last_error);
+ return(rc);
+}
+
+int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
+{
+ unsigned int last_server_status;
+
+ if (!stmt->mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->field_count)
+ return(0);
+
+ /* test_pure_coverage requires checking of error_no */
+ if (stmt->last_errno)
+ return(1);
+
+ if (stmt->state < MYSQL_STMT_EXECUTED)
+ {
+ SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ last_server_status= stmt->mysql->server_status;
+
+ /* if stmt is a cursor, we need to tell server to send all rows */
+ if (stmt->cursor_exists && stmt->mysql->status == MYSQL_STATUS_READY)
+ {
+ char buff[STMT_ID_LENGTH + 4];
+ int4store(buff, stmt->stmt_id);
+ int4store(buff + STMT_ID_LENGTH, (int)~0);
+
+ if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH,
+ buff, sizeof(buff), 1, stmt))
+ return(1);
+ /* todo: cursor */
+ }
+ else if (stmt->mysql->status != MYSQL_STATUS_STMT_RESULT)
+ {
+ SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->mysql->methods->db_stmt_read_all_rows(stmt))
+ {
+ /* error during read - reset stmt->data */
+ ma_free_root(&stmt->result.alloc, 0);
+ stmt->result.data= NULL;
+ stmt->result.rows= 0;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ return(1);
+ }
+
+ /* workaround for MDEV 6304:
+ more results not set if the resultset has
+ SERVER_PS_OUT_PARAMS set
+ */
+ if (last_server_status & SERVER_PS_OUT_PARAMS &&
+ !(stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST))
+ stmt->mysql->server_status|= SERVER_MORE_RESULTS_EXIST;
+
+ stmt->result_cursor= stmt->result.data;
+ stmt->fetch_row_func= stmt_buffered_fetch;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+
+ if (!stmt->result.rows)
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ else
+ stmt->state= MYSQL_STMT_USE_OR_STORE_CALLED;
+
+ /* set affected rows: see bug 2247 */
+ stmt->upsert_status.affected_rows= stmt->result.rows;
+ stmt->mysql->affected_rows= stmt->result.rows;
+
+ return(0);
+}
+
+static int madb_alloc_stmt_fields(MYSQL_STMT *stmt)
+{
+ uint i;
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+
+ if (stmt->mysql->field_count)
+ {
+ ma_free_root(fields_ma_alloc_root, MYF(0));
+ if (!(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root,
+ sizeof(MYSQL_FIELD) * stmt->mysql->field_count)))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ stmt->field_count= stmt->mysql->field_count;
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ if (stmt->mysql->fields[i].db)
+ stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].db);
+ if (stmt->mysql->fields[i].table)
+ stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].table);
+ if (stmt->mysql->fields[i].org_table)
+ stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].org_table);
+ if (stmt->mysql->fields[i].name)
+ stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].name);
+ if (stmt->mysql->fields[i].org_name)
+ stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].org_name);
+ if (stmt->mysql->fields[i].catalog)
+ stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].catalog);
+ stmt->fields[i].def= stmt->mysql->fields[i].def ? ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].def) : NULL;
+ stmt->fields[i].type= stmt->mysql->fields[i].type;
+ stmt->fields[i].length= stmt->mysql->fields[i].length;
+ stmt->fields[i].flags= stmt->mysql->fields[i].flags;
+ stmt->fields[i].decimals= stmt->mysql->fields[i].decimals;
+ stmt->fields[i].charsetnr= stmt->mysql->fields[i].charsetnr;
+ stmt->fields[i].max_length= stmt->mysql->fields[i].max_length;
+ }
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ memset(stmt->bind, 0, stmt->field_count * sizeof(MYSQL_BIND));
+ stmt->bind_result_done= 0;
+ }
+ return(0);
+}
+
+int stmt_read_execute_response(MYSQL_STMT *stmt)
+{
+ MYSQL *mysql= stmt->mysql;
+ int ret;
+
+ if (!mysql)
+ return(1);
+
+ ret= test((mysql->methods->db_read_stmt_result &&
+ mysql->methods->db_read_stmt_result(mysql)));
+ /* if a reconnect occured, our connection handle is invalid */
+ if (!stmt->mysql)
+ return(1);
+
+ /* update affected rows, also if an error occured */
+ stmt->upsert_status.affected_rows= stmt->mysql->affected_rows;
+
+ if (ret)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
+ mysql->net.last_error);
+ stmt->state= MYSQL_STMT_PREPARED;
+ return(1);
+ }
+ stmt->upsert_status.last_insert_id= mysql->insert_id;
+ stmt->upsert_status.server_status= mysql->server_status;
+ stmt->upsert_status.warning_count= mysql->warning_count;
+
+ CLEAR_CLIENT_ERROR(mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ stmt->execute_count++;
+ stmt->send_types_to_server= 0;
+
+ stmt->state= MYSQL_STMT_EXECUTED;
+
+ if (mysql->field_count)
+ {
+ if (!stmt->field_count ||
+ mysql->server_status & SERVER_MORE_RESULTS_EXIST) /* fix for ps_bug: test_misc */
+ {
+ MA_MEM_ROOT *fields_ma_alloc_root=
+ &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ uint i;
+
+ ma_free_root(fields_ma_alloc_root, MYF(0));
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root,
+ sizeof(MYSQL_BIND) * mysql->field_count)) ||
+ !(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root,
+ sizeof(MYSQL_FIELD) * mysql->field_count)))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ memset(stmt->bind, 0, sizeof(MYSQL_BIND) * mysql->field_count);
+ stmt->field_count= mysql->field_count;
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ if (mysql->fields[i].db)
+ stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].db);
+ if (mysql->fields[i].table)
+ stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].table);
+ if (mysql->fields[i].org_table)
+ stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_table);
+ if (mysql->fields[i].name)
+ stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].name);
+ if (mysql->fields[i].org_name)
+ stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_name);
+ if (mysql->fields[i].catalog)
+ stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].catalog);
+ stmt->fields[i].def= mysql->fields[i].def ? ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].def) : NULL;
+ }
+ }
+
+ if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS)
+ {
+ stmt->cursor_exists = TRUE;
+ mysql->status = MYSQL_STATUS_READY;
+
+ /* Only cursor read */
+ stmt->default_rset_handler = _mysql_stmt_use_result;
+
+ } else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
+ {
+ /*
+ We have asked for CURSOR but got no cursor, because the condition
+ above is not fulfilled. Then...
+ This is a single-row result set, a result set with no rows, EXPLAIN,
+ SHOW VARIABLES, or some other command which either a) bypasses the
+ cursors framework in the server and writes rows directly to the
+ network or b) is more efficient if all (few) result set rows are
+ precached on client and server's resources are freed.
+ */
+
+ /* preferred is buffered read */
+ mysql_stmt_store_result(stmt);
+ stmt->mysql->status= MYSQL_STATUS_STMT_RESULT;
+ } else
+ {
+ /* preferred is unbuffered read */
+ stmt->default_rset_handler = _mysql_stmt_use_result;
+ stmt->mysql->status= MYSQL_STATUS_STMT_RESULT;
+ }
+ stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE;
+ /* in certain cases parameter types can change: For example see bug
+ 4026 (SELECT ?), so we need to update field information */
+ if (mysql->field_count == stmt->field_count)
+ {
+ uint i;
+ for (i=0; i < stmt->field_count; i++)
+ {
+ stmt->fields[i].type= mysql->fields[i].type;
+ stmt->fields[i].length= mysql->fields[i].length;
+ stmt->fields[i].flags= mysql->fields[i].flags;
+ stmt->fields[i].decimals= mysql->fields[i].decimals;
+ stmt->fields[i].charsetnr= mysql->fields[i].charsetnr;
+ stmt->fields[i].max_length= mysql->fields[i].max_length;
+ }
+ } else
+ {
+ /* table was altered, see test_wl4166_2 */
+ SET_CLIENT_STMT_ERROR(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
+{
+ MYSQL *mysql= stmt->mysql;
+ char *request;
+ int ret;
+ size_t request_len= 0;
+
+ if (!stmt->mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state < MYSQL_STMT_PREPARED)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->param_count && !stmt->bind_param_done)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler = _mysql_stmt_use_result;
+ stmt->default_rset_handler(stmt);
+ }
+ if (stmt->state > MYSQL_STMT_WAITING_USE_OR_STORE && stmt->state < MYSQL_STMT_FETCH_DONE && !stmt->result.data)
+ {
+ if (!stmt->cursor_exists)
+ do {
+ stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
+ } while(mysql_stmt_more_results(stmt));
+ stmt->state= MYSQL_STMT_PREPARED;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ }
+
+ /* clear data, in case mysql_stmt_store_result was called */
+ if (stmt->result.data)
+ {
+ ma_free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC));
+ stmt->result_cursor= stmt->result.data= 0;
+ stmt->result.rows= 0;
+ }
+ if (stmt->array_size > 0)
+ request= (char *)mysql_stmt_execute_generate_bulk_request(stmt, &request_len);
+ else
+ request= (char *)mysql_stmt_execute_generate_simple_request(stmt, &request_len);
+
+ if (!request)
+ return 1;
+
+ ret= stmt->mysql->methods->db_command(mysql,
+ stmt->array_size > 0 ? COM_STMT_BULK_EXECUTE : COM_STMT_EXECUTE,
+ request, request_len, 1, stmt);
+ if (request)
+ free(request);
+
+ if (ret)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
+ mysql->net.last_error);
+ return(1);
+ }
+
+ if (mysql->net.extension->multi_status > COM_MULTI_OFF)
+ return(0);
+
+ return(stmt_read_execute_response(stmt));
+}
+
+static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags)
+{
+ MYSQL *mysql= stmt->mysql;
+ my_bool ret= 0;
+
+ if (!stmt->mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* clear error */
+ if (flags & MADB_RESET_ERROR)
+ {
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ }
+
+ if (stmt->stmt_id)
+ {
+ /* free buffered resultset, previously allocated
+ * by mysql_stmt_store_result
+ */
+ if (flags & MADB_RESET_STORED &&
+ stmt->result_cursor)
+ {
+ ma_free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC));
+ stmt->result.data= NULL;
+ stmt->result.rows= 0;
+ stmt->result_cursor= NULL;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ }
+
+ /* if there is a pending result set, we will flush it */
+ if (flags & MADB_RESET_BUFFER)
+ {
+ if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler(stmt);
+ stmt->state = MYSQL_STMT_USER_FETCHING;
+ }
+
+ if (stmt->mysql->status!= MYSQL_STATUS_READY && stmt->field_count)
+ {
+ mysql->methods->db_stmt_flush_unbuffered(stmt);
+ mysql->status= MYSQL_STATUS_READY;
+ }
+ }
+
+ if (flags & MADB_RESET_SERVER)
+ {
+ /* reset statement on server side */
+ if (stmt->mysql && stmt->mysql->status == MYSQL_STATUS_READY &&
+ stmt->mysql->net.pvio)
+ {
+ unsigned char cmd_buf[STMT_ID_LENGTH];
+ int4store(cmd_buf, stmt->stmt_id);
+ if ((ret= stmt->mysql->methods->db_command(mysql,COM_STMT_RESET, (char *)cmd_buf,
+ sizeof(cmd_buf), 0, stmt)))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
+ mysql->net.last_error);
+ return(ret);
+ }
+ }
+ }
+
+ if (flags & MADB_RESET_LONGDATA)
+ {
+ if (stmt->params)
+ {
+ ulonglong i;
+ for (i=0; i < stmt->param_count; i++)
+ if (stmt->params[i].long_data_used)
+ stmt->params[i].long_data_used= 0;
+ }
+ }
+
+ }
+ return(ret);
+}
+
+static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close)
+{
+ MYSQL *mysql= stmt->mysql;
+ my_bool ret= 1;
+ unsigned int flags= MADB_RESET_LONGDATA | MADB_RESET_BUFFER | MADB_RESET_ERROR;
+
+ if (!mysql)
+ {
+ /* connection could be invalid, e.g. after mysql_stmt_close or failed reconnect
+ attempt (see bug CONC-97) */
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state >= MYSQL_STMT_USER_FETCHING &&
+ stmt->fetch_row_func == stmt_unbuffered_fetch)
+ flags|= MADB_RESET_BUFFER;
+
+ ret= madb_reset_stmt(stmt, flags);
+
+ if (stmt->stmt_id)
+ {
+ if ((stmt->state > MYSQL_STMT_EXECUTED &&
+ stmt->mysql->status != MYSQL_STATUS_READY) ||
+ stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST)
+ {
+ /* flush any pending (multiple) result sets */
+ if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler(stmt);
+ stmt->state = MYSQL_STMT_USER_FETCHING;
+ }
+
+ if (stmt->field_count)
+ {
+ while (mysql_stmt_next_result(stmt) == 0);
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ }
+ }
+ if (!is_close)
+ ret= madb_reset_stmt(stmt, MADB_RESET_SERVER);
+ }
+ stmt->state= MYSQL_STMT_PREPARED;
+ stmt->upsert_status.affected_rows= mysql->affected_rows;
+ stmt->upsert_status.last_insert_id= mysql->insert_id;
+ stmt->upsert_status.server_status= mysql->server_status;
+ stmt->upsert_status.warning_count= mysql->warning_count;
+ mysql->status= MYSQL_STATUS_READY;
+
+ return(ret);
+}
+
+MYSQL_RES * STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt)
+{
+ MYSQL_RES *res;
+
+ if (!stmt->field_count)
+ return(NULL);
+
+ /* aloocate result set structutr and copy stmt information */
+ if (!(res= (MYSQL_RES *)calloc(1, sizeof(MYSQL_RES))))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+ res->eof= 1;
+ res->fields= stmt->fields;
+ res->field_count= stmt->field_count;
+ return(res);
+}
+
+my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
+{
+ return mysql_stmt_internal_reset(stmt, 0);
+}
+
+const char * STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt)
+{
+ return stmt->sqlstate;
+}
+
+MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt)
+{
+ return(stmt->result_cursor);
+}
+
+unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT *stmt)
+{
+ return stmt->param_count;
+}
+
+MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt, MYSQL_ROW_OFFSET new_row)
+{
+ MYSQL_ROW_OFFSET old_row; /* for returning old position */
+
+ old_row= stmt->result_cursor;
+ stmt->result_cursor= new_row;
+
+ return(old_row);
+}
+
+my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
+ const char *data, unsigned long length)
+{
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ if (stmt->state < MYSQL_STMT_PREPARED || !stmt->params)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (param_number >= stmt->param_count)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (length || !stmt->params[param_number].long_data_used)
+ {
+ int ret;
+ size_t packet_len= STMT_ID_LENGTH + 2 + length;
+ uchar *cmd_buff= (uchar *)calloc(1, packet_len);
+ int4store(cmd_buff, stmt->stmt_id);
+ int2store(cmd_buff + STMT_ID_LENGTH, param_number);
+ memcpy(cmd_buff + STMT_ID_LENGTH + 2, data, length);
+ stmt->params[param_number].long_data_used= 1;
+ ret= stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_SEND_LONG_DATA,
+ (char *)cmd_buff, packet_len, 1, stmt);
+ free(cmd_buff);
+ return(ret);
+ }
+ return(0);
+}
+
+unsigned long long STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt)
+{
+ return stmt->upsert_status.last_insert_id;
+}
+
+unsigned long long STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
+{
+ return stmt->result.rows;
+}
+
+MYSQL_RES* STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt __attribute__((unused)))
+{
+ /* server doesn't deliver any information yet,
+ so we just return NULL
+ */
+ return(NULL);
+}
+
+my_bool STDCALL mysql_stmt_more_results(MYSQL_STMT *stmt)
+{
+ /* MDEV 4604: Server doesn't set MORE_RESULT flag for
+ OutParam result set, so we need to check
+ for SERVER_MORE_RESULTS_EXIST and for
+ SERVER_PS_OUT_PARAMS)
+ */
+ return (stmt &&
+ stmt->mysql &&
+ ((stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST) ||
+ (stmt->mysql->server_status & SERVER_PS_OUT_PARAMS)));
+}
+
+int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt)
+{
+ int rc= 0;
+
+ if (!stmt->mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state < MYSQL_STMT_EXECUTED)
+ {
+ SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!mysql_stmt_more_results(stmt))
+ return(-1);
+
+ if (stmt->state > MYSQL_STMT_EXECUTED &&
+ stmt->state < MYSQL_STMT_FETCH_DONE)
+ madb_reset_stmt(stmt, MADB_RESET_ERROR | MADB_RESET_BUFFER | MADB_RESET_LONGDATA);
+ stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE;
+
+ if (mysql_next_result(stmt->mysql))
+ {
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate,
+ stmt->mysql->net.last_error);
+ return(1);
+ }
+
+ if (stmt->mysql->status == MYSQL_STATUS_GET_RESULT)
+ stmt->mysql->status= MYSQL_STATUS_STMT_RESULT;
+
+ if (stmt->mysql->field_count)
+ rc= madb_alloc_stmt_fields(stmt);
+ else
+ {
+ stmt->upsert_status.affected_rows= stmt->mysql->affected_rows;
+ stmt->upsert_status.last_insert_id= stmt->mysql->insert_id;
+ stmt->upsert_status.server_status= stmt->mysql->server_status;
+ stmt->upsert_status.warning_count= stmt->mysql->warning_count;
+ }
+
+ stmt->field_count= stmt->mysql->field_count;
+
+ return(rc);
+}
+
+int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt,
+ const char *stmt_str,
+ size_t length)
+{
+ MYSQL *mysql= stmt->mysql;
+ my_bool emulate_cmd= !(!(stmt->mysql->server_capabilities & CLIENT_MYSQL) &&
+ (stmt->mysql->extension->mariadb_server_capabilities &
+ (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32)));
+
+ if (!mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ goto fail;
+ }
+
+ /* Server versions < 10.2 don't support execute_direct, so we need to
+ emulate it */
+ if (emulate_cmd)
+ {
+ int rc;
+
+ /* avoid sending close + prepare in 2 packets */
+
+ if ((rc= mysql_stmt_prepare(stmt, stmt_str, length)))
+ return rc;
+ return mysql_stmt_execute(stmt);
+ }
+
+ if (ma_multi_command(mysql, COM_MULTI_ENABLED))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ goto fail;
+ }
+
+ if (!stmt->mysql)
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (length == (size_t) -1)
+ length= strlen(stmt_str);
+
+ /* clear flags */
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~0;
+
+ /* check if we have to clear results */
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ /* We need to semi-close the prepared statement:
+ reset stmt and free all buffers and close the statement
+ on server side. Statment handle will get a new stmt_id */
+ char stmt_id[STMT_ID_LENGTH];
+
+ if (mysql_stmt_internal_reset(stmt, 1))
+ goto fail;
+
+ ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
+ ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0));
+ stmt->field_count= 0;
+ stmt->param_count= 0;
+ stmt->params= 0;
+
+ int4store(stmt_id, stmt->stmt_id);
+ if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id,
+ sizeof(stmt_id), 1, stmt))
+ goto fail;
+ }
+ stmt->stmt_id= -1;
+ if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, stmt_str, length, 1, stmt))
+ goto fail;
+
+ stmt->state= MYSQL_STMT_PREPARED;
+ /* Since we can't determine stmt_id here, we need to set it to -1, so server will know that the
+ * execute command belongs to previous prepare */
+ stmt->stmt_id= -1;
+ if (mysql_stmt_execute(stmt))
+ goto fail;
+
+ /* flush multi buffer */
+ if (ma_multi_command(mysql, COM_MULTI_END))
+ goto fail;
+
+ /* read prepare response */
+ if (mysql->methods->db_read_prepare_response &&
+ mysql->methods->db_read_prepare_response(stmt))
+ goto fail;
+
+ /* metadata not supported yet */
+
+ if (stmt->param_count &&
+ stmt->mysql->methods->db_stmt_get_param_metadata(stmt))
+ {
+ goto fail;
+ }
+
+ /* allocated bind buffer for parameters */
+ if (stmt->field_count &&
+ stmt->mysql->methods->db_stmt_get_result_metadata(stmt))
+ {
+ goto fail;
+ }
+
+ /* allocated bind buffer for result */
+ if (stmt->field_count)
+ {
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
+ {
+ SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto fail;
+ }
+ memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count);
+ }
+ stmt->state = MYSQL_STMT_PREPARED;
+
+ /* read execute response packet */
+ return stmt_read_execute_response(stmt);
+fail:
+ SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate,
+ mysql->net.last_error);
+ do {
+ stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
+ } while(mysql_stmt_more_results(stmt));
+ stmt->state= MYSQL_STMT_INITTED;
+ return 1;
+}
diff --git a/mysql/libmariadb/mariadbclient_win32.def.in b/mysql/libmariadb/mariadbclient_win32.def.in
new file mode 100644
index 0000000..5448d8a
--- /dev/null
+++ b/mysql/libmariadb/mariadbclient_win32.def.in
@@ -0,0 +1,229 @@
+EXPORTS
+mariadb_cancel
+mariadb_connection
+mariadb_convert_string
+ma_pvio_register_callback
+mariadb_get_charset_by_name
+mariadb_stmt_execute_direct
+mariadb_get_charset_by_nr
+mariadb_get_info
+mariadb_get_infov
+mysql_get_timeout_value
+mysql_get_timeout_value_ms
+mysql_optionsv
+mysql_ps_fetch_functions
+mariadb_reconnect
+mysql_stmt_warning_count
+$mariadb_deinitialize_ssl$
+mariadb_dyncol_check
+mariadb_dyncol_column_cmp_named
+mariadb_dyncol_column_count
+mariadb_dyncol_create_many_named
+mariadb_dyncol_create_many_num
+mariadb_dyncol_exists_named
+mariadb_dyncol_exists_num
+mariadb_dyncol_free
+mariadb_dyncol_get_named
+mariadb_dyncol_get_num
+mariadb_dyncol_has_names
+mariadb_dyncol_json
+mariadb_dyncol_list_named
+mariadb_dyncol_list_num
+mariadb_dyncol_unpack
+mariadb_dyncol_update_many_named
+mariadb_dyncol_update_many_num
+mariadb_dyncol_val_double
+mariadb_dyncol_val_long
+mariadb_dyncol_val_str
+mysql_autocommit_cont
+mysql_autocommit_start
+mysql_change_user_cont
+mysql_change_user_start
+mysql_close_cont
+mysql_close_start
+mysql_commit_cont
+mysql_commit_start
+mysql_dump_debug_info_cont
+mysql_dump_debug_info_start
+mysql_fetch_row_cont
+mysql_fetch_row_start
+mysql_free_result_cont
+mysql_free_result_start
+mysql_get_timeout_value
+mysql_get_timeout_value_ms
+mysql_kill_cont
+mysql_kill_start
+mysql_list_fields_cont
+mysql_list_fields_start
+mysql_next_result_cont
+mysql_next_result_start
+mysql_ping_cont
+mysql_ping_start
+mysql_reset_connection_start
+mysql_reset_connection_cont
+mysql_query_cont
+mysql_query_start
+mysql_read_query_result_cont
+mysql_read_query_result_start
+mysql_real_connect_cont
+mysql_real_connect_start
+mysql_real_query_cont
+mysql_real_query_start
+mysql_refresh_cont
+mysql_refresh_start
+mysql_rollback_cont
+mysql_rollback_start
+mysql_select_db_cont
+mysql_select_db_start
+mysql_send_query_cont
+mysql_send_query_start
+mysql_set_character_set_cont
+mysql_set_character_set_start
+mysql_set_server_option_cont
+mysql_set_server_option_start
+mysql_shutdown_cont
+mysql_shutdown_start
+mysql_stat_cont
+mysql_stat_start
+mysql_stmt_close_cont
+mysql_stmt_close_start
+mysql_stmt_execute_cont
+mysql_stmt_execute_start
+mysql_stmt_fetch_cont
+mysql_stmt_fetch_start
+mysql_stmt_free_result_cont
+mysql_stmt_free_result_start
+mysql_stmt_next_result_cont
+mysql_stmt_next_result_start
+mysql_stmt_prepare_cont
+mysql_stmt_prepare_start
+mysql_stmt_reset_cont
+mysql_stmt_reset_start
+mysql_stmt_send_long_data_cont
+mysql_stmt_send_long_data_start
+mysql_stmt_store_result_cont
+mysql_stmt_store_result_start
+mysql_store_result_cont
+mysql_store_result_start
+mysql_affected_rows
+mysql_autocommit
+mysql_change_user
+mysql_character_set_name
+mysql_client_find_plugin
+mysql_client_register_plugin
+mysql_close
+mysql_commit
+mysql_data_seek
+mysql_debug
+mysql_dump_debug_info
+mysql_embedded
+mysql_eof
+mysql_errno
+mysql_error
+mysql_escape_string
+mysql_fetch_field
+mysql_fetch_field_direct
+mysql_fetch_fields
+mysql_fetch_lengths
+mysql_fetch_row
+mysql_field_count
+mysql_field_seek
+mysql_field_tell
+mysql_free_result
+mysql_get_character_set_info
+mysql_get_charset_by_name
+mysql_get_charset_by_nr
+mysql_get_client_info
+mysql_get_client_version
+mysql_get_host_info
+mysql_get_option
+mysql_get_optionv
+mysql_get_parameters
+mysql_get_proto_info
+mysql_get_server_info
+mysql_get_server_name
+mysql_get_server_version
+mysql_get_socket
+mysql_get_ssl_cipher
+mysql_get_timeout_value
+mysql_get_timeout_value_ms
+mysql_hex_string
+mysql_info
+mysql_init
+mysql_insert_id
+mysql_kill
+mysql_list_dbs
+mysql_list_fields
+mysql_list_processes
+mysql_list_tables
+mysql_load_plugin
+mysql_load_plugin_v
+mysql_more_results
+mysql_net_field_length
+mysql_net_read_packet
+mysql_next_result
+mysql_num_fields
+mysql_num_rows
+mysql_options
+mysql_options4
+mysql_ping
+mysql_query
+mysql_read_query_result
+mysql_real_connect
+mysql_real_escape_string
+mysql_real_query
+mysql_refresh
+mysql_reset_connection
+mysql_rollback
+mysql_row_seek
+mysql_row_tell
+mysql_select_db
+mysql_send_query
+mysql_server_end
+mysql_server_init
+mysql_session_track_get_next
+mysql_session_track_get_first
+mysql_set_character_set
+mysql_set_local_infile_default
+mysql_set_local_infile_handler
+mysql_set_server_option
+mysql_shutdown
+mysql_sqlstate
+mysql_ssl_set
+mysql_stat
+mysql_stmt_affected_rows
+mysql_stmt_attr_get
+mysql_stmt_attr_set
+mysql_stmt_bind_param
+mysql_stmt_bind_result
+mysql_stmt_close
+mysql_stmt_data_seek
+mysql_stmt_errno
+mysql_stmt_error
+mysql_stmt_execute
+mysql_stmt_fetch
+mysql_stmt_fetch_column
+mysql_stmt_field_count
+mysql_stmt_free_result
+mysql_stmt_init
+mysql_stmt_insert_id
+mysql_stmt_more_results
+mysql_stmt_next_result
+mysql_stmt_num_rows
+mysql_stmt_param_count
+mysql_stmt_param_metadata
+mysql_stmt_prepare
+mysql_stmt_reset
+mysql_stmt_result_metadata
+mysql_stmt_row_seek
+mysql_stmt_row_tell
+mysql_stmt_send_long_data
+mysql_stmt_sqlstate
+mysql_stmt_store_result
+mysql_store_result
+mysql_thread_end
+mysql_thread_id
+mysql_thread_init
+mysql_thread_safe
+mysql_use_result
+mysql_warning_count
diff --git a/mysql/libmariadb/secure/ma_schannel.c b/mysql/libmariadb/secure/ma_schannel.c
new file mode 100644
index 0000000..4d8af89
--- /dev/null
+++ b/mysql/libmariadb/secure/ma_schannel.c
@@ -0,0 +1,1008 @@
+/************************************************************************************
+ Copyright (C) 2014 MariaDB Corporation 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
+
+ Author: Georg Richter
+
+ *************************************************************************************/
+#include "ma_schannel.h"
+#include <assert.h>
+
+#define SC_IO_BUFFER_SIZE 0x4000
+#define MAX_SSL_ERR_LEN 100
+
+#define SCHANNEL_PAYLOAD(A) (A).cbMaximumMessage + (A).cbHeader + (A).cbTrailer
+void ma_schannel_set_win_error(MARIADB_PVIO *pvio);
+
+/* {{{ void ma_schannel_set_sec_error */
+void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo)
+{
+ MYSQL *mysql= pvio->mysql;
+ switch(ErrorNo) {
+ case SEC_E_ILLEGAL_MESSAGE:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: The message received was unexpected or badly formatted");
+ break;
+ case SEC_E_UNTRUSTED_ROOT:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Untrusted root certificate");
+ break;
+ case SEC_E_BUFFER_TOO_SMALL:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Buffer too small");
+ break;
+ case SEC_E_CRYPTO_SYSTEM_INVALID:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Cipher is not supported");
+ break;
+ case SEC_E_INSUFFICIENT_MEMORY:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Out of memory");
+ break;
+ case SEC_E_OUT_OF_SEQUENCE:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Invalid message sequence");
+ break;
+ case SEC_E_DECRYPT_FAILURE:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: An error occured during decrypting data");
+ break;
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Incomplete credentials");
+ break;
+ case SEC_E_ENCRYPT_FAILURE:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: An error occured during encrypting data");
+ break;
+ case SEC_I_CONTEXT_EXPIRED:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Context expired ");
+ break;
+ case SEC_E_ALGORITHM_MISMATCH:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: no cipher match");
+ break;
+ case SEC_E_NO_CREDENTIALS:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: no credentials");
+ break;
+ case SEC_E_OK:
+ break;
+ case SEC_E_INTERNAL_ERROR:
+ if (GetLastError())
+ ma_schannel_set_win_error(pvio);
+ else
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "The Local Security Authority cannot be contacted");
+ break;
+ default:
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error (0x%x)", ErrorNo);
+ }
+}
+/* }}} */
+
+/* {{{ void ma_schnnel_set_win_error */
+void ma_schannel_set_win_error(MARIADB_PVIO *pvio)
+{
+ ulong ssl_errno= GetLastError();
+ char *ssl_error_reason= NULL;
+ char *p;
+ char buffer[256];
+ if (!ssl_errno)
+ {
+ pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
+ return;
+ }
+ /* todo: obtain error messge */
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &ssl_error_reason, 0, NULL );
+ for (p = ssl_error_reason; *p; p++)
+ if (*p == '\n' || *p == '\r')
+ *p = 0;
+ snprintf(buffer, sizeof(buffer), "SSL connection error: %s",ssl_error_reason);
+ pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, buffer);
+ if (ssl_error_reason)
+ LocalFree(ssl_error_reason);
+ return;
+}
+/* }}} */
+
+/* {{{ LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) */
+/*
+ Load a pem or clr file and convert it to a binary DER object
+
+ SYNOPSIS
+ ma_schannel_load_pem()
+ PemFileName name of the pem file (in)
+ buffer_len length of the converted DER binary
+
+ DESCRIPTION
+ Loads a X509 file (ca, certification, key or clr) into memory and converts
+ it to a DER binary object. This object can be decoded and loaded into
+ a schannel crypto context.
+ If the function failed, error can be retrieved by GetLastError()
+ The returned binary object must be freed by caller.
+
+ RETURN VALUE
+ NULL if the conversion failed or file was not found
+ LPBYTE * a pointer to a binary der object
+ buffer_len will contain the length of binary der object
+*/
+static LPBYTE ma_schannel_load_pem(MARIADB_PVIO *pvio, const char *PemFileName, DWORD *buffer_len)
+{
+ HANDLE hfile;
+ char *buffer= NULL;
+ DWORD dwBytesRead= 0;
+ LPBYTE der_buffer= NULL;
+ DWORD der_buffer_length;
+
+ if (buffer_len == NULL)
+ return NULL;
+
+
+ if ((hfile= CreateFile(PemFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE)
+ {
+ ma_schannel_set_win_error(pvio);
+ return NULL;
+ }
+
+ if (!(*buffer_len = GetFileSize(hfile, NULL)))
+ {
+ pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Invalid pem format");
+ goto end;
+ }
+
+ if (!(buffer= LocalAlloc(0, *buffer_len + 1)))
+ {
+ pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
+ goto end;
+ }
+
+ if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+
+ CloseHandle(hfile);
+
+ /* calculate the length of DER binary */
+ if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
+ NULL, &der_buffer_length, NULL, NULL))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+ /* allocate DER binary buffer */
+ if (!(der_buffer= (LPBYTE)LocalAlloc(0, der_buffer_length)))
+ {
+ pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
+ goto end;
+ }
+ /* convert to DER binary */
+ if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
+ der_buffer, &der_buffer_length, NULL, NULL))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+
+ *buffer_len= der_buffer_length;
+ LocalFree(buffer);
+
+ return der_buffer;
+
+end:
+ if (hfile != INVALID_HANDLE_VALUE)
+ CloseHandle(hfile);
+ if (buffer)
+ LocalFree(buffer);
+ if (der_buffer)
+ LocalFree(der_buffer);
+ *buffer_len= 0;
+ return NULL;
+}
+/* }}} */
+
+/* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_PVIO *pvio, const char *pem_file) */
+/*
+ Create a certification context from ca or cert file
+
+ SYNOPSIS
+ ma_schannel_create_cert_context()
+ pvio pvio object
+ pem_file name of certificate or ca file
+
+ DESCRIPTION
+ Loads a PEM file (certificate authority or certificate) creates a certification
+ context and loads the binary representation into context.
+ The returned context must be freed by caller.
+ If the function failed, error can be retrieved by GetLastError().
+
+ RETURNS
+ NULL If loading of the file or creating context failed
+ CERT_CONTEXT * A pointer to a certification context structure
+*/
+CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_PVIO *pvio, const char *pem_file)
+{
+ DWORD der_buffer_length;
+ LPBYTE der_buffer= NULL;
+
+ CERT_CONTEXT *ctx= NULL;
+
+ /* create DER binary object from ca/certification file */
+ if (!(der_buffer= ma_schannel_load_pem(pvio, pem_file, (DWORD *)&der_buffer_length)))
+ goto end;
+ if (!(ctx= (CERT_CONTEXT *)CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ der_buffer, der_buffer_length)))
+ ma_schannel_set_win_error(pvio);
+
+end:
+ if (der_buffer)
+ LocalFree(der_buffer);
+ return ctx;
+}
+/* }}} */
+
+/* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_PVIO *pvio, const char *pem_file) */
+/*
+ Create a crl context from crlfile
+
+ SYNOPSIS
+ ma_schannel_create_crl_context()
+ pem_file name of certificate or ca file
+
+ DESCRIPTION
+ Loads a certification revocation list file, creates a certification
+ context and loads the binary representation into crl context.
+ The returned context must be freed by caller.
+ If the function failed, error can be retrieved by GetLastError().
+
+ RETURNS
+ NULL If loading of the file or creating context failed
+ PCCRL_CONTEXT A pointer to a certification context structure
+*/
+PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_PVIO *pvio, const char *pem_file)
+{
+ DWORD der_buffer_length;
+ LPBYTE der_buffer= NULL;
+
+ PCCRL_CONTEXT ctx= NULL;
+
+ /* load ca pem file into memory */
+ if (!(der_buffer= ma_schannel_load_pem(pvio, pem_file, (DWORD *)&der_buffer_length)))
+ goto end;
+ if (!(ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ der_buffer, der_buffer_length)))
+ ma_schannel_set_win_error(pvio);
+end:
+ if (der_buffer)
+ LocalFree(der_buffer);
+ return ctx;
+}
+/* }}} */
+
+/* {{{ my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, CERT_CONTEXT *ctx, char *key_file) */
+/*
+ Load privte key into context
+
+ SYNOPSIS
+ ma_schannel_load_private_key()
+ ctx pointer to a certification context
+ pem_file name of certificate or ca file
+
+ DESCRIPTION
+ Loads a certification revocation list file, creates a certification
+ context and loads the binary representation into crl context.
+ The returned context must be freed by caller.
+ If the function failed, error can be retrieved by GetLastError().
+
+ RETURNS
+ NULL If loading of the file or creating context failed
+ PCCRL_CONTEXT A pointer to a certification context structure
+*/
+
+my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, CERT_CONTEXT *ctx, char *key_file)
+{
+ DWORD der_buffer_len= 0;
+ LPBYTE der_buffer= NULL;
+ DWORD priv_key_len= 0;
+ LPBYTE priv_key= NULL;
+ HCRYPTPROV crypt_prov= 0;
+ HCRYPTKEY crypt_key= 0;
+ CERT_KEY_CONTEXT kpi={ 0 };
+ my_bool rc= 0;
+
+ /* load private key into der binary object */
+ if (!(der_buffer= ma_schannel_load_pem(pvio, key_file, &der_buffer_len)))
+ return 0;
+
+ /* determine required buffer size for decoded private key */
+ if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ PKCS_RSA_PRIVATE_KEY,
+ der_buffer, der_buffer_len,
+ 0, NULL,
+ NULL, &priv_key_len))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+
+ /* allocate buffer for decoded private key */
+ if (!(priv_key= LocalAlloc(0, priv_key_len)))
+ {
+ pvio->set_error(pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
+ goto end;
+ }
+
+ /* decode */
+ if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ PKCS_RSA_PRIVATE_KEY,
+ der_buffer, der_buffer_len,
+ 0, NULL,
+ priv_key, &priv_key_len))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+
+ /* Acquire context */
+ if (!CryptAcquireContext(&crypt_prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+ /* ... and import the private key */
+ if (!CryptImportKey(crypt_prov, priv_key, priv_key_len, 0, 0, (HCRYPTKEY *)&crypt_key))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+
+ kpi.hCryptProv= crypt_prov;
+ kpi.dwKeySpec = AT_KEYEXCHANGE;
+ kpi.cbSize= sizeof(kpi);
+
+ /* assign private key to certificate context */
+ if (CertSetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, 0, &kpi))
+ rc= 1;
+ else
+ ma_schannel_set_win_error(pvio);
+
+end:
+ if (der_buffer)
+ LocalFree(der_buffer);
+ if (priv_key)
+ {
+ if (crypt_key)
+ CryptDestroyKey(crypt_key);
+ LocalFree(priv_key);
+ if (!rc)
+ if (crypt_prov)
+ CryptReleaseContext(crypt_prov, 0);
+ }
+ return rc;
+}
+/* }}} */
+
+/* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData) */
+/*
+ perform handshake loop
+
+ SYNOPSIS
+ ma_schannel_handshake_loop()
+ pvio Pointer to an Communication/IO structure
+ InitialRead TRUE if it's the very first read
+ ExtraData Pointer to an SecBuffer which contains extra data (sent by application)
+
+
+*/
+
+SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData)
+{
+ SecBufferDesc OutBuffer, InBuffer;
+ SecBuffer InBuffers[2], OutBuffers;
+ DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS rc;
+ PUCHAR IoBuffer;
+ BOOL fDoRead;
+ MARIADB_TLS *ctls= pvio->ctls;
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+
+ /* Allocate data buffer */
+ if (!(IoBuffer = LocalAlloc(LMEM_FIXED, SC_IO_BUFFER_SIZE)))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ cbIoBuffer = 0;
+ fDoRead = InitialRead;
+
+ /* handshake loop: We will leave if handshake is finished
+ or an error occurs */
+
+ rc = SEC_I_CONTINUE_NEEDED;
+
+ while (rc == SEC_I_CONTINUE_NEEDED ||
+ rc == SEC_E_INCOMPLETE_MESSAGE ||
+ rc == SEC_I_INCOMPLETE_CREDENTIALS )
+ {
+ /* Read data */
+ if (rc == SEC_E_INCOMPLETE_MESSAGE ||
+ !cbIoBuffer)
+ {
+ if(fDoRead)
+ {
+ ssize_t nbytes = pvio->methods->read(pvio, IoBuffer + cbIoBuffer, (size_t)(SC_IO_BUFFER_SIZE - cbIoBuffer));
+ if (nbytes <= 0)
+ {
+ rc = SEC_E_INTERNAL_ERROR;
+ break;
+ }
+ cbData = (DWORD)nbytes;
+ cbIoBuffer += cbData;
+ }
+ else
+ fDoRead = TRUE;
+ }
+
+ /* input buffers
+ First buffer stores data received from server. leftover data
+ will be stored in second buffer with BufferType SECBUFFER_EXTRA */
+
+ InBuffers[0].pvBuffer = IoBuffer;
+ InBuffers[0].cbBuffer = cbIoBuffer;
+ InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ InBuffers[1].pvBuffer = NULL;
+ InBuffers[1].cbBuffer = 0;
+ InBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ InBuffer.cBuffers = 2;
+ InBuffer.pBuffers = InBuffers;
+ InBuffer.ulVersion = SECBUFFER_VERSION;
+
+
+ /* output buffer */
+ OutBuffers.pvBuffer = NULL;
+ OutBuffers.BufferType= SECBUFFER_TOKEN;
+ OutBuffers.cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = &OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+
+ rc = InitializeSecurityContextA(&sctx->CredHdl,
+ &sctx->ctxt,
+ NULL,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &InBuffer,
+ 0,
+ NULL,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry );
+
+
+ if (rc == SEC_E_OK ||
+ rc == SEC_I_CONTINUE_NEEDED ||
+ FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
+ {
+ if(OutBuffers.cbBuffer && OutBuffers.pvBuffer)
+ {
+ ssize_t nbytes = pvio->methods->write(pvio, (uchar *)OutBuffers.pvBuffer, (size_t)OutBuffers.cbBuffer);
+ if(nbytes <= 0)
+ {
+ FreeContextBuffer(OutBuffers.pvBuffer);
+ DeleteSecurityContext(&sctx->ctxt);
+ return SEC_E_INTERNAL_ERROR;
+ }
+ cbData= (DWORD)nbytes;
+ /* Free output context buffer */
+ FreeContextBuffer(OutBuffers.pvBuffer);
+ OutBuffers.pvBuffer = NULL;
+ }
+ }
+ /* check if we need to read more data */
+ switch (rc) {
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* we didn't receive all data, so just continue loop */
+ continue;
+ break;
+ case SEC_E_OK:
+ /* handshake completed, but we need to check if extra
+ data was sent (which contains encrypted application data) */
+ if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ if (!(pExtraData->pvBuffer= LocalAlloc(0, InBuffers[1].cbBuffer)))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
+ pExtraData->BufferType = SECBUFFER_TOKEN;
+ pExtraData->cbBuffer = InBuffers[1].cbBuffer;
+ }
+ else
+ {
+ pExtraData->BufferType= SECBUFFER_EMPTY;
+ pExtraData->pvBuffer= NULL;
+ pExtraData->cbBuffer= 0;
+ }
+ break;
+
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ /* Provided credentials didn't contain a valid client certificate.
+ We will try to connect anonymously, using current credentials */
+ fDoRead= FALSE;
+ rc= SEC_I_CONTINUE_NEEDED;
+ continue;
+ break;
+ default:
+ if (FAILED(rc))
+ {
+ goto loopend;
+ }
+ break;
+ }
+
+ if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
+ {
+ MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
+ cbIoBuffer = InBuffers[1].cbBuffer;
+ }
+
+ cbIoBuffer = 0;
+ }
+loopend:
+ if (FAILED(rc))
+ {
+ ma_schannel_set_sec_error(pvio, rc);
+ DeleteSecurityContext(&sctx->ctxt);
+ }
+ LocalFree(IoBuffer);
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls) */
+/*
+ performs client side handshake
+
+ SYNOPSIS
+ ma_schannel_client_handshake()
+ ctls Pointer to a MARIADB_TLS structure
+
+ DESCRIPTION
+ initiates a client/server handshake. This function can be used
+ by clients only
+
+ RETURN
+ SEC_E_OK on success
+*/
+
+SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls)
+{
+ MARIADB_PVIO *pvio;
+ SECURITY_STATUS sRet;
+ DWORD OutFlags;
+ DWORD r;
+ SC_CTX *sctx;
+ SecBuffer ExtraData;
+ DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_USE_SUPPLIED_CREDS |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
+
+ SecBufferDesc BufferOut;
+ SecBuffer BuffersOut;
+
+ if (!ctls || !ctls->pvio)
+ return 1;
+
+ pvio= ctls->pvio;
+ sctx= (SC_CTX *)ctls->ssl;
+
+ /* Initialie securifty context */
+ BuffersOut.BufferType= SECBUFFER_TOKEN;
+ BuffersOut.cbBuffer= 0;
+ BuffersOut.pvBuffer= NULL;
+
+
+ BufferOut.cBuffers= 1;
+ BufferOut.pBuffers= &BuffersOut;
+ BufferOut.ulVersion= SECBUFFER_VERSION;
+
+ sRet = InitializeSecurityContext(&sctx->CredHdl,
+ NULL,
+ pvio->mysql->host,
+ SFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ &sctx->ctxt,
+ &BufferOut,
+ &OutFlags,
+ NULL);
+
+ if(sRet != SEC_I_CONTINUE_NEEDED)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ return sRet;
+ }
+
+ /* send client hello packaet */
+ if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL)
+ {
+ ssize_t nbytes = (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer);
+
+ if (nbytes <= 0)
+ {
+ sRet= SEC_E_INTERNAL_ERROR;
+ goto end;
+ }
+ r = (DWORD)nbytes;
+ }
+ sRet= ma_schannel_handshake_loop(pvio, TRUE, &ExtraData);
+
+ /* allocate IO-Buffer for write operations: After handshake
+ was successfull, we are able now to calculate payload */
+ if ((sRet = QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes )))
+ goto end;
+
+ sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
+ if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, sctx->IoBufferSize)))
+ {
+ sRet= SEC_E_INSUFFICIENT_MEMORY;
+ goto end;
+ }
+
+ return sRet;
+end:
+ LocalFree(sctx->IoBuffer);
+ sctx->IoBufferSize= 0;
+ FreeContextBuffer(BuffersOut.pvBuffer);
+ DeleteSecurityContext(&sctx->ctxt);
+ return sRet;
+}
+/* }}} */
+
+/* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext,
+ DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
+/*
+ Reads encrypted data from a SSL stream and decrypts it.
+
+ SYNOPSIS
+ ma_schannel_read
+ pvio pointer to Communication IO structure
+ phContext a context handle
+ DecryptLength size of decrypted buffer
+ ReadBuffer Buffer for decrypted data
+ ReadBufferSize size of ReadBuffer
+
+
+ DESCRIPTION
+ Reads decrypted data from a SSL stream and encrypts it.
+
+ RETURN
+ SEC_E_OK on success
+ SEC_E_* if an error occured
+*/
+
+SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
+ PCredHandle phCreds,
+ CtxtHandle * phContext,
+ DWORD *DecryptLength,
+ uchar *ReadBuffer,
+ DWORD ReadBufferSize)
+{
+ ssize_t nbytes= 0;
+ DWORD dwOffset= 0;
+ SC_CTX *sctx;
+ SECURITY_STATUS sRet= SEC_E_INCOMPLETE_MESSAGE;
+ SecBufferDesc Msg;
+ SecBuffer Buffers[4],
+ *pData, *pExtra;
+ int i;
+
+ if (!pvio || !pvio->methods || !pvio->methods->read || !pvio->ctls || !DecryptLength)
+ return SEC_E_INTERNAL_ERROR;
+
+ sctx= (SC_CTX *)pvio->ctls->ssl;
+ *DecryptLength= 0;
+
+ if (sctx->dataBuf.cbBuffer)
+ {
+ /* Have unread decrypted data from the last time, copy. */
+ nbytes = MIN(ReadBufferSize, sctx->dataBuf.cbBuffer);
+ memcpy(ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
+ sctx->dataBuf.pvBuffer = (char *)(sctx->dataBuf.pvBuffer) + nbytes;
+ sctx->dataBuf.cbBuffer -= (DWORD)nbytes;
+ *DecryptLength = (DWORD)nbytes;
+ return SEC_E_OK;
+ }
+
+
+ while (1)
+ {
+ /* Check for any encrypted data returned by last DecryptMessage() in SECBUFFER_EXTRA buffer. */
+ if (sctx->extraBuf.cbBuffer)
+ {
+ memmove(sctx->IoBuffer, sctx->extraBuf.pvBuffer, sctx->extraBuf.cbBuffer);
+ dwOffset = sctx->extraBuf.cbBuffer;
+ sctx->extraBuf.cbBuffer = 0;
+ }
+
+ nbytes= pvio->methods->read(pvio, sctx->IoBuffer + dwOffset, (size_t)(sctx->IoBufferSize - dwOffset));
+ if (nbytes <= 0)
+ {
+ /* server closed connection, or an error */
+ // todo: error
+ return SEC_E_INVALID_HANDLE;
+ }
+ dwOffset+= (DWORD)nbytes;
+
+ ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
+ Buffers[0].pvBuffer= sctx->IoBuffer;
+ Buffers[0].cbBuffer= dwOffset;
+
+ Buffers[0].BufferType= SECBUFFER_DATA;
+ Buffers[1].BufferType=
+ Buffers[2].BufferType=
+ Buffers[3].BufferType= SECBUFFER_EMPTY;
+
+ Msg.ulVersion= SECBUFFER_VERSION; // Version number
+ Msg.cBuffers= 4;
+ Msg.pBuffers= Buffers;
+
+ sRet = DecryptMessage(phContext, &Msg, 0, NULL);
+
+ if (sRet == SEC_E_INCOMPLETE_MESSAGE)
+ continue; /* Continue reading until full message arrives */
+
+ if (sRet != SEC_E_OK)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ return sRet;
+ }
+
+ pData= pExtra= NULL;
+ for (i=0; i < 4; i++)
+ {
+ if (!pData && Buffers[i].BufferType == SECBUFFER_DATA)
+ pData= &Buffers[i];
+ if (!pExtra && Buffers[i].BufferType == SECBUFFER_EXTRA)
+ pExtra= &Buffers[i];
+ if (pData && pExtra)
+ break;
+ }
+
+ if (pExtra)
+ {
+ /* Save preread encrypted data, will be processed next time.*/
+ sctx->extraBuf.cbBuffer = pExtra->cbBuffer;
+ sctx->extraBuf.pvBuffer = pExtra->pvBuffer;
+ }
+
+ if (pData && pData->cbBuffer)
+ {
+ /*
+ Copy at most ReadBufferSize bytes to output.
+ Store the rest (if any) to be processed next time.
+ */
+ nbytes=MIN(pData->cbBuffer, ReadBufferSize);
+ memcpy((char *)ReadBuffer, pData->pvBuffer, nbytes);
+
+
+ sctx->dataBuf.cbBuffer = pData->cbBuffer - (DWORD)nbytes;
+ sctx->dataBuf.pvBuffer = (char *)pData->pvBuffer + nbytes;
+
+ *DecryptLength = (DWORD)nbytes;
+ return SEC_E_OK;
+ }
+ else
+ {
+ /*
+ DecryptMessage() did not return data buffer.
+ According to MSDN, this happens sometimes and is normal.
+ We retry the read/decrypt in this case.
+ */
+ dwOffset = 0;
+ }
+ }
+}
+/* }}} */
+
+my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls)
+{
+ SECURITY_STATUS sRet;
+
+ MARIADB_PVIO *pvio= ctls->pvio;
+ MYSQL *mysql= pvio->mysql;
+ SC_CTX *sctx = (SC_CTX *)ctls->ssl;
+
+ const char *ca_file= mysql->options.ssl_ca;
+ const char *crl_file= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL;
+ PCCERT_CONTEXT pServerCert= NULL;
+ CRL_CONTEXT *crl_ctx= NULL;
+ CERT_CONTEXT *ca_ctx= NULL;
+ int ret= 0;
+
+ if (!ca_file && !crl_file)
+ return 1;
+
+ if (ca_file && !(ca_ctx = ma_schannel_create_cert_context(pvio, ca_file)))
+ goto end;
+
+ if (crl_file && !(crl_ctx= (CRL_CONTEXT *)ma_schannel_create_crl_context(pvio, mysql->options.extension->ssl_crl)))
+ goto end;
+
+ if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ goto end;
+ }
+
+ if (ca_ctx)
+ {
+ DWORD flags = CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG;
+ if (!CertVerifySubjectCertificateContext(pServerCert, ca_ctx, &flags))
+ {
+ ma_schannel_set_win_error(pvio);
+ goto end;
+ }
+
+ if (flags)
+ {
+ if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Certificate signature check failed");
+ else if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: certificate was revoked");
+ else if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: certificate has expired");
+ else
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Unknown error during certificate validation");
+ goto end;
+ }
+ }
+
+
+ /* Check certificates in the certificate chain have been revoked. */
+ if (crl_ctx)
+ {
+ if (!CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pServerCert->pCertInfo, 1, &crl_ctx->pCrlInfo))
+ {
+ pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: CRL Revocation test failed");
+ goto end;
+ }
+ }
+ ret= 1;
+
+end:
+ if (crl_ctx)
+ {
+ CertFreeCRLContext(crl_ctx);
+ }
+ if (ca_ctx)
+ {
+ CertFreeCertificateContext(ca_ctx);
+ }
+ if (pServerCert)
+ {
+ CertFreeCertificateContext(pServerCert);
+ }
+ return ret;
+}
+
+
+/* {{{ size_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext) */
+/*
+ Decrypts data and write to SSL stream
+ SYNOPSIS
+ ma_schannel_write_decrypt
+ pvio pointer to Communication IO structure
+ phContext a context handle
+ DecryptLength size of decrypted buffer
+ ReadBuffer Buffer for decrypted data
+ ReadBufferSize size of ReadBuffer
+
+ DESCRIPTION
+ Write encrypted data to SSL stream.
+
+ RETURN
+ SEC_E_OK on success
+ SEC_E_* if an error occured
+*/
+ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
+ uchar *WriteBuffer,
+ size_t WriteBufferSize)
+{
+ SECURITY_STATUS scRet;
+ SecBufferDesc Message;
+ SecBuffer Buffers[4];
+ DWORD cbMessage;
+ PBYTE pbMessage;
+ SC_CTX *sctx= (SC_CTX *)pvio->ctls->ssl;
+ size_t payload;
+ ssize_t nbytes;
+ DWORD write_size;
+
+ payload= MIN(WriteBufferSize, sctx->Sizes.cbMaximumMessage);
+
+ memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
+ pbMessage = sctx->IoBuffer + sctx->Sizes.cbHeader;
+ cbMessage = (DWORD)payload;
+
+ Buffers[0].pvBuffer = sctx->IoBuffer;
+ Buffers[0].cbBuffer = sctx->Sizes.cbHeader;
+ Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
+
+ Buffers[1].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader];
+ Buffers[1].cbBuffer = (DWORD)payload;
+ Buffers[1].BufferType = SECBUFFER_DATA;
+
+ Buffers[2].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader] + payload;
+ Buffers[2].cbBuffer = sctx->Sizes.cbTrailer;
+ Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+ Buffers[3].pvBuffer = SECBUFFER_EMPTY; // Pointer to buffer 4
+ Buffers[3].cbBuffer = SECBUFFER_EMPTY; // length of buffer 4
+ Buffers[3].BufferType = SECBUFFER_EMPTY; // Type of the buffer 4
+
+
+ Message.ulVersion = SECBUFFER_VERSION;
+ Message.cBuffers = 4;
+ Message.pBuffers = Buffers;
+ if ((scRet = EncryptMessage(&sctx->ctxt, 0, &Message, 0))!= SEC_E_OK)
+ return -1;
+ write_size = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
+ nbytes = pvio->methods->write(pvio, sctx->IoBuffer, write_size);
+ return nbytes == write_size ? payload : -1;
+}
+/* }}} */
+
+extern char *ssl_protocol_version[5];
+
+/* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls) */
+int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
+{
+ SC_CTX *sctx;
+ SecPkgContext_ConnectionInfo ConnectionInfo;
+ if (!ctls->ssl)
+ return 1;
+
+ sctx= (SC_CTX *)ctls->ssl;
+
+ if (QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_CONNECTION_INFO, &ConnectionInfo) != SEC_E_OK)
+ return -1;
+
+ switch(ConnectionInfo.dwProtocol)
+ {
+ case SP_PROT_SSL3_CLIENT:
+ return PROTOCOL_SSLV3;
+ case SP_PROT_TLS1_CLIENT:
+ return PROTOCOL_TLS_1_0;
+ case SP_PROT_TLS1_1_CLIENT:
+ return PROTOCOL_TLS_1_1;
+ case SP_PROT_TLS1_2_CLIENT:
+ return PROTOCOL_TLS_1_2;
+ default:
+ return -1;
+ }
+}
+/* }}} */
diff --git a/mysql/libmariadb/secure/ma_schannel.h b/mysql/libmariadb/secure/ma_schannel.h
new file mode 100644
index 0000000..08ff3e7
--- /dev/null
+++ b/mysql/libmariadb/secure/ma_schannel.h
@@ -0,0 +1,87 @@
+/************************************************************************************
+ Copyright (C) 2014 MariaDB Corporation 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
+
+ Author: Georg Richter
+
+ *************************************************************************************/
+#ifndef _ma_schannel_h_
+#define _ma_schannel_h_
+
+#define SECURITY_WIN32
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <ma_pvio.h>
+#include <errmsg.h>
+
+
+#include <wincrypt.h>
+#include <wintrust.h>
+
+
+#include <security.h>
+
+#include <schnlsp.h>
+#undef SECURITY_WIN32
+#include <Windows.h>
+#include <sspi.h>
+
+#define SC_IO_BUFFER_SIZE 0x4000
+
+
+#include <ma_pthread.h>
+
+struct st_schannel {
+ HCERTSTORE cert_store;
+ CERT_CONTEXT *client_cert_ctx;
+ CredHandle CredHdl;
+ my_bool FreeCredHdl;
+ PUCHAR IoBuffer;
+ DWORD IoBufferSize;
+ SecPkgContext_StreamSizes Sizes;
+ CtxtHandle ctxt;
+
+ /* Cached data from the last read/decrypt call.*/
+ SecBuffer extraBuf; /* encrypted data read from server. */
+ SecBuffer dataBuf; /* decrypted but still unread data from server.*/
+
+};
+
+typedef struct st_schannel SC_CTX;
+
+extern HCERTSTORE ca_CertStore, crl_CertStore;
+extern my_bool ca_Check, crl_Check;
+
+CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_PVIO *pvio, const char *pem_file);
+SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls);
+SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData);
+my_bool ma_schannel_load_private_key(MARIADB_PVIO *pvio, CERT_CONTEXT *ctx, char *key_file);
+PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_PVIO *pvio, const char *pem_file);
+my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls);
+ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
+ uchar *WriteBuffer,
+ size_t WriteBufferSize);
+ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
+ PCredHandle phCreds,
+ CtxtHandle * phContext,
+ DWORD *DecryptLength,
+ uchar *ReadBuffer,
+ DWORD ReadBufferSize);
+
+
+#endif /* _ma_schannel_h_ */
diff --git a/mysql/libmariadb/secure/schannel.c b/mysql/libmariadb/secure/schannel.c
new file mode 100644
index 0000000..2725172
--- /dev/null
+++ b/mysql/libmariadb/secure/schannel.c
@@ -0,0 +1,586 @@
+/************************************************************************************
+ Copyright (C) 2014 MariaDB Corporation Ab
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not see <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+#include "ma_schannel.h"
+
+#pragma comment (lib, "crypt32.lib")
+#pragma comment (lib, "secur32.lib")
+
+extern my_bool ma_tls_initialized;
+char tls_library_version[TLS_VERSION_LENGTH];
+
+#define PROT_SSL3 1
+#define PROT_TLS1_0 2
+#define PROT_TLS1_2 4
+#define PROT_TLS1_3 8
+
+static struct
+{
+ DWORD cipher_id;
+ DWORD protocol;
+ const char *iana_name;
+ const char *openssl_name;
+ ALG_ID algs[4]; /* exchange, encryption, hash, signature */
+}
+cipher_map[] =
+{
+ {
+ 0x0002,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_RSA_WITH_NULL_SHA", "NULL-SHA",
+ { CALG_RSA_KEYX, 0, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0004,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_RSA_WITH_RC4_128_MD5", "RC4-MD5",
+ { CALG_RSA_KEYX, CALG_RC4, CALG_MD5, CALG_RSA_SIGN }
+ },
+ {
+ 0x0005,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_RSA_WITH_RC4_128_SHA", "RC4-SHA",
+ { CALG_RSA_KEYX, CALG_RC4, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x000A,
+ PROT_SSL3,
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "DES-CBC3-SHA",
+ {CALG_RSA_KEYX, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN}
+ },
+ {
+ 0x0013,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "EDH-DSS-DES-CBC3-SHA",
+ { CALG_DH_EPHEM, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN }
+ },
+ {
+ 0x002F,
+ PROT_SSL3 | PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_128_CBC_SHA", "AES128-SHA",
+ { CALG_RSA_KEYX, CALG_AES_128, CALG_SHA, CALG_RSA_SIGN}
+ },
+ {
+ 0x0032,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DHE-DSS-AES128-SHA",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0033,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DHE-RSA-AES128-SHA",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0035,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_256_CBC_SHA", "AES256-SHA",
+ { CALG_RSA_KEYX, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0038,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DHE-DSS-AES256-SHA",
+ { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_DSS_SIGN }
+ },
+ {
+ 0x0039,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DHE-RSA-AES256-SHA",
+ { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x003B,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_NULL_SHA256", "NULL-SHA256",
+ { CALG_RSA_KEYX, 0, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x003C,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_128_CBC_SHA256", "AES128-SHA256",
+ { CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x003D,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_256_CBC_SHA256", "AES256-SHA256",
+ { CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x0040,
+ PROT_TLS1_2,
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DHE-DSS-AES128-SHA256",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_DSS_SIGN }
+ },
+ {
+ 0x009C,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_128_GCM_SHA256", "AES128-GCM-SHA256",
+ { CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x009D,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_256_GCM_SHA384", "AES256-GCM-SHA384",
+ { CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
+ },
+ {
+ 0x009E,
+ PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DHE-RSA-AES128-GCM-SHA256",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x009F,
+ PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DHE-RSA-AES256-GCM-SHA384",
+ { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
+ }
+
+};
+
+#define MAX_ALG_ID 50
+
+void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
+void ma_schannel_set_win_error(MYSQL *mysql);
+
+/*
+ Initializes SSL and allocate global
+ context SSL_context
+
+ SYNOPSIS
+ ma_tls_start
+
+ RETURN VALUES
+ 0 success
+ 1 error
+*/
+int ma_tls_start(char *errmsg, size_t errmsg_len)
+{
+ DWORD size;
+ DWORD handle;
+
+ if ((size= GetFileVersionInfoSize("schannel.dll", &handle)))
+ {
+ LPBYTE VersionInfo;
+ if ((VersionInfo = (LPBYTE)malloc(size)))
+ {
+ unsigned int len;
+ VS_FIXEDFILEINFO *fileinfo;
+
+ GetFileVersionInfo("schannel.dll", 0, size, VersionInfo);
+ VerQueryValue(VersionInfo, "\\", (LPVOID *)&fileinfo, &len);
+ snprintf(tls_library_version, TLS_VERSION_LENGTH - 1, "Schannel %d.%d.%d.%d\n",
+ HIWORD(fileinfo->dwFileVersionMS),
+ LOWORD(fileinfo->dwFileVersionMS),
+ HIWORD(fileinfo->dwFileVersionLS),
+ LOWORD(fileinfo->dwFileVersionLS));
+ free(VersionInfo);
+ goto end;
+ }
+ }
+ /* this shouldn't happen anyway */
+ strcpy(tls_library_version, "Schannel 0.0.0.0");
+end:
+ ma_tls_initialized = TRUE;
+ return 0;
+}
+
+/*
+ Release SSL and free resources
+ Will be automatically executed by
+ mysql_server_end() function
+
+ SYNOPSIS
+ ma_tls_end()
+ void
+
+ RETURN VALUES
+ void
+*/
+void ma_tls_end()
+{
+ return;
+}
+
+/* {{{ static int ma_tls_set_client_certs(MARIADB_TLS *ctls) */
+static int ma_tls_set_client_certs(MARIADB_TLS *ctls)
+{
+ MYSQL *mysql= ctls->pvio->mysql;
+ char *certfile= mysql->options.ssl_cert,
+ *keyfile= mysql->options.ssl_key;
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+ MARIADB_PVIO *pvio= ctls->pvio;
+
+ sctx->client_cert_ctx= NULL;
+
+ if (!certfile && keyfile)
+ certfile= keyfile;
+ if (!keyfile && certfile)
+ keyfile= certfile;
+
+ if (!certfile)
+ return 0;
+
+ if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(ctls->pvio, certfile)))
+ return 1;
+
+ if (!ma_schannel_load_private_key(pvio, sctx->client_cert_ctx, keyfile))
+ {
+ CertFreeCertificateContext(sctx->client_cert_ctx);
+ sctx->client_cert_ctx= NULL;
+ return 1;
+ }
+ return 0;
+}
+/* }}} */
+
+/* {{{ void *ma_tls_init(MARIADB_TLS *ctls, MYSQL *mysql) */
+void *ma_tls_init(MYSQL *mysql)
+{
+ SC_CTX *sctx= NULL;
+ if ((sctx= (SC_CTX *)LocalAlloc(0, sizeof(SC_CTX))))
+ ZeroMemory(sctx, sizeof(SC_CTX));
+ return sctx;
+}
+/* }}} */
+
+
+/*
+ Maps between openssl suite names and schannel alg_ids.
+ Every suite has 4 algorithms (for exchange, encryption, hash and signing).
+
+ The input string is a set of suite names (openssl), separated
+ by ':'
+
+ The output is written into the array 'arr' of size 'arr_size'
+ The function returns number of elements written to the 'arr'.
+*/
+
+static struct _tls_version {
+ const char *tls_version;
+ DWORD protocol;
+} tls_version[]= {
+ {"TLSv1.0", PROT_TLS1_0},
+ {"TLSv1.2", PROT_TLS1_2},
+ {"TLSv1.3", PROT_TLS1_3},
+ {"SSLv3", PROT_SSL3}
+};
+
+static size_t set_cipher(char * cipher_str, DWORD protocol, ALG_ID *arr , size_t arr_size)
+{
+ char *token = strtok(cipher_str, ":");
+ size_t pos = 0;
+
+ while (token)
+ {
+ size_t i;
+
+ for(i = 0; i < sizeof(cipher_map)/sizeof(cipher_map[0]) ; i++)
+ {
+ if(pos + 4 < arr_size && strcmp(cipher_map[i].openssl_name, token) == 0 ||
+ (cipher_map[i].protocol <= protocol))
+ {
+ memcpy(arr + pos, cipher_map[i].algs, sizeof(ALG_ID)* 4);
+ pos += 4;
+ break;
+ }
+ }
+ token = strtok(NULL, ":");
+ }
+ return pos;
+}
+
+my_bool ma_tls_connect(MARIADB_TLS *ctls)
+{
+ MYSQL *mysql;
+ SCHANNEL_CRED Cred;
+ MARIADB_PVIO *pvio;
+ my_bool rc= 1;
+ SC_CTX *sctx;
+ SECURITY_STATUS sRet;
+ ALG_ID AlgId[MAX_ALG_ID];
+ WORD validTokens = 0;
+
+ if (!ctls || !ctls->pvio)
+ return 1;;
+
+ pvio= ctls->pvio;
+ sctx= (SC_CTX *)ctls->ssl;
+
+ mysql= pvio->mysql;
+
+ if (ma_tls_set_client_certs(ctls))
+ goto end;
+
+ ZeroMemory(&Cred, sizeof(SCHANNEL_CRED));
+
+ /* Set cipher */
+ if (mysql->options.ssl_cipher)
+ {
+ int i;
+ DWORD protocol = 0;
+
+ /* check if a protocol was specified as a cipher:
+ * In this case don't allow cipher suites which belong to newer protocols
+ * Please note: There are no cipher suites for TLS1.1
+ */
+ for (i = 0; i < sizeof(tls_version) / sizeof(tls_version[0]); i++)
+ {
+ if (!stricmp(mysql->options.ssl_cipher, tls_version[i].tls_version))
+ protocol |= tls_version[i].protocol;
+ }
+ memset(AlgId, 0, MAX_ALG_ID * sizeof(ALG_ID));
+ Cred.cSupportedAlgs = (DWORD)set_cipher(mysql->options.ssl_cipher, protocol, AlgId, MAX_ALG_ID);
+ if (Cred.cSupportedAlgs)
+ {
+ Cred.palgSupportedAlgs = AlgId;
+ }
+ else if (!protocol)
+ {
+ ma_schannel_set_sec_error(pvio, SEC_E_ALGORITHM_MISMATCH);
+ goto end;
+ }
+ }
+
+ Cred.dwVersion= SCHANNEL_CRED_VERSION;
+
+ Cred.dwFlags = SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
+
+ if (sctx->client_cert_ctx)
+ {
+ Cred.cCreds = 1;
+ Cred.paCred = &sctx->client_cert_ctx;
+ }
+ if (mysql->options.extension && mysql->options.extension->tls_version)
+ {
+ if (strstr(mysql->options.extension->tls_version, "TLSv1.0"))
+ Cred.grbitEnabledProtocols|= SP_PROT_TLS1_0_CLIENT;
+ if (strstr(mysql->options.extension->tls_version, "TLSv1.1"))
+ Cred.grbitEnabledProtocols|= SP_PROT_TLS1_1_CLIENT;
+ if (strstr(mysql->options.extension->tls_version, "TLSv1.2"))
+ Cred.grbitEnabledProtocols|= SP_PROT_TLS1_2_CLIENT;
+ }
+ if (!Cred.grbitEnabledProtocols)
+ Cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT;
+
+ if ((sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
+ NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL)) != SEC_E_OK)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ goto end;
+ }
+ sctx->FreeCredHdl= 1;
+
+ if (ma_schannel_client_handshake(ctls) != SEC_E_OK)
+ goto end;
+
+ if (!ma_schannel_verify_certs(ctls))
+ goto end;
+
+ return 0;
+
+end:
+ if (rc && sctx->IoBufferSize)
+ LocalFree(sctx->IoBuffer);
+ sctx->IoBufferSize= 0;
+ if (sctx->client_cert_ctx)
+ CertFreeCertificateContext(sctx->client_cert_ctx);
+ sctx->client_cert_ctx= 0;
+ return 1;
+}
+
+ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+ MARIADB_PVIO *pvio= ctls->pvio;
+ DWORD dlength= 0;
+ SECURITY_STATUS status = ma_schannel_read_decrypt(pvio, &sctx->CredHdl, &sctx->ctxt, &dlength, (uchar *)buffer, (DWORD)length);
+ if (status == SEC_I_CONTEXT_EXPIRED)
+ return 0; /* other side shut down the connection. */
+ if (status == SEC_I_RENEGOTIATE)
+ return -1; /* Do not handle renegotiate yet */
+
+ return (status == SEC_E_OK)? (ssize_t)dlength : -1;
+}
+
+ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+ MARIADB_PVIO *pvio= ctls->pvio;
+ ssize_t rc, wlength= 0;
+ ssize_t remain= length;
+
+ while (remain > 0)
+ {
+ if ((rc= ma_schannel_write_encrypt(pvio, (uchar *)buffer + wlength, remain)) <= 0)
+ return rc;
+ wlength+= rc;
+ remain-= rc;
+ }
+ return length;
+}
+
+/* {{{ my_bool ma_tls_close(MARIADB_PVIO *pvio) */
+my_bool ma_tls_close(MARIADB_TLS *ctls)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+
+ if (sctx)
+ {
+ if (sctx->IoBufferSize)
+ LocalFree(sctx->IoBuffer);
+ if (sctx->client_cert_ctx)
+ CertFreeCertificateContext(sctx->client_cert_ctx);
+ FreeCredentialHandle(&sctx->CredHdl);
+ DeleteSecurityContext(&sctx->ctxt);
+ }
+ LocalFree(sctx);
+ return 0;
+}
+/* }}} */
+
+int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+ MARIADB_PVIO *pvio= ctls->pvio;
+ int rc= 1;
+ char *szName= NULL;
+ char *pszServerName= pvio->mysql->host;
+ PCCERT_CONTEXT pServerCert= NULL;
+
+ /* check server name */
+ if (pszServerName && (ctls->pvio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
+ {
+ DWORD NameSize= 0;
+ char *p1;
+ SECURITY_STATUS sRet;
+
+ if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ goto end;
+ }
+
+ if (!(NameSize= CertGetNameString(pServerCert,
+ CERT_NAME_DNS_TYPE,
+ CERT_NAME_SEARCH_ALL_NAMES_FLAG,
+ NULL, NULL, 0)))
+ {
+ pvio->set_error(ctls->pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Can't retrieve name of server certificate");
+ goto end;
+ }
+
+ if (!(szName= (char *)LocalAlloc(0, NameSize + 1)))
+ {
+ pvio->set_error(ctls->pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
+ goto end;
+ }
+
+ if (!CertGetNameString(pServerCert,
+ CERT_NAME_DNS_TYPE,
+ CERT_NAME_SEARCH_ALL_NAMES_FLAG,
+ NULL, szName, NameSize))
+
+ {
+ pvio->set_error(ctls->pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: Can't retrieve name of server certificate");
+ goto end;
+ }
+
+ /* szName may contain multiple names: Each name is zero terminated, the last name is
+ double zero terminated */
+
+
+ p1 = szName;
+ while (p1 && *p1 != 0)
+ {
+ DWORD len = strlen(p1);
+ /* check if given name contains wildcard */
+ if (len && *p1 == '*')
+ {
+ DWORD hostlen = strlen(pszServerName);
+ if (hostlen < len)
+ break;
+ if (!stricmp(pszServerName + hostlen - len + 1, p1 + 1))
+ {
+ rc = 0;
+ goto end;
+ }
+ }
+ else if (!stricmp(pszServerName, p1))
+ {
+ rc = 0;
+ goto end;
+ }
+ p1 += (len + 1);
+ }
+ pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ "SSL connection error: Name of server certificate didn't match");
+ }
+end:
+ if (szName)
+ LocalFree(szName);
+ if (pServerCert)
+ CertFreeCertificateContext(pServerCert);
+ return rc;
+}
+
+static const char *cipher_name(const SecPkgContext_CipherInfo *CipherInfo)
+{
+ int i;
+
+ for(i = 0; i < sizeof(cipher_map)/sizeof(cipher_map[0]) ; i++)
+ {
+ if (CipherInfo->dwCipherSuite == cipher_map[i].cipher_id)
+ return cipher_map[i].openssl_name;
+ }
+ return "";
+};
+
+const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
+{
+ SecPkgContext_CipherInfo CipherInfo = { SECPKGCONTEXT_CIPHERINFO_V1 };
+ SECURITY_STATUS sRet;
+ SC_CTX *sctx;
+ DWORD i= 0;
+
+ if (!ctls || !ctls->ssl)
+ return NULL;
+
+ sctx= (SC_CTX *)ctls->ssl;
+
+ sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_CIPHER_INFO, (PVOID)&CipherInfo);
+ if (sRet != SEC_E_OK)
+ return NULL;
+
+ return cipher_name(&CipherInfo);
+}
+
+unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+ PCCERT_CONTEXT pRemoteCertContext = NULL;
+ if (QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK)
+ return 0;
+ CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, (DWORD *)&len);
+ CertFreeCertificateContext(pRemoteCertContext);
+ return len;
+}