summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2010-03-03 15:44:14 +0100
committerSergei Golubchik <sergii@pisem.net>2010-03-03 15:44:14 +0100
commit2ceaffc4672c62a772890da7761d11f13d3f5ee4 (patch)
treea88f704359d35209d76f0dbb52fe18f7b259e175
parentabb87914ecb4caa1becce4fc4d30c110a6b2c041 (diff)
downloadmariadb-git-2ceaffc4672c62a772890da7761d11f13d3f5ee4.tar.gz
mwl:98 - libservices
-rwxr-xr-xCMakeLists.txt1
-rw-r--r--Makefile.am4
-rw-r--r--configure.in2
-rw-r--r--include/Makefile.am5
-rw-r--r--include/m_string.h10
-rw-r--r--include/mysql/plugin.h65
-rw-r--r--include/mysql/plugin.h.pp42
-rw-r--r--include/mysql/service_my_snprintf.h98
-rw-r--r--include/mysql/service_thd_alloc.h128
-rw-r--r--include/mysql/services.h30
-rw-r--r--include/service_versions.h24
-rw-r--r--libservices/CMakeLists.txt20
-rw-r--r--libservices/HOWTO100
-rw-r--r--libservices/Makefile.am19
-rw-r--r--libservices/my_snprintf_service.c17
-rw-r--r--libservices/thd_alloc_service.c17
-rw-r--r--mysql-test/r/plugin.result9
-rw-r--r--mysql-test/t/plugin.test6
-rw-r--r--plugin/daemon_example/Makefile.am3
-rwxr-xr-xsql/CMakeLists.txt2
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/mysql_priv.h10
-rw-r--r--sql/mysqld.cc17
-rw-r--r--sql/sql_plugin.cc137
-rw-r--r--sql/sql_plugin.h10
-rw-r--r--sql/sql_plugin_services.h44
-rw-r--r--storage/example/Makefile.am2
-rw-r--r--storage/example/ha_example.cc22
-rw-r--r--storage/mysql_storage_engine.cmake2
-rw-r--r--storage/pbxt/src/ha_pbxt.cc8
-rw-r--r--strings/my_vsnprintf.c663
-rw-r--r--unittest/mysys/Makefile.am3
-rw-r--r--unittest/mysys/my_vsnprintf-t.c155
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();
+}
+