diff options
33 files changed, 1370 insertions, 307 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d7380c8f75..99517d04497 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,6 +325,7 @@ ADD_SUBDIRECTORY(client) ADD_SUBDIRECTORY(sql) ADD_SUBDIRECTORY(server-tools/instance-manager) ADD_SUBDIRECTORY(libmysql) +ADD_SUBDIRECTORY(libservices) ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(unittest/mytap) ADD_SUBDIRECTORY(unittest/examples) diff --git a/Makefile.am b/Makefile.am index 7f212a67c3f..17d2e2ce891 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,7 +25,7 @@ EXTRA_DIST = INSTALL-SOURCE INSTALL-WIN-SOURCE \ SUBDIRS = . include @docs_dirs@ @zlib_dir@ \ @readline_topdir@ sql-common scripts \ - @pstack_dir@ \ + @pstack_dir@ libservices \ @sql_union_dirs@ storage \ @sql_server@ @man_dirs@ tests \ netware @libmysqld_dirs@ \ @@ -34,7 +34,7 @@ SUBDIRS = . include @docs_dirs@ @zlib_dir@ \ DIST_SUBDIRS = . include Docs zlib \ cmd-line-utils sql-common scripts \ - pstack \ + pstack libservices \ strings mysys dbug extra regex libmysql libmysql_r client unittest storage plugin \ vio sql man tests \ netware libmysqld \ diff --git a/configure.in b/configure.in index 639fc4174d1..a18e1a26868 100644 --- a/configure.in +++ b/configure.in @@ -2913,7 +2913,7 @@ AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl man/Makefile BUILD/Makefile vio/Makefile dnl libmysql/Makefile libmysql_r/Makefile client/Makefile dnl sql/Makefile sql/share/Makefile dnl - sql/sql_builtin.cc sql-common/Makefile dnl + sql/sql_builtin.cc sql-common/Makefile libservices/Makefile dnl dbug/Makefile scripts/Makefile include/Makefile dnl tests/Makefile Docs/Makefile support-files/Makefile dnl support-files/MacOSX/Makefile support-files/RHEL4-SElinux/Makefile dnl diff --git a/include/Makefile.am b/include/Makefile.am index 516fdeee392..d5f68f25026 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -21,7 +21,8 @@ HEADERS_GEN_MAKE = my_config.h HEADERS_ABI = mysql.h mysql_com.h mysql_time.h \ my_list.h my_alloc.h typelib.h mysql/plugin.h pkginclude_HEADERS = $(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \ - my_xml.h mysql_embed.h \ + my_xml.h mysql_embed.h mysql/services.h \ + mysql/service_my_snprintf.h mysql/service_thd_alloc.h \ my_pthread.h my_no_pthread.h \ decimal.h errmsg.h my_global.h my_net.h \ my_getopt.h sslopt-longopts.h my_dir.h \ @@ -36,7 +37,7 @@ noinst_HEADERS = config-win.h config-netware.h lf.h my_bit.h \ my_nosys.h my_alarm.h queues.h rijndael.h sha1.h \ my_aes.h my_tree.h my_trie.h hash.h thr_alarm.h \ thr_lock.h t_ctype.h violite.h my_md5.h base64.h \ - my_handler.h my_time.h \ + my_handler.h my_time.h service_versions.h \ my_vle.h my_user.h my_atomic.h atomic/nolock.h \ atomic/rwlock.h atomic/x86-gcc.h atomic/generic-msvc.h \ atomic/gcc_builtins.h my_libwrap.h my_stacktrace.h \ diff --git a/include/m_string.h b/include/m_string.h index 4bfc4ae51a9..737ad4be606 100644 --- a/include/m_string.h +++ b/include/m_string.h @@ -257,16 +257,10 @@ extern size_t my_snprintf(char *to, size_t n, const char *fmt, ...) /* LEX_STRING -- a pair of a C-string and its length. + (it's part of the plugin API as a MYSQL_LEX_STRING) */ -#ifndef _my_plugin_h -/* This definition must match the one given in mysql/plugin.h */ -struct st_mysql_lex_string -{ - char *str; - size_t length; -}; -#endif +#include <mysql/plugin.h> typedef struct st_mysql_lex_string LEX_STRING; #define STRING_WITH_LEN(X) (X), ((size_t) (sizeof(X) - 1)) diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index 23e508f8f46..5ac0472fc5f 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2005 MySQL AB +/* Copyright (C) 2005 MySQL AB, 2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,15 +34,7 @@ class Item; #define MYSQL_THD void* #endif -#ifndef _m_string_h -/* This definition must match the one given in m_string.h */ -struct st_mysql_lex_string -{ - char *str; - unsigned int length; -}; -#endif /* _m_string_h */ -typedef struct st_mysql_lex_string MYSQL_LEX_STRING; +#include <mysql/services.h> #define MYSQL_XIDDATASIZE 128 /** @@ -65,7 +57,7 @@ typedef struct st_mysql_xid MYSQL_XID; Plugin API. Common for all plugin types. */ -#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0101 /* The allowable types of plugins @@ -120,7 +112,8 @@ enum enum_mysql_show_type { SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG, SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR, - SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE + SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE, + SHOW_always_last }; struct st_mysql_show_var { @@ -739,54 +732,6 @@ int thd_killed(const MYSQL_THD thd); */ unsigned long thd_get_thread_id(const MYSQL_THD thd); - -/** - Allocate memory in the connection's local memory pool - - @details - When properly used in place of @c my_malloc(), this can significantly - improve concurrency. Don't use this or related functions to allocate - large chunks of memory. Use for temporary storage only. The memory - will be freed automatically at the end of the statement; no explicit - code is required to prevent memory leaks. - - @see alloc_root() -*/ -void *thd_alloc(MYSQL_THD thd, unsigned int size); -/** - @see thd_alloc() -*/ -void *thd_calloc(MYSQL_THD thd, unsigned int size); -/** - @see thd_alloc() -*/ -char *thd_strdup(MYSQL_THD thd, const char *str); -/** - @see thd_alloc() -*/ -char *thd_strmake(MYSQL_THD thd, const char *str, unsigned int size); -/** - @see thd_alloc() -*/ -void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size); - -/** - Create a LEX_STRING in this connection's local memory pool - - @param thd user thread connection handle - @param lex_str pointer to LEX_STRING object to be initialized - @param str initializer to be copied into lex_str - @param size length of str, in bytes - @param allocate_lex_string flag: if TRUE, allocate new LEX_STRING object, - instead of using lex_str value - @return NULL on failure, or pointer to the LEX_STRING object - - @see thd_alloc() -*/ -MYSQL_LEX_STRING *thd_make_lex_string(MYSQL_THD thd, MYSQL_LEX_STRING *lex_str, - const char *str, unsigned int size, - int allocate_lex_string); - /** Get the XID for this connection's transaction diff --git a/include/mysql/plugin.h.pp b/include/mysql/plugin.h.pp index e43f228bad5..c5c520dfe51 100644 --- a/include/mysql/plugin.h.pp +++ b/include/mysql/plugin.h.pp @@ -1,9 +1,38 @@ +#include <mysql/services.h> +#include <mysql/service_my_snprintf.h> +#include <stdarg.h> +#include <stdlib.h> +extern struct my_snprintf_service_st { + size_t (*my_snprintf_type)(char*, size_t, const char*, ...); + size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list); +} *my_snprintf_service; +size_t my_snprintf(char* to, size_t n, const char* fmt, ...); +size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap); +#include <mysql/service_thd_alloc.h> +#include <stdlib.h> struct st_mysql_lex_string { char *str; - unsigned int length; + size_t length; }; typedef struct st_mysql_lex_string MYSQL_LEX_STRING; +extern struct thd_alloc_service_st { + void *(*thd_alloc_func)(void*, unsigned int); + void *(*thd_calloc_func)(void*, unsigned int); + char *(*thd_strdup_func)(void*, const char *); + char *(*thd_strmake_func)(void*, const char *, unsigned int); + void *(*thd_memdup_func)(void*, const void*, unsigned int); + MYSQL_LEX_STRING *(*thd_make_lex_string_func)(void*, MYSQL_LEX_STRING *, + const char *, unsigned int, int); +} *thd_alloc_service; +void *thd_alloc(void* thd, unsigned int size); +void *thd_calloc(void* thd, unsigned int size); +char *thd_strdup(void* thd, const char *str); +char *thd_strmake(void* thd, const char *str, unsigned int size); +void *thd_memdup(void* thd, const void* str, unsigned int size); +MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str, + const char *str, unsigned int size, + int allocate_lex_string); struct st_mysql_xid { long formatID; long gtrid_length; @@ -15,7 +44,8 @@ enum enum_mysql_show_type { SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG, SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR, - SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE + SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE, + SHOW_always_last }; struct st_mysql_show_var { const char *name; @@ -127,14 +157,6 @@ const char *set_thd_proc_info(void*, const char * info, const char *func, int mysql_tmpfile(const char *prefix); int thd_killed(const void* thd); unsigned long thd_get_thread_id(const void* thd); -void *thd_alloc(void* thd, unsigned int size); -void *thd_calloc(void* thd, unsigned int size); -char *thd_strdup(void* thd, const char *str); -char *thd_strmake(void* thd, const char *str, unsigned int size); -void *thd_memdup(void* thd, const void* str, unsigned int size); -MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str, - const char *str, unsigned int size, - int allocate_lex_string); void thd_get_xid(const void* thd, MYSQL_XID *xid); void mysql_query_cache_invalidate4(void* thd, const char *key, unsigned int key_length, diff --git a/include/mysql/service_my_snprintf.h b/include/mysql/service_my_snprintf.h new file mode 100644 index 00000000000..ad344864c34 --- /dev/null +++ b/include/mysql/service_my_snprintf.h @@ -0,0 +1,98 @@ +#ifndef MYSQL_SERVICE_MY_SNPRINTF_INCLUDED +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + my_snprintf service + + Portable and limited vsnprintf() implementation. + + This is a portable, limited vsnprintf() implementation, with some + extra features. "Portable" means that it'll produce identical result + on all platforms (for example, on Windows and Linux system printf %e + formats the exponent differently, on different systems %p either + prints leading 0x or not, %s may accept null pointer or crash on + it). "Limited" means that it does not support all the C89 features. + But it supports few extensions, not in any standard. + + my_vsnprintf(to, n, fmt, ap) + + @param[out] to A buffer to store the result in + @param[in] n Store up to n-1 characters, followed by an end 0 + @param[in] fmt printf-like format string + @param[in] ap Arguments + + @return a number of bytes written to a buffer *excluding* terminating '\0' + + @post + The syntax of a format string is generally the same: + % <flag> <width> <precision> <length modifier> <format> + where everithing but the format is optional. + + Three one-character flags are recognized: + '0' has the standard zero-padding semantics; + '-' is parsed, but silently ignored; + '`' (backtick) is only supported for strings (%s) and means that the + string will be quoted according to MySQL identifier quoting rules. + + Both <width> and <precision> can be specified as numbers or '*'. + + <length modifier> can be 'l', 'll', or 'z'. + + Supported formats are 's' (null pointer is accepted, printed as + "(null)"), 'b' (extension, see below), 'c', 'd', 'u', 'x', + 'X', 'p' (works as 0x%x). + + Standard syntax for positional arguments $n is supported. + + Extensions: + + Flag '`' (backtick): see above. + + Format 'b': binary buffer, prints exactly <precision> bytes from the + argument, without stopping at '\0'. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> +#include <stdlib.h> +extern struct my_snprintf_service_st { + size_t (*my_snprintf_type)(char*, size_t, const char*, ...); + size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list); +} *my_snprintf_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define my_vsnprintf my_snprintf_service->my_vsnprintf_type +#define my_snprintf my_snprintf_service->my_snprintf_type + +#else + +size_t my_snprintf(char* to, size_t n, const char* fmt, ...); +size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap); + +#endif + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICE_MY_SNPRINTF_INCLUDED +#endif + diff --git a/include/mysql/service_thd_alloc.h b/include/mysql/service_thd_alloc.h new file mode 100644 index 00000000000..86158ba1359 --- /dev/null +++ b/include/mysql/service_thd_alloc.h @@ -0,0 +1,128 @@ +#ifndef MYSQL_SERVICE_THD_ALLOC_INCLUDED +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + This service provdes functions to allocate memory in a connection local + memory pool. The memory allocated there will be automatically freed at the + end of the statement, don't use it for allocations that should live longer + than that. For short living allocations this is more efficient than + using my_malloc and friends, and automatic "garbage collection" allows not + to think about memory leaks. + + The pool is best for small to medium objects, don't use it for large + allocations - they are better served with my_malloc. +*/ + +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct st_mysql_lex_string +{ + char *str; + size_t length; +}; +typedef struct st_mysql_lex_string MYSQL_LEX_STRING; + +extern struct thd_alloc_service_st { + void *(*thd_alloc_func)(MYSQL_THD, unsigned int); + void *(*thd_calloc_func)(MYSQL_THD, unsigned int); + char *(*thd_strdup_func)(MYSQL_THD, const char *); + char *(*thd_strmake_func)(MYSQL_THD, const char *, unsigned int); + void *(*thd_memdup_func)(MYSQL_THD, const void*, unsigned int); + MYSQL_LEX_STRING *(*thd_make_lex_string_func)(MYSQL_THD, MYSQL_LEX_STRING *, + const char *, unsigned int, int); +} *thd_alloc_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define thd_alloc(thd,size) (thd_alloc_service->thd_alloc_func((thd), (size))) + +#define thd_calloc(thd,size) (thd_alloc_service->thd_calloc_func((thd), (size))) + +#define thd_strdup(thd,str) (thd_alloc_service->thd_strdup_func((thd), (str))) + +#define thd_strmake(thd,str,size) \ + (thd_alloc_service->thd_strmake_func((thd), (str), (size))) + +#define thd_memdup(thd,str,size) \ + (thd_alloc_service->thd_memdup_func((thd), (str), (size))) + +#define thd_make_lex_string(thd, lex_str, str, size, allocate_lex_string) \ + (thd_alloc_service->thd_make_lex_string_func((thd), (lex_str), (str), \ + (size), (allocate_lex_string))) + +#else + +/** + Allocate memory in the connection's local memory pool + + @details + When properly used in place of @c my_malloc(), this can significantly + improve concurrency. Don't use this or related functions to allocate + large chunks of memory. Use for temporary storage only. The memory + will be freed automatically at the end of the statement; no explicit + code is required to prevent memory leaks. + + @see alloc_root() +*/ +void *thd_alloc(MYSQL_THD thd, unsigned int size); +/** + @see thd_alloc() +*/ +void *thd_calloc(MYSQL_THD thd, unsigned int size); +/** + @see thd_alloc() +*/ +char *thd_strdup(MYSQL_THD thd, const char *str); +/** + @see thd_alloc() +*/ +char *thd_strmake(MYSQL_THD thd, const char *str, unsigned int size); +/** + @see thd_alloc() +*/ +void *thd_memdup(MYSQL_THD thd, const void* str, unsigned int size); + +/** + Create a LEX_STRING in this connection's local memory pool + + @param thd user thread connection handle + @param lex_str pointer to LEX_STRING object to be initialized + @param str initializer to be copied into lex_str + @param size length of str, in bytes + @param allocate_lex_string flag: if TRUE, allocate new LEX_STRING object, + instead of using lex_str value + @return NULL on failure, or pointer to the LEX_STRING object + + @see thd_alloc() +*/ +MYSQL_LEX_STRING *thd_make_lex_string(MYSQL_THD thd, MYSQL_LEX_STRING *lex_str, + const char *str, unsigned int size, + int allocate_lex_string); + +#endif + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICE_THD_ALLOC_INCLUDED +#endif + diff --git a/include/mysql/services.h b/include/mysql/services.h new file mode 100644 index 00000000000..19003e66b96 --- /dev/null +++ b/include/mysql/services.h @@ -0,0 +1,30 @@ +#ifndef MYSQL_SERVICES_INCLUDED +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <mysql/service_my_snprintf.h> +#include <mysql/service_thd_alloc.h> + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICES_INCLUDED +#endif + diff --git a/include/service_versions.h b/include/service_versions.h new file mode 100644 index 00000000000..114957cdd86 --- /dev/null +++ b/include/service_versions.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef _WIN32 +#define SERVICE_VERSION __declspec(dllexport) void * +#else +#define SERVICE_VERSION void * +#endif + +#define VERSION_my_snprintf 0x0100 +#define VERSION_thd_alloc 0x0100 + diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt new file mode 100644 index 00000000000..ddfa2495ade --- /dev/null +++ b/libservices/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2006 MySQL AB +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +SET(MYSQLSERVICES_SOURCES my_snprintf_service.c thd_alloc_service.c) + +ADD_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES}) diff --git a/libservices/HOWTO b/libservices/HOWTO new file mode 100644 index 00000000000..3e5597105ab --- /dev/null +++ b/libservices/HOWTO @@ -0,0 +1,100 @@ +How to create a new service +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A "service" is a set of C functions in a structure that a +service dynamic linker uses when a dynamic plugin is loaded. + +If you want to export C++ class you need to provide an +extern "C" function that will create a new instance of your class, +and put it in a service. + +Data structures are not part of the service structure, but they are part +of the API you create and usually need to be declared in the same +service_*.h file. + +To turn a set of functions (foo_func1, foo_func2) +into a service "foo" you need to + +1. create a new file include/mysql/service_foo.h + +2. the template is +================================================================== + #ifndef MYSQL_SERVICE_FOO_INCLUDED + /* standard GPL header */ + + /** + @file + *exhaustive* description of the interface you provide. + This file is the main user documentation of the new service + */ + #ifdef __cplusplus + extern "C" { + #endif + + extern struct foo_service_st { + int (*foo_func1_type)(...); /* fix the prototype as appropriate */ + void (*foo_func2_type)(...); /* fix the prototype as appropriate */ + } *foo_service; + + #ifdef MYSQL_DYNAMIC_PLUGIN + + #define foo_func1(...) foo_service->foo_func1_type(...) + #define foo_func2(...) foo_service->foo_func2_type(...) + + #else + + int foo_func1(...); /* fix the prototype as appropriate */ + void foo_func2(...); /* fix the prototype as appropriate */ + + #endif + + #ifdef __cplusplus + } + #endif + + #define MYSQL_SERVICE_FOO_INCLUDED + #endif +================================================================== + +the service_foo.h file should be self-contained, if it needs system headers - +include them in it, e.g. if you use size_t - #include <stdlib.h> + +it should also declare all the accompanying data structures, as necessary +(e.g. thd_alloc_service declares MYSQL_LEX_STRING). + +3. add the new file to include/Makefile.am (pkginclude_HEADERS) +4. add the new file to include/mysql/services.h +5. increase the minor plugin ABI version in include/mysql/plugin.h + (MYSQL_PLUGIN_INTERFACE_VERSION = MYSQL_PLUGIN_INTERFACE_VERSION+1) +6. add the version of your service to include/service_versions.h: +================================================================== + #define VERSION_foo 0x0100 +================================================================== + +7. create a new file libservices/foo_service.c using the following template: +================================================================== + /* GPL header */ + #include <service_versions.h> + SERVICE_VERSION *foo_service= (void*)VERSION_foo; +================================================================== + +8. add the new file to libservices/CMakeLists.txt (MYSQLSERVICES_SOURCES) +9. add the new file to libservices/Makefile.am (libmysqlservices_a_SOURCES) +10. and finally, register your service for dynamic linking in + sql/sql_plugin_services.h +10.1 fill in the service structure: +================================================================== + static struct foo_service_st foo_handler = { + foo_func1, + foo_func2 + } +================================================================== + +10.2 and add it to the list of services + +================================================================== + { "foo_service", VERSION_foo, &foo_handler } +================================================================== + +that's all. + diff --git a/libservices/Makefile.am b/libservices/Makefile.am new file mode 100644 index 00000000000..642081859c1 --- /dev/null +++ b/libservices/Makefile.am @@ -0,0 +1,19 @@ +# Copyright 2009 Sun Microsystems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +AM_CPPFLAGS = -I$(top_srcdir)/include +pkglib_LIBRARIES = libmysqlservices.a +libmysqlservices_a_SOURCES = my_snprintf_service.c thd_alloc_service.c +EXTRA_DIST = CMakeLists.txt diff --git a/libservices/my_snprintf_service.c b/libservices/my_snprintf_service.c new file mode 100644 index 00000000000..40d778e4b8d --- /dev/null +++ b/libservices/my_snprintf_service.c @@ -0,0 +1,17 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <service_versions.h> +SERVICE_VERSION my_snprintf_service= (void*)VERSION_my_snprintf; diff --git a/libservices/thd_alloc_service.c b/libservices/thd_alloc_service.c new file mode 100644 index 00000000000..5d4d496774c --- /dev/null +++ b/libservices/thd_alloc_service.c @@ -0,0 +1,17 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <service_versions.h> +SERVICE_VERSION *thd_alloc_service= (void*)VERSION_thd_alloc; diff --git a/mysql-test/r/plugin.result b/mysql-test/r/plugin.result index 782d2a5a9a4..85fbd1353cc 100644 --- a/mysql-test/r/plugin.result +++ b/mysql-test/r/plugin.result @@ -12,6 +12,15 @@ CREATE TABLE t1(a int) ENGINE=EXAMPLE; SELECT * FROM t1; a DROP TABLE t1; +set global example_ulong_var=500; +set global example_enum_var= e1; +show status like 'example%'; +Variable_name Value +example_func_example enum_var is 0, ulong_var is 500, really +show variables like 'example%'; +Variable_name Value +example_enum_var e1 +example_ulong_var 500 UNINSTALL PLUGIN example; UNINSTALL PLUGIN EXAMPLE; ERROR 42000: PLUGIN EXAMPLE does not exist diff --git a/mysql-test/t/plugin.test b/mysql-test/t/plugin.test index 788a7b336ef..0bf86b47dd7 100644 --- a/mysql-test/t/plugin.test +++ b/mysql-test/t/plugin.test @@ -22,6 +22,12 @@ SELECT * FROM t1; DROP TABLE t1; +# a couple of tests for variables +set global example_ulong_var=500; +set global example_enum_var= e1; +show status like 'example%'; +show variables like 'example%'; + UNINSTALL PLUGIN example; --error 1305 UNINSTALL PLUGIN EXAMPLE; diff --git a/plugin/daemon_example/Makefile.am b/plugin/daemon_example/Makefile.am index c5414cd46c7..fce67285a5f 100644 --- a/plugin/daemon_example/Makefile.am +++ b/plugin/daemon_example/Makefile.am @@ -26,7 +26,8 @@ INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \ EXTRA_LTLIBRARIES = libdaemon_example.la pkgplugin_LTLIBRARIES = @plugin_daemon_example_shared_target@ -libdaemon_example_la_LDFLAGS = -module -rpath $(pkgplugindir) +libdaemon_example_la_LDFLAGS = -module -rpath $(pkgplugindir) -L$(top_builddir)/libservices -lmysqlservices + libdaemon_example_la_CXXFLAGS= $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN libdaemon_example_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN libdaemon_example_la_SOURCES = daemon_example.cc diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index e8bc72f15d7..531dfacce7a 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -46,7 +46,7 @@ SET (SQL_SOURCE discover.cc ../libmysql/errmsg.c field.cc field_conv.cc filesort.cc gstream.cc ha_partition.cc - handler.cc hash_filo.cc hash_filo.h + handler.cc hash_filo.cc hash_filo.h sql_plugin_services.h hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index af34e961480..0242c11a22b 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -49,7 +49,7 @@ mysqld_LDADD = libndb.la \ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h \ - item_xmlfunc.h \ + item_xmlfunc.h sql_plugin_services.h \ item_create.h item_subselect.h item_row.h \ mysql_priv.h item_geofunc.h sql_bitmap.h \ procedure.h sql_class.h sql_lex.h sql_list.h \ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index c329d8ecdb3..e6c5ef31f42 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -28,6 +28,16 @@ #ifndef MYSQL_CLIENT +/* + the following #define adds server-only members to enum_mysql_show_type, + that is defined in mysql/plugin.h + it has to be before mysql/plugin.h is included. +*/ +#define SHOW_always_last SHOW_KEY_CACHE_LONG, \ + SHOW_KEY_CACHE_LONGLONG, SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \ + SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \ + SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS + #include <my_global.h> #include <mysql_version.h> #include <mysql_embed.h> diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c3ab412d9e8..4afa141b279 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1508,20 +1508,23 @@ static void clean_up_mutexes() mysys/thr_mutex.c, will give a warning on first wrong mutex usage! */ +#ifdef SAFE_MUTEX +#define always_in_that_order(A,B) \ + pthread_mutex_lock(A); pthread_mutex_lock(B); \ + pthread_mutex_unlock(B); pthread_mutex_unlock(A) +#else +#define always_in_that_order(A,B) +#endif + static void register_mutex_order() { -#ifdef SAFE_MUTEX /* We must have LOCK_open before LOCK_global_system_variables because LOCK_open is hold while sql_plugin.c::intern_sys_var_ptr() is called. */ - pthread_mutex_lock(&LOCK_open); - pthread_mutex_lock(&LOCK_global_system_variables); - - pthread_mutex_unlock(&LOCK_global_system_variables); - pthread_mutex_unlock(&LOCK_open); -#endif + always_in_that_order(&LOCK_open, &LOCK_global_system_variables); } +#undef always_in_that_order /**************************************************************************** diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index e2d7fb086db..9fc903066bc 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2005 MySQL AB +/* Copyright (C) 2005 MySQL AB, 2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -104,7 +104,9 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; -static bool initialized= 0; +/* support for Services */ + +#include "sql_plugin_services.h" /* A mutex LOCK_plugin must be acquired before accessing the @@ -118,6 +120,8 @@ static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; static bool reap_needed= false; static int plugin_array_version=0; +static bool initialized= 0; + /* write-lock on LOCK_system_variables_hash is required before modifying the following variables/structures @@ -230,6 +234,22 @@ extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, extern bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists); #endif /* EMBEDDED_LIBRARY */ +static void report_error(int where_to, uint error, ...) +{ + va_list args; + if (where_to & REPORT_TO_USER) + { + va_start(args, error); + my_printv_error(error, ER(error), MYF(0), args); + va_end(args); + } + if (where_to & REPORT_TO_LOG) + { + va_start(args, error); + error_log_print(ERROR_LEVEL, ER(error), args); + va_end(args); + } +} /**************************************************************************** Value type thunks, allows the C world to play in the C++ world @@ -350,7 +370,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) { #ifdef HAVE_DLOPEN char dlpath[FN_REFLEN]; - uint plugin_dir_len, dummy_errors, dlpathlen; + uint plugin_dir_len, dummy_errors, dlpathlen, i; struct st_plugin_dl *tmp, plugin_dl; void *sym; DBUG_ENTER("plugin_dl_add"); @@ -365,10 +385,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) system_charset_info, 1) || plugin_dir_len + dl->length + 1 >= FN_REFLEN) { - if (report & REPORT_TO_USER) - my_error(ER_UDF_NO_PATHS, MYF(0)); - if ((report & (REPORT_TO_LOG | REPORT_TO_USER)) == REPORT_TO_LOG) - sql_print_error("%s", ER(ER_UDF_NO_PATHS)); + report_error(report, ER_UDF_NO_PATHS); DBUG_RETURN(0); } /* If this dll is already loaded just increase ref_count. */ @@ -393,20 +410,14 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (*errmsg == ':') errmsg++; if (*errmsg == ' ') errmsg++; } - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, errno, errmsg); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, errno, errmsg); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, errmsg); DBUG_RETURN(0); } /* Determine interface version */ if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_interface_version_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_interface_version_sym); + report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym); DBUG_RETURN(0); } plugin_dl.version= *(int *)sym; @@ -415,28 +426,41 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) (plugin_dl.version >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8)) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, 0, - "plugin interface version mismatch"); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, 0, - "plugin interface version mismatch"); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, + "plugin interface version mismatch"); DBUG_RETURN(0); } + + /* link the services in */ + for (i= 0; i < array_elements(list_of_services); i++) + { + if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name))) + { + uint ver= (uint)(intptr)*(void**)sym; + if (ver > list_of_services[i].version || + (ver >> 8) < (list_of_services[i].version >> 8)) + { + char buf[MYSQL_ERRMSG_SIZE]; + my_snprintf(buf, sizeof(buf), + "service '%s' interface version mismatch", + list_of_services[i].name); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, buf); + DBUG_RETURN(0); + } + *(void**)sym= list_of_services[i].service; + } + } + /* Find plugin declarations */ if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_declarations_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_declarations_sym); + report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym); DBUG_RETURN(0); } if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION) { - int i; uint sizeof_st_plugin; struct st_mysql_plugin *old, *cur; char *ptr= (char *)sym; @@ -447,10 +471,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) { #ifdef ERROR_ON_NO_SIZEOF_PLUGIN_SYMBOL free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), sizeof_st_plugin_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), sizeof_st_plugin_sym); + report_error(report, ER_CANT_FIND_DL_ENTRY, sizeof_st_plugin_sym); DBUG_RETURN(0); #else /* @@ -472,10 +493,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (!cur) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length); + report_error(report, ER_OUTOFMEMORY, plugin_dl.dl.length); DBUG_RETURN(0); } /* @@ -497,10 +515,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0)))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length); + report_error(report, ER_OUTOFMEMORY, plugin_dl.dl.length); DBUG_RETURN(0); } plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length, @@ -511,19 +526,13 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(struct st_plugin_dl)); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_OUTOFMEMORY), sizeof(struct st_plugin_dl)); + report_error(report, ER_OUTOFMEMORY, sizeof(struct st_plugin_dl)); DBUG_RETURN(0); } DBUG_RETURN(tmp); #else DBUG_ENTER("plugin_dl_add"); - if (report & REPORT_TO_USER) - my_error(ER_FEATURE_DISABLED, MYF(0), "plugin", "HAVE_DLOPEN"); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_FEATURE_DISABLED), "plugin", "HAVE_DLOPEN"); + report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN"); DBUG_RETURN(0); #endif } @@ -639,7 +648,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO) /* For debugging, we do an additional malloc which allows the memory manager and/or valgrind to track locked references and - double unlocks to aid resolving reference counting.problems. + double unlocks to aid resolving reference counting problems. */ if (!(plugin= (plugin_ref) my_malloc_ci(sizeof(pi), MYF(MY_WME)))) DBUG_RETURN(NULL); @@ -722,10 +731,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, DBUG_ENTER("plugin_add"); if (plugin_find_internal(name, MYSQL_ANY_PLUGIN)) { - if (report & REPORT_TO_USER) - my_error(ER_UDF_EXISTS, MYF(0), name->str); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_UDF_EXISTS), name->str); + report_error(report, ER_UDF_EXISTS, name->str); DBUG_RETURN(TRUE); } /* Clear the whole struct to catch future extensions. */ @@ -752,10 +758,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, strxnmov(buf, sizeof(buf) - 1, "API version for ", plugin_type_names[plugin->type].str, " plugin is too different", NullS); - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dl->str, 0, buf); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dl->str, 0, buf); + report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf); goto err; } tmp.plugin= plugin; @@ -784,10 +787,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, DBUG_RETURN(FALSE); } } - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), name->str); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), name->str); + report_error(report, ER_CANT_FIND_DL_ENTRY, name->str); err: plugin_dl_del(dl); DBUG_RETURN(TRUE); @@ -1006,9 +1006,14 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) static int plugin_initialize(struct st_plugin_int *plugin) { + int ret= 1; DBUG_ENTER("plugin_initialize"); safe_mutex_assert_owner(&LOCK_plugin); + uint state= plugin->state; + DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED); + + pthread_mutex_unlock(&LOCK_plugin); if (plugin_type_initialize[plugin->plugin->type]) { if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) @@ -1027,8 +1032,7 @@ static int plugin_initialize(struct st_plugin_int *plugin) goto err; } } - - plugin->state= PLUGIN_IS_READY; + state= PLUGIN_IS_READY; // plugin->init() succeeded if (plugin->plugin->status_vars) { @@ -1047,7 +1051,8 @@ static int plugin_initialize(struct st_plugin_int *plugin) if (add_status_vars(array)) // add_status_vars makes a copy goto err; #else - add_status_vars(plugin->plugin->status_vars); // add_status_vars makes a copy + if (add_status_vars(plugin->plugin->status_vars)) + goto err; #endif /* FIX_LATER */ } @@ -1067,9 +1072,13 @@ static int plugin_initialize(struct st_plugin_int *plugin) } } - DBUG_RETURN(0); + ret= 0; + err: - DBUG_RETURN(1); + pthread_mutex_lock(&LOCK_plugin); + plugin->state= state; + + DBUG_RETURN(ret); } diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 004d0d5abb7..3c7da673184 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -27,16 +27,6 @@ class sys_var; #define INITIAL_LEX_PLUGIN_LIST_SIZE 16 -/* - the following #define adds server-only members to enum_mysql_show_type, - that is defined in plugin.h -*/ -#define SHOW_FUNC SHOW_FUNC, SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_LONGLONG, \ - SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_HAVE, \ - SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, SHOW_LONG_NOFLUSH, \ - SHOW_LONGLONG_STATUS -#include <mysql/plugin.h> -#undef SHOW_FUNC typedef enum enum_mysql_show_type SHOW_TYPE; typedef struct st_mysql_show_var SHOW_VAR; diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h new file mode 100644 index 00000000000..7491ddab79d --- /dev/null +++ b/sql/sql_plugin_services.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* support for Services */ +#include <service_versions.h> + +struct st_service_ref { + const char *name; + uint version; + void *service; +}; + +static struct my_snprintf_service_st my_snprintf_handler = { + my_snprintf, + my_vsnprintf +}; + +static struct thd_alloc_service_st thd_alloc_handler= { + thd_alloc, + thd_calloc, + thd_strdup, + thd_strmake, + thd_memdup, + thd_make_lex_string +}; + +static struct st_service_ref list_of_services[]= +{ + { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler }, + { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler } +}; + diff --git a/storage/example/Makefile.am b/storage/example/Makefile.am index 4b2f165377c..10163e307b1 100644 --- a/storage/example/Makefile.am +++ b/storage/example/Makefile.am @@ -34,7 +34,7 @@ noinst_HEADERS = ha_example.h EXTRA_LTLIBRARIES = ha_example.la pkgplugin_LTLIBRARIES = @plugin_example_shared_target@ -ha_example_la_LDFLAGS = -module -rpath $(pkgplugindir) +ha_example_la_LDFLAGS = -module -rpath $(pkgplugindir) -L$(top_builddir)/libservices -lmysqlservices ha_example_la_CXXFLAGS= $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN ha_example_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN ha_example_la_SOURCES = ha_example.cc diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc index 604722c3c8c..b98466635cc 100644 --- a/storage/example/ha_example.cc +++ b/storage/example/ha_example.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 MySQL AB +/* Copyright (C) 2003 MySQL AB, 2009 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -890,6 +890,24 @@ static struct st_mysql_sys_var* example_system_variables[]= { NULL }; +// this is an example of SHOW_FUNC and of my_snprintf() service +static int show_func_example(MYSQL_THD thd, struct st_mysql_show_var *var, + char *buf) +{ + var->type= SHOW_CHAR; + var->value= buf; // it's of SHOW_VAR_FUNC_BUFF_SIZE bytes + my_snprintf(buf, SHOW_VAR_FUNC_BUFF_SIZE, + "enum_var is %u, ulong_var is %lu, %.6b", // %b is MySQL extension + srv_enum_var, srv_ulong_var, "really"); + return 0; +} + +static struct st_mysql_show_var func_status[]= +{ + {"example_func_example", (char *)show_func_example, SHOW_FUNC}, + {0,0,SHOW_UNDEF} +}; + mysql_declare_plugin(example) { MYSQL_STORAGE_ENGINE_PLUGIN, @@ -901,7 +919,7 @@ mysql_declare_plugin(example) example_init_func, /* Plugin Init */ example_done_func, /* Plugin Deinit */ 0x0001 /* 0.1 */, - NULL, /* status variables */ + func_status, /* status variables */ example_system_variables, /* system variables */ NULL /* config options */ } diff --git a/storage/mysql_storage_engine.cmake b/storage/mysql_storage_engine.cmake index af8c3a85cd1..b920f16452b 100644 --- a/storage/mysql_storage_engine.cmake +++ b/storage/mysql_storage_engine.cmake @@ -34,7 +34,7 @@ IF(NOT SOURCE_SUBLIBS) #The dll is linked to the mysqld executable SET(dyn_libname ha_${libname}) ADD_LIBRARY(${dyn_libname} SHARED ${${engine}_SOURCES}) - TARGET_LINK_LIBRARIES (${dyn_libname} mysqld) + TARGET_LINK_LIBRARIES (${dyn_libname} mysqlservices mysqld) IF(${engine}_LIBS) TARGET_LINK_LIBRARIES(${dyn_libname} ${${engine}_LIBS}) ENDIF(${engine}_LIBS) diff --git a/storage/pbxt/src/ha_pbxt.cc b/storage/pbxt/src/ha_pbxt.cc index bd71092d2d9..1bbd21e4331 100644 --- a/storage/pbxt/src/ha_pbxt.cc +++ b/storage/pbxt/src/ha_pbxt.cc @@ -1233,9 +1233,6 @@ static int pbxt_init(void *p) * Only real problem, 2 threads try to load the same * plugin at the same time. */ -#if MYSQL_VERSION_ID < 60014 - myxt_mutex_unlock(&LOCK_plugin); -#endif #endif /* Can't do this here yet, because I need a THD! */ @@ -1269,11 +1266,6 @@ static int pbxt_init(void *p) if (thd) myxt_destroy_thread(thd, FALSE); -#ifndef DRIZZLED -#if MYSQL_VERSION_ID < 60014 - myxt_mutex_lock(&LOCK_plugin); -#endif -#endif } #endif } diff --git a/strings/my_vsnprintf.c b/strings/my_vsnprintf.c index 920022aae91..ad8e7c8c776 100644 --- a/strings/my_vsnprintf.c +++ b/strings/my_vsnprintf.c @@ -18,61 +18,530 @@ #include <stdarg.h> #include <m_ctype.h> -/* - Limited snprintf() implementations - SYNOPSIS - my_vsnprintf() - to Store result here - n Store up to n-1 characters, followed by an end 0 - fmt printf format - ap Arguments - - IMPLEMENTION: - Supports following formats: - %#[l]d - %#[l]u - %#[l]x - %#.#b Local format; note first # is ignored and second is REQUIRED - %#.#s Note first # is ignored +#define MAX_ARGS 32 /* max positional args count*/ +#define MAX_PRINT_INFO 32 /* max print position count */ + +#define LENGTH_ARG 1 +#define WIDTH_ARG 2 +#define PREZERO_ARG 4 +#define ESCAPED_ARG 8 + +typedef struct pos_arg_info ARGS_INFO; +typedef struct print_info PRINT_INFO; + +struct pos_arg_info +{ + char arg_type; /* argument type */ + uint have_longlong; /* used from integer values */ + char *str_arg; /* string value of the arg */ + longlong longlong_arg; /* integer value of the arg */ + double double_arg; /* double value of the arg */ +}; + + +struct print_info +{ + char arg_type; /* argument type */ + size_t arg_idx; /* index of the positional arg */ + size_t length; /* print width or arg index */ + size_t width; /* print width or arg index */ + uint flags; + const char *begin; /**/ + const char *end; /**/ +}; + + +/** + Calculates print length or index of positional argument + + @param fmt processed string + @param length print length or index of positional argument + @param pre_zero returns flags with PREZERO_ARG set if necessary + + @retval + string position right after length digits +*/ + +static const char *get_length(const char *fmt, size_t *length, uint *pre_zero) +{ + for (; my_isdigit(&my_charset_latin1, *fmt); fmt++) + { + *length= *length * 10 + (uint)(*fmt - '0'); + if (!*length) + *pre_zero|= PREZERO_ARG; /* first digit was 0 */ + } + return fmt; +} + + +/** + Calculates print width or index of positional argument + + @param fmt processed string + @param width print width or index of positional argument + + @retval + string position right after width digits +*/ + +static const char *get_width(const char *fmt, size_t *width) +{ + for (; my_isdigit(&my_charset_latin1, *fmt); fmt++) + { + *width= *width * 10 + (uint)(*fmt - '0'); + } + return fmt; +} + +/** + Calculates print width or index of positional argument + + @param fmt processed string + @param have_longlong TRUE if longlong is required + + @retval + string position right after modifier symbol +*/ + +static const char *check_longlong(const char *fmt, uint *have_longlong) +{ + *have_longlong= 0; + if (*fmt == 'l') + { + fmt++; + if (*fmt != 'l') + *have_longlong= (sizeof(long) == sizeof(longlong)); + else + { + fmt++; + *have_longlong= 1; + } + } + else if (*fmt == 'z') + { + fmt++; + *have_longlong= (sizeof(size_t) == sizeof(longlong)); + } + return fmt; +} + + +/** + Returns escaped string + + @param cs string charset + @param to buffer where escaped string will be placed + @param end end of buffer + @param par string to escape + @param par_len string length + @param quote_char character for quoting + + @retval + position in buffer which points on the end of escaped string +*/ + +static char *backtick_string(CHARSET_INFO *cs, char *to, char *end, + char *par, size_t par_len, char quote_char) +{ + uint char_len; + char *start= to; + char *par_end= par + par_len; + size_t buff_length= (size_t) (end - to); + + if (buff_length <= par_len) + goto err; + *start++= quote_char; + + for ( ; par < par_end; par+= char_len) + { + uchar c= *(uchar *) par; + if (!(char_len= my_mbcharlen(cs, c))) + char_len= 1; + if (char_len == 1 && c == (uchar) quote_char ) + { + if (start + 1 >= end) + goto err; + *start++= quote_char; + } + if (start + char_len >= end) + goto err; + start= strnmov(start, par, char_len); + } - RETURN + if (start + 1 >= end) + goto err; + *start++= quote_char; + return start; + +err: + *to='\0'; + return to; +} + + +/** + Prints string argument +*/ + +static char *process_str_arg(CHARSET_INFO *cs, char *to, char *end, + size_t width, char *par, uint print_type) +{ + int well_formed_error; + size_t plen, left_len= (size_t) (end - to) + 1; + if (!par) + par = (char*) "(null)"; + + plen= strnlen(par, width); + if (left_len <= plen) + plen = left_len - 1; + plen= cs->cset->well_formed_len(cs, par, par + plen, + width, &well_formed_error); + if (print_type & ESCAPED_ARG) + to= backtick_string(cs, to, end, par, plen, '`'); + else + to= strnmov(to,par,plen); + return to; +} + + +/** + Prints binary argument +*/ + +static char *process_bin_arg(char *to, char *end, size_t width, char *par) +{ + DBUG_ASSERT(to <= end); + if (to + width + 1 > end) + width= end - to - 1; /* sign doesn't matter */ + memmove(to, par, width); + to+= width; + return to; +} + + +/** + Prints integer argument +*/ + +static char *process_int_arg(char *to, char *end, size_t length, + longlong par, char arg_type, uint print_type) +{ + size_t res_length, to_length; + char *store_start= to, *store_end; + char buff[32]; + + if ((to_length= (size_t) (end-to)) < 16 || length) + store_start= buff; + + if (arg_type == 'd') + store_end= int10_to_str(par, store_start, -10); + else if (arg_type == 'u') + store_end= int10_to_str(par, store_start, 10); + else if (arg_type == 'p') + { + store_start[0]= '0'; + store_start[1]= 'x'; + store_end= int2str(par, store_start + 2, 16, 0); + } + else + { + DBUG_ASSERT(arg_type == 'X' || arg_type =='x'); + store_end= int2str(par, store_start, 16, (arg_type == 'X')); + } + + if ((res_length= (size_t) (store_end - store_start)) > to_length) + return to; /* num doesn't fit in output */ + /* If %#d syntax was used, we have to pre-zero/pre-space the string */ + if (store_start == buff) + { + length= min(length, to_length); + if (res_length < length) + { + size_t diff= (length- res_length); + bfill(to, diff, (print_type & PREZERO_ARG) ? '0' : ' '); + if (arg_type == 'p' && print_type & PREZERO_ARG) + { + if (diff > 1) + to[1]= 'x'; + else + store_start[0]= 'x'; + store_start[1]= '0'; + } + to+= diff; + } + bmove(to, store_start, res_length); + } + to+= res_length; + return to; +} + + +/** + Procesed positional arguments. + + @param cs string charset + @param to buffer where processed string will be place + @param end end of buffer + @param par format string + @param arg_index arg index of the first occurrence of positional arg + @param ap list of parameters + + @retval + end of buffer where processed string is placed +*/ + +static char *process_args(CHARSET_INFO *cs, char *to, char *end, + const char* fmt, size_t arg_index, va_list ap) +{ + ARGS_INFO args_arr[MAX_ARGS]; + PRINT_INFO print_arr[MAX_PRINT_INFO]; + uint idx= 0, arg_count= arg_index; + +start: + /* Here we are at the beginning of positional argument, right after $ */ + arg_index--; + print_arr[idx].flags= 0; + if (*fmt == '`') + { + print_arr[idx].flags|= ESCAPED_ARG; + fmt++; + } + if (*fmt == '-') + fmt++; + print_arr[idx].length= print_arr[idx].width= 0; + /* Get print length */ + if (*fmt == '*') + { + fmt++; + fmt= get_length(fmt, &print_arr[idx].length, &print_arr[idx].flags); + print_arr[idx].length--; + DBUG_ASSERT(*fmt == '$' && print_arr[idx].length < MAX_ARGS); + args_arr[print_arr[idx].length].arg_type= 'd'; + print_arr[idx].flags|= LENGTH_ARG; + arg_count= max(arg_count, print_arr[idx].length + 1); + fmt++; + } + else + fmt= get_length(fmt, &print_arr[idx].length, &print_arr[idx].flags); + + if (*fmt == '.') + { + fmt++; + /* Get print width */ + if (*fmt == '*') + { + fmt++; + fmt= get_width(fmt, &print_arr[idx].width); + print_arr[idx].width--; + DBUG_ASSERT(*fmt == '$' && print_arr[idx].width < MAX_ARGS); + args_arr[print_arr[idx].width].arg_type= 'd'; + print_arr[idx].flags|= WIDTH_ARG; + arg_count= max(arg_count, print_arr[idx].width + 1); + fmt++; + } + else + fmt= get_width(fmt, &print_arr[idx].width); + } + else + print_arr[idx].width= SIZE_T_MAX; + + fmt= check_longlong(fmt, &args_arr[arg_index].have_longlong); + if (*fmt == 'p') + args_arr[arg_index].have_longlong= (sizeof(void *) == sizeof(longlong)); + args_arr[arg_index].arg_type= print_arr[idx].arg_type= *fmt; + + print_arr[idx].arg_idx= arg_index; + print_arr[idx].begin= ++fmt; + + while (*fmt && *fmt != '%') + fmt++; + + if (!*fmt) /* End of format string */ + { + uint i; + print_arr[idx].end= fmt; + /* Obtain parameters from the list */ + for (i= 0 ; i < arg_count; i++) + { + switch (args_arr[i].arg_type) { + case 's': + case 'b': + args_arr[i].str_arg= va_arg(ap, char *); + break; + case 'f': + case 'g': + args_arr[i].double_arg= va_arg(ap, double); + break; + case 'd': + case 'u': + case 'x': + case 'X': + case 'p': + if (args_arr[i].have_longlong) + args_arr[i].longlong_arg= va_arg(ap,longlong); + else if (args_arr[i].arg_type == 'd') + args_arr[i].longlong_arg= va_arg(ap, int); + else + args_arr[i].longlong_arg= va_arg(ap, uint); + break; + case 'c': + args_arr[i].longlong_arg= va_arg(ap, int); + break; + default: + DBUG_ASSERT(0); + } + } + /* Print result string */ + for (i= 0; i <= idx; i++) + { + uint width= 0, length= 0; + switch (print_arr[i].arg_type) { + case 's': + { + char *par= args_arr[print_arr[i].arg_idx].str_arg; + width= (print_arr[i].flags & WIDTH_ARG) ? + args_arr[print_arr[i].width].longlong_arg : print_arr[i].width; + to= process_str_arg(cs, to, end, width, par, print_arr[i].flags); + break; + } + case 'b': + { + char *par = args_arr[print_arr[i].arg_idx].str_arg; + width= (print_arr[i].flags & WIDTH_ARG) ? + args_arr[print_arr[i].width].longlong_arg : print_arr[i].width; + to= process_bin_arg(to, end, width, par); + break; + } + case 'c': + { + if (to == end) + break; + *to++= (char) args_arr[print_arr[i].arg_idx].longlong_arg; + break; + } + case 'd': + case 'u': + case 'x': + case 'X': + case 'p': + { + /* Integer parameter */ + longlong larg; + length= (print_arr[i].flags & LENGTH_ARG) ? + args_arr[print_arr[i].length].longlong_arg : print_arr[i].length; + + if (args_arr[print_arr[i].arg_idx].have_longlong) + larg = args_arr[print_arr[i].arg_idx].longlong_arg; + else if (print_arr[i].arg_type == 'd') + larg = (int) args_arr[print_arr[i].arg_idx].longlong_arg; + else + larg= (uint) args_arr[print_arr[i].arg_idx].longlong_arg; + + to= process_int_arg(to, end, length, larg, print_arr[i].arg_type, + print_arr[i].flags); + break; + } + default: + break; + } + + if (to == end) + break; + + length= min(end - to , print_arr[i].end - print_arr[i].begin); + if (to + length < end) + length++; + to= strnmov(to, print_arr[i].begin, length); + } + DBUG_ASSERT(to <= end); + *to='\0'; /* End of errmessage */ + return to; + } + else + { + /* Process next positional argument*/ + DBUG_ASSERT(*fmt == '%'); + print_arr[idx].end= fmt - 1; + idx++; + fmt++; + arg_index= 0; + fmt= get_width(fmt, &arg_index); + DBUG_ASSERT(*fmt == '$'); + fmt++; + arg_count= max(arg_count, arg_index); + goto start; + } + DBUG_ASSERT(0); + return 0; +} + + + +/** + Produces output string according to a format string + + See the detailed documentation around my_snprintf_service_st + + @param cs string charset + @param to buffer where processed string will be place + @param n size of buffer + @param par format string + @param ap list of parameters + + @retval length of result string */ -size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) +size_t my_vsnprintf_ex(CHARSET_INFO *cs, char *to, size_t n, + const char* fmt, va_list ap) { char *start=to, *end=to+n-1; size_t length, width; - uint pre_zero, have_long; + uint print_type, have_longlong; for (; *fmt ; fmt++) { if (*fmt != '%') { - if (to == end) /* End of buffer */ + if (to == end) /* End of buffer */ break; - *to++= *fmt; /* Copy ordinary char */ + *to++= *fmt; /* Copy ordinary char */ continue; } fmt++; /* skip '%' */ - /* Read max fill size (only used with %d and %u) */ - if (*fmt == '-') - fmt++; + length= width= 0; - pre_zero= have_long= 0; - if (*fmt == '*') + print_type= 0; + + /* Read max fill size (only used with %d and %u) */ + if (my_isdigit(&my_charset_latin1, *fmt)) { - fmt++; - length= va_arg(ap, int); + fmt= get_length(fmt, &length, &print_type); + if (*fmt == '$') + { + to= process_args(cs, to, end, (fmt+1), length, ap); + return (size_t) (to - start); + } } else - for (; my_isdigit(&my_charset_latin1, *fmt); fmt++) + { + if (*fmt == '`') + { + print_type|= ESCAPED_ARG; + fmt++; + } + if (*fmt == '-') + fmt++; + if (*fmt == '*') { - length= length * 10 + (uint)(*fmt - '0'); - if (!length) - pre_zero= 1; /* first digit was 0 */ + fmt++; + length= va_arg(ap, int); } + else + fmt= get_length(fmt, &length, &print_type); + } + if (*fmt == '.') { fmt++; @@ -82,75 +551,41 @@ size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) width= va_arg(ap, int); } else - for (; my_isdigit(&my_charset_latin1, *fmt); fmt++) - width= width * 10 + (uint)(*fmt - '0'); + fmt= get_width(fmt, &width); } else - width= ~0; - if (*fmt == 'l') - { - fmt++; - have_long= 1; - } + width= SIZE_T_MAX; + + fmt= check_longlong(fmt, &have_longlong); + if (*fmt == 's') /* String parameter */ { - reg2 char *par = va_arg(ap, char *); - size_t plen,left_len = (size_t) (end - to) + 1; - if (!par) par = (char*)"(null)"; - plen= (uint) strnlen(par, width); - if (left_len <= plen) - plen = left_len - 1; - to=strnmov(to,par,plen); + reg2 char *par= va_arg(ap, char *); + to= process_str_arg(cs, to, end, width, par, print_type); continue; } else if (*fmt == 'b') /* Buffer parameter */ { char *par = va_arg(ap, char *); - DBUG_ASSERT(to <= end); - if (to + abs(width) + 1 > end) - width= (uint) (end - to - 1); /* sign doesn't matter */ - memmove(to, par, abs(width)); - to+= width; + to= process_bin_arg(to, end, width, par); continue; } - else if (*fmt == 'd' || *fmt == 'u'|| *fmt== 'x') /* Integer parameter */ + else if (*fmt == 'd' || *fmt == 'u' || *fmt == 'x' || *fmt == 'X' || + *fmt == 'p') { - register long larg; - size_t res_length, to_length; - char *store_start= to, *store_end; - char buff[32]; - - if ((to_length= (size_t) (end-to)) < 16 || length) - store_start= buff; - if (have_long) - larg = va_arg(ap, long); - else - if (*fmt == 'd') - larg = va_arg(ap, int); - else - larg= (long) (uint) va_arg(ap, int); - if (*fmt == 'd') - store_end= int10_to_str(larg, store_start, -10); + /* Integer parameter */ + longlong larg; + if (*fmt == 'p') + have_longlong= (sizeof(void *) == sizeof(longlong)); + + if (have_longlong) + larg = va_arg(ap,longlong); + else if (*fmt == 'd') + larg = va_arg(ap, int); else - if (*fmt== 'u') - store_end= int10_to_str(larg, store_start, 10); - else - store_end= int2str(larg, store_start, 16, 0); - if ((res_length= (size_t) (store_end - store_start)) > to_length) - break; /* num doesn't fit in output */ - /* If %#d syntax was used, we have to pre-zero/pre-space the string */ - if (store_start == buff) - { - length= min(length, to_length); - if (res_length < length) - { - size_t diff= (length- res_length); - bfill(to, diff, pre_zero ? '0' : ' '); - to+= diff; - } - bmove(to, store_start, res_length); - } - to+= res_length; + larg= va_arg(ap, uint); + + to= process_int_arg(to, end, length, larg, *fmt, print_type); continue; } else if (*fmt == 'c') /* Character parameter */ @@ -174,6 +609,19 @@ size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) } +/* + Limited snprintf() implementations + + exported to plugins as a service, see the detailed documentation + around my_snprintf_service_st +*/ + +size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) +{ + return my_vsnprintf_ex(&my_charset_latin1, to, n, fmt, ap); +} + + size_t my_snprintf(char* to, size_t n, const char* fmt, ...) { size_t result; @@ -184,42 +632,3 @@ size_t my_snprintf(char* to, size_t n, const char* fmt, ...) return result; } -#ifdef MAIN -#define OVERRUN_SENTRY 250 -static void my_printf(const char * fmt, ...) -{ - char buf[33]; - int n; - va_list ar; - va_start(ar, fmt); - buf[sizeof(buf)-1]=OVERRUN_SENTRY; - n = my_vsnprintf(buf, sizeof(buf)-1,fmt, ar); - printf(buf); - printf("n=%d, strlen=%d\n", n, strlen(buf)); - if ((uchar) buf[sizeof(buf)-1] != OVERRUN_SENTRY) - { - fprintf(stderr, "Buffer overrun\n"); - abort(); - } - va_end(ar); -} - - -int main() -{ - - my_printf("Hello\n"); - my_printf("Hello int, %d\n", 1); - my_printf("Hello string '%s'\n", "I am a string"); - my_printf("Hello hack hack hack hack hack hack hack %d\n", 1); - my_printf("Hello %d hack %d\n", 1, 4); - my_printf("Hello %d hack hack hack hack hack %d\n", 1, 4); - my_printf("Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n", "hack"); - my_printf("Hello hhhhhhhhhhhhhh %d sssssssssssssss\n", 1); - my_printf("Hello %u\n", 1); - my_printf("Hex: %lx '%6lx'\n", 32, 65); - my_printf("conn %ld to: '%-.64s' user: '%-.32s' host:\ - `%-.64s' (%-.64s)", 1, 0,0,0,0); - return 0; -} -#endif diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index 115eeb8df7a..e9ec6210e20 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -24,7 +24,8 @@ LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ $(top_builddir)/strings/libmystrings.a EXTRA_DIST = CMakeLists.txt -noinst_PROGRAMS = bitmap-t base64-t my_atomic-t lf-t waiting_threads-t +noinst_PROGRAMS = bitmap-t base64-t my_atomic-t lf-t waiting_threads-t \ + my_vsnprintf-t if NEED_THREAD # my_atomic-t is used to check thread functions, so it is safe to diff --git a/unittest/mysys/my_vsnprintf-t.c b/unittest/mysys/my_vsnprintf-t.c new file mode 100644 index 00000000000..f3a6b5700da --- /dev/null +++ b/unittest/mysys/my_vsnprintf-t.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <m_string.h> +#include <tap.h> + +char buf[1024]; /* let's hope that's enough */ + +void test1(const char *res, const char *fmt, ...) +{ + va_list args; + size_t len; + va_start(args,fmt); + len= my_vsnprintf(buf, sizeof(buf)-1, fmt, args); + va_end(args); + ok(strlen(res) == len && strcmp(buf, res) == 0, "\"%s\"", buf); +} + +int main(void) +{ + plan(47); + + test1("Constant string", + "Constant string"); + + test1("Format specifier s works", + "Format specifier s %s", "works"); + test1("Format specifier b works (mysql extension)", + "Format specifier b %.5b (mysql extension)", "works!!!"); + test1("Format specifier c !", + "Format specifier c %c", '!'); + test1("Format specifier d 1", + "Format specifier d %d", 1); + test1("Format specifier u 2", + "Format specifier u %u", 2); + test1("Format specifier x a", + "Format specifier x %x", 10); + test1("Format specifier X B", + "Format specifier X %X", 11); + test1("Format specifier p 0x5", + "Format specifier p %p", 5); + + test1("Flag '-' is ignored < 1>", + "Flag '-' is ignored <%-4d>", 1); + test1("Flag '0' works <0006>", + "Flag '0' works <%04d>", 6); + + test1("Width is ignored for strings <x> <y>", + "Width is ignored for strings <%04s> <%5s>", "x", "y"); + + test1("Precision works for strings <abcde>", + "Precision works for strings <%.5s>", "abcdef!"); + + test1("Flag '`' (backtick) works: `abcd` `op``q` (mysql extension)", + "Flag '`' (backtick) works: %`s %`.4s (mysql extension)", + "abcd", "op`qrst"); + + test1("Length modifiers work: 1 * -1 * 2 * 3", + "Length modifiers work: %d * %ld * %lld * %zd", 1, -1L, 2LL, (size_t)3); + + test1("(null) pointer is fine", + "%s pointer is fine", NULL); + + test1("Positional arguments work: on the dark side they are", + "Positional arguments work: %3$s %1$s %2$s", + "they", "are", "on the dark side"); + + test1("Asterisk '*' as a width works: < 4>", + "Asterisk '*' as a width works: <%*d>", 5, 4); + + test1("Asterisk '*' as a precision works: <qwerty>", + "Asterisk '*' as a precision works: <%.*s>", 6, "qwertyuiop"); + + test1("Positional arguments for a width: < 4>", + "Positional arguments for a width: <%1$*2$d>", 4, 5); + + test1("Positional arguments for a precision: <qwerty>", + "Positional arguments for a precision: <%1$.*2$s>", "qwertyuiop", 6); + + test1("Positional arguments and a width: <0000ab>", + "Positional arguments and a width: <%1$06x>", 0xab); + + test1("Padding and %p <0x12> <0x034> <0x0000ab> < 0xcd>", + "Padding and %%p <%04p> <%05p> <%08p> <%8p>", 0x12, 0x34, 0xab, 0xcd); + +#if MYSQL_VERSION_ID > 60000 +#error %f/%g tests go here +#endif + + test1("Hello", + "Hello"); + test1("Hello int, 1", + "Hello int, %d", 1); + test1("Hello int, -1", + "Hello int, %d", -1); + test1("Hello string 'I am a string'", + "Hello string '%s'", "I am a string"); + test1("Hello hack hack hack hack hack hack hack 1", + "Hello hack hack hack hack hack hack hack %d", 1); + test1("Hello 1 hack 4", + "Hello %d hack %d", 1, 4); + test1("Hello 1 hack hack hack hack hack 4", + "Hello %d hack hack hack hack hack %d", 1, 4); + test1("Hello 'hack' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", + "Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", "hack"); + test1("Hello hhhhhhhhhhhhhh 1 sssssssssssssss", + "Hello hhhhhhhhhhhhhh %d sssssssssssssss", 1); + test1("Hello 1", + "Hello %u", 1); + test1("Hello 4294967295", + "Hello %u", -1); + test1("Hex: 20 ' 41'", + "Hex: %lx '%6lx'", 32, 65); + test1("conn 1 to: '(null)' user: '(null)' host: '(null)' ((null))", + "conn %ld to: '%-.64s' user: '%-.32s' host: '%-.64s' (%-.64s)", + 1L, NULL, NULL, NULL, NULL); + test1("Hello string `I am a string`", + "Hello string %`s", "I am a string"); + test1("Hello TEST", + "Hello %05s", "TEST"); + test1("My `Q` test", + "My %1$`-.1s test", "QQQQ"); + test1("My AAAA test done DDDD", + "My %2$s test done %1$s", "DDDD", "AAAA"); + test1("My DDDD test CCCC, DDD", + "My %1$s test %2$s, %1$-.3s", "DDDD", "CCCC"); + test1("My QQQQ test", + "My %1$`-.4b test", "QQQQ"); + test1("My X test", + "My %1$c test", 'X'); + test1("My <0000000010> test1 < a> test2 < A>", + "My <%010d> test1 <%4x> test2 <%4X>", 10, 10, 10); + test1("My <0000000010> test1 < a> test2 < a>", + "My <%1$010d> test1 <%2$4x> test2 <%2$4x>", 10, 10); + test1("My 00010 test", + "My %1$*02$d test", 10, 5); + test1("My `DDDD` test CCCC, `DDD`", + "My %1$`s test %2$s, %1$`-.3s", "DDDD", "CCCC"); + + return exit_status(); +} + |