mwl:98 - libservices
This commit is contained in:
parent
abb87914ec
commit
2ceaffc467
@ -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)
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
98
include/mysql/service_my_snprintf.h
Normal file
98
include/mysql/service_my_snprintf.h
Normal file
@ -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
|
||||
|
128
include/mysql/service_thd_alloc.h
Normal file
128
include/mysql/service_thd_alloc.h
Normal file
@ -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
|
||||
|
30
include/mysql/services.h
Normal file
30
include/mysql/services.h
Normal file
@ -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
|
||||
|
24
include/service_versions.h
Normal file
24
include/service_versions.h
Normal file
@ -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
|
||||
|
20
libservices/CMakeLists.txt
Normal file
20
libservices/CMakeLists.txt
Normal file
@ -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})
|
100
libservices/HOWTO
Normal file
100
libservices/HOWTO
Normal file
@ -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.
|
||||
|
19
libservices/Makefile.am
Normal file
19
libservices/Makefile.am
Normal file
@ -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
|
17
libservices/my_snprintf_service.c
Normal file
17
libservices/my_snprintf_service.c
Normal file
@ -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;
|
17
libservices/thd_alloc_service.c
Normal file
17
libservices/thd_alloc_service.c
Normal file
@ -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;
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
44
sql/sql_plugin_services.h
Normal file
44
sql/sql_plugin_services.h
Normal file
@ -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 }
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
#define MAX_ARGS 32 /* max positional args count*/
|
||||
#define MAX_PRINT_INFO 32 /* max print position count */
|
||||
|
||||
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 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 == '`')
|
||||
{
|
||||
length= length * 10 + (uint)(*fmt - '0');
|
||||
if (!length)
|
||||
pre_zero= 1; /* first digit was 0 */
|
||||
print_type|= ESCAPED_ARG;
|
||||
fmt++;
|
||||
}
|
||||
if (*fmt == '-')
|
||||
fmt++;
|
||||
if (*fmt == '*')
|
||||
{
|
||||
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];
|
||||
/* Integer parameter */
|
||||
longlong larg;
|
||||
if (*fmt == 'p')
|
||||
have_longlong= (sizeof(void *) == sizeof(longlong));
|
||||
|
||||
if ((to_length= (size_t) (end-to)) < 16 || length)
|
||||
store_start= buff;
|
||||
if (have_long)
|
||||
larg = va_arg(ap, long);
|
||||
if (have_longlong)
|
||||
larg = va_arg(ap,longlong);
|
||||
else if (*fmt == 'd')
|
||||
larg = va_arg(ap, int);
|
||||
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);
|
||||
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
|
||||
|
@ -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
|
||||
|
155
unittest/mysys/my_vsnprintf-t.c
Normal file
155
unittest/mysys/my_vsnprintf-t.c
Normal file
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user