1
/*****************************************************************************
3
Copyright (c) 2008, 2009, Innobase Oy. All Rights Reserved.
5
This program is free software; you can redistribute it and/or modify it under
6
the terms of the GNU General Public License as published by the Free Software
7
Foundation; version 2 of the License.
9
This program is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15
Place, Suite 330, Boston, MA 02111-1307 USA
17
*****************************************************************************/
19
/*******************************************************************//**
20
@file handler/win_delay_loader.cc
21
This file contains functions that implement the delay loader on Windows.
23
This is a customized version of delay loader with limited functionalities.
27
* multiple delay loaded DLLs
28
* multiple loading of the same DLL
30
This delay loader is used only by the InnoDB plugin. Other components (DLLs)
31
can still use the default delay loader, provided by MSVC.
33
Several acronyms used by Microsoft:
34
* IAT: import address table
35
* INT: import name table
36
* RVA: Relative Virtual Address
38
See http://msdn.microsoft.com/en-us/magazine/bb985992.aspx for details of
40
***********************************************************************/
41
#if defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN)
42
# define WIN32_LEAN_AND_MEAN
44
# include <delayimp.h>
45
# include <mysql_priv.h>
49
# include "hash0hash.h"
52
/*******************************************************************//**
53
This following contains a list of externals that can not be resolved by
54
delay loading. They have to be resolved indirectly via their addresses
55
in the .map file. All of them are external variables. */
56
CHARSET_INFO* wdl_my_charset_bin;
57
CHARSET_INFO* wdl_my_charset_latin1;
58
CHARSET_INFO* wdl_my_charset_filename;
59
CHARSET_INFO** wdl_system_charset_info;
60
CHARSET_INFO** wdl_default_charset_info;
61
CHARSET_INFO** wdl_all_charsets;
62
system_variables* wdl_global_system_variables;
63
char* wdl_mysql_real_data_home;
64
char** wdl_mysql_data_home;
65
char** wdl_tx_isolation_names;
67
pthread_mutex_t* wdl_LOCK_thread_count;
68
key_map* wdl_key_map_full;
69
MY_TMPDIR* wdl_mysql_tmpdir_list;
70
bool* wdl_mysqld_embedded;
71
uint* wdl_lower_case_table_names;
72
ulong* wdl_specialflag;
75
/*******************************************************************//**
76
The preferred load-address defined in PE (portable executable format). */
78
#pragma section(".base", long, read)
80
__declspec(allocate(".base"))
81
const IMAGE_DOS_HEADER __ImageBase;
84
const IMAGE_DOS_HEADER __ImageBase;
87
/*******************************************************************//**
88
A template function for converting a relative address (RVA) to an
89
absolute address (VA). This is due to the pointers in the delay
90
descriptor (ImgDelayDescr in delayimp.h) have been changed from
91
VAs to RVAs to work on both 32- and 64-bit platforms.
92
@return absolute virtual address */
96
RVA rva) /*!< in: relative virtual address */
98
return X(PBYTE(&__ImageBase) + rva);
101
/*******************************************************************//**
102
Convert to the old format for convenience. The structure as well as its
103
element names follow the definition of ImgDelayDescr in delayimp.h. */
104
struct InternalImgDelayDescr
106
DWORD grAttrs; /*!< attributes */
107
LPCSTR szName; /*!< pointer to dll name */
108
HMODULE* phmod; /*!< address of module handle */
109
PImgThunkData pIAT; /*!< address of the IAT */
110
PCImgThunkData pINT; /*!< address of the INT */
111
PCImgThunkData pBoundIAT; /*!< address of the optional bound IAT */
112
PCImgThunkData pUnloadIAT; /*!< address of optional copy of
114
DWORD dwTimeStamp; /*!< 0 if not bound,
115
otherwise date/time stamp of DLL
116
bound to (Old BIND) */
119
typedef struct map_hash_chain_struct map_hash_chain_t;
121
struct map_hash_chain_struct {
122
char* symbol; /*!< pointer to a symbol */
123
ulint value; /*!< address of the symbol */
124
map_hash_chain_t* next; /*!< pointer to the next cell
125
in the same folder. */
126
map_hash_chain_t* chain; /*!< a linear chain used for
130
static HMODULE my_hmod = 0;
131
static struct hash_table_struct* m_htbl = NULL ;
132
static map_hash_chain_t* chain_header = NULL;
133
static ibool wdl_init = FALSE;
134
const ulint MAP_HASH_CELLS_NUM = 10000;
137
/*******************************************************************//**
138
In the dynamic plugin, it is required to call the following dbug functions
146
The plugin will get those function pointers during the initialization. */
147
typedef void (__cdecl* pfn_db_enter_)(
151
const char** _sfunc_,
152
const char** _sfile_,
156
typedef void (__cdecl* pfn_db_return_)(
158
const char** _sfunc_,
159
const char** _sfile_,
162
typedef void (__cdecl* pfn_db_pargs_)(
164
const char* keyword);
166
typedef void (__cdecl* pfn_db_doprnt_)(
170
typedef void (__cdecl* pfn_db_dump_)(
173
const unsigned char* memory,
176
static pfn_db_enter_ wdl_db_enter_;
177
static pfn_db_return_ wdl_db_return_;
178
static pfn_db_pargs_ wdl_db_pargs_;
179
static pfn_db_doprnt_ wdl_db_doprnt_;
180
static pfn_db_dump_ wdl_db_dump_;
181
#endif /* !DBUG_OFF */
183
/*************************************************************//**
184
Creates a hash table with >= n array cells. The actual number of cells is
185
chosen to be a prime number slightly bigger than n.
187
This is the same function as hash_create in hash0hash.c, except the
188
memory allocation. This function is invoked before the engine is
189
initialized, and buffer pools are not ready yet.
190
@return own: created hash table */
195
ulint n) /*!< in: number of array cells */
201
prime = ut_find_prime(n);
203
table = (hash_table_t*) malloc(sizeof(hash_table_t));
208
array = (hash_cell_t*) malloc(sizeof(hash_cell_t) * prime);
214
table->array = array;
215
table->n_cells = prime;
216
table->n_mutexes = 0;
217
table->mutexes = NULL;
220
table->magic_n = HASH_TABLE_MAGIC_N;
222
/* Initialize the cell array */
223
hash_table_clear(table);
228
/*************************************************************//**
229
Frees a hash table. */
234
hash_table_t* table) /*!< in, own: hash table */
237
ut_a(table->mutexes == NULL);
243
/*******************************************************************//**
244
Function for calculating the count of imports given the base of the IAT.
245
@return number of imports */
250
PCImgThunkData pitd_base) /*!< in: base of the IAT */
253
PCImgThunkData pitd = pitd_base;
255
while (pitd->u1.Function) {
263
/*******************************************************************//**
264
Read Mapfile to a hashtable for faster access
265
@return TRUE if the mapfile is loaded successfully. */
270
const char* filename) /*!< in: name of the mapfile. */
273
const size_t nSize = 256;
278
ibool valid_load_addr = FALSE;
280
const char* tmp_string = " Preferred load address is %16llx";
282
const char* tmp_string = " Preferred load address is %08x";
285
fp = fopen(filename, "r");
291
/* Check whether to create the hashtable */
292
if (m_htbl == NULL) {
294
m_htbl = wdl_hash_create(MAP_HASH_CELLS_NUM);
296
if (m_htbl == NULL) {
303
/* Search start of symbol list and get the preferred load address */
304
while (fgets(tmp_buf, sizeof(tmp_buf), fp)) {
306
if (sscanf(tmp_buf, tmp_string, &load_addr) == 1) {
308
valid_load_addr = TRUE;
311
if (strstr(tmp_buf, "Rva+Base") != NULL) {
317
if (valid_load_addr == FALSE) {
319
/* No "Preferred load address", the map file is wrong. */
324
/* Read symbol list */
325
while (fgets(tmp_buf, sizeof(tmp_buf), fp))
327
map_hash_chain_t* map_cell;
335
func_name = strtok(tmp_buf, " ");
336
func_name = strtok(NULL, " ");
337
func_addr = strtok(NULL, " ");
339
if (func_name && func_addr) {
341
ut_snprintf(tmp_buf, nSize, "0x%s", func_addr);
342
if (*func_name == '_') {
347
map_cell = (map_hash_chain_t*)
348
malloc(sizeof(map_hash_chain_t));
349
if (map_cell == NULL) {
353
/* Chain all cells together */
354
map_cell->chain = chain_header;
355
chain_header = map_cell;
357
map_cell->symbol = strdup(func_name);
358
map_cell->value = (ulint) _strtoui64(tmp_buf, NULL, 0)
360
map_fold = ut_fold_string(map_cell->symbol);
362
HASH_INSERT(map_hash_chain_t,
375
/*************************************************************//**
376
Cleanup.during DLL unload */
382
while (chain_header != NULL) {
383
map_hash_chain_t* tmp;
385
tmp = chain_header->chain;
386
free(chain_header->symbol);
391
if (m_htbl != NULL) {
393
wdl_hash_table_free(m_htbl);
397
/*******************************************************************//**
398
Load the mapfile mysqld.map.
399
@return the module handle */
402
wdl_get_mysqld_mapfile(void)
403
/*========================*/
405
char file_name[MAX_PATH];
411
size_t nSize = MAX_PATH - strlen(".map") -1;
413
/* First find out the name of current executable */
414
my_hmod = GetModuleHandle(NULL);
420
err = GetModuleFileName(my_hmod, file_name, nSize);
427
ext = strrchr(file_name, '.');
431
strcat(file_name, ".map");
433
err = wdl_load_mapfile(file_name);
447
/*******************************************************************//**
448
Retrieves the address of an exported function. It follows the convention
450
@return address of exported function. */
453
wdl_get_procaddr_from_map(
454
/*======================*/
455
HANDLE m_handle, /*!< in: module handle */
456
const char* import_proc) /*!< in: procedure name */
458
map_hash_chain_t* hash_chain;
461
map_fold = ut_fold_string(import_proc);
469
(ut_strcmp(hash_chain->symbol, import_proc) == 0));
471
if (hash_chain == NULL) {
474
/* On Win64, the leading '_' may not be taken out. In this
475
case, search again without the leading '_'. */
476
if (*import_proc == '_') {
481
map_fold = ut_fold_string(import_proc);
489
(ut_strcmp(hash_chain->symbol, import_proc) == 0));
491
if (hash_chain == NULL) {
493
if (wdl_init == TRUE) {
495
errmsg_printf(ERRMSG_LVL_ERROR,
496
"InnoDB: the procedure pointer of %s"
507
return((FARPROC) ((ulint) m_handle + hash_chain->value));
510
/*******************************************************************//**
511
Retrieves the address of an exported variable.
512
Note: It does not follow the Windows call convention FARPROC.
513
@return address of exported variable. */
516
wdl_get_varaddr_from_map(
517
/*=====================*/
518
HANDLE m_handle, /*!< in: module handle */
519
const char* import_variable) /*!< in: variable name */
521
map_hash_chain_t* hash_chain;
524
map_fold = ut_fold_string(import_variable);
532
(ut_strcmp(hash_chain->symbol, import_variable) == 0));
534
if (hash_chain == NULL) {
537
/* On Win64, the leading '_' may not be taken out. In this
538
case, search again without the leading '_'. */
539
if (*import_variable == '_') {
544
map_fold = ut_fold_string(import_variable);
552
(ut_strcmp(hash_chain->symbol, import_variable) == 0));
554
if (hash_chain == NULL) {
556
if (wdl_init == TRUE) {
558
errmsg_printf(ERRMSG_LVL_ERROR,
559
"InnoDB: the variable address of %s"
570
return((void*) ((ulint) m_handle + hash_chain->value));
573
/*******************************************************************//**
574
Bind all unresolved external variables from the MySQL executable.
575
@return TRUE if successful */
578
wdl_get_external_variables(void)
579
/*============================*/
581
HMODULE hmod = wdl_get_mysqld_mapfile();
588
#define GET_SYM(sym, var, type) \
589
var = (type*) wdl_get_varaddr_from_map(hmod, sym); \
590
if (var == NULL) return(FALSE)
592
#define GET_SYM2(sym1, sym2, var, type) \
593
var = (type*) wdl_get_varaddr_from_map(hmod, sym1); \
594
if (var == NULL) return(FALSE)
596
#define GET_SYM2(sym1, sym2, var, type) \
597
var = (type*) wdl_get_varaddr_from_map(hmod, sym2); \
598
if (var == NULL) return(FALSE)
600
#define GET_C_SYM(sym, type) GET_SYM(#sym, wdl_##sym, type)
601
#define GET_PROC_ADDR(sym) \
602
wdl##sym = (pfn##sym) wdl_get_procaddr_from_map(hmod, #sym)
604
GET_C_SYM(my_charset_bin, CHARSET_INFO);
605
GET_C_SYM(my_charset_latin1, CHARSET_INFO);
606
GET_C_SYM(my_charset_filename, CHARSET_INFO);
607
GET_C_SYM(default_charset_info, CHARSET_INFO*);
608
GET_C_SYM(all_charsets, CHARSET_INFO*);
609
GET_C_SYM(my_umask, int);
611
GET_SYM("?global_system_variables@@3Usystem_variables@@A",
612
wdl_global_system_variables, struct system_variables);
613
GET_SYM("?mysql_real_data_home@@3PADA",
614
wdl_mysql_real_data_home, char);
615
GET_SYM("?reg_ext@@3PADA", wdl_reg_ext, char);
616
GET_SYM("?LOCK_thread_count@@3U_RTL_CRITICAL_SECTION@@A",
617
wdl_LOCK_thread_count, pthread_mutex_t);
618
GET_SYM("?key_map_full@@3V?$Bitmap@$0EA@@@A",
619
wdl_key_map_full, key_map);
620
GET_SYM("?mysql_tmpdir_list@@3Ust_my_tmpdir@@A",
621
wdl_mysql_tmpdir_list, MY_TMPDIR);
622
GET_SYM("?mysqld_embedded@@3_NA",
623
wdl_mysqld_embedded, bool);
624
GET_SYM("?lower_case_table_names@@3IA",
625
wdl_lower_case_table_names, uint);
626
GET_SYM("?specialflag@@3KA", wdl_specialflag, ulong);
628
GET_SYM2("?system_charset_info@@3PEAUcharset_info_st@@EA",
629
"?system_charset_info@@3PAUcharset_info_st@@A",
630
wdl_system_charset_info, CHARSET_INFO*);
631
GET_SYM2("?mysql_data_home@@3PEADEA",
632
"?mysql_data_home@@3PADA",
633
wdl_mysql_data_home, char*);
634
GET_SYM2("?tx_isolation_names@@3PAPEBDA",
635
"?tx_isolation_names@@3PAPBDA",
636
wdl_tx_isolation_names, char*);
639
GET_PROC_ADDR(_db_enter_);
640
GET_PROC_ADDR(_db_return_);
641
GET_PROC_ADDR(_db_pargs_);
642
GET_PROC_ADDR(_db_doprnt_);
643
GET_PROC_ADDR(_db_dump_);
645
/* If any of the dbug functions is not available, just make them
646
all invalid. This is the case when working with a non-debug
647
version of the server. */
648
if (wdl_db_enter_ == NULL || wdl_db_return_ == NULL
649
|| wdl_db_pargs_ == NULL || wdl_db_doprnt_ == NULL
650
|| wdl_db_dump_ == NULL) {
652
wdl_db_enter_ = NULL;
653
wdl_db_return_ = NULL;
654
wdl_db_pargs_ = NULL;
655
wdl_db_doprnt_ = NULL;
658
#endif /* !DBUG_OFF */
669
/*******************************************************************//**
670
The DLL Delayed Loading Helper Function for resolving externals.
672
The function may fail due to one of the three reasons:
674
* Invalid parameter, which happens if the attributes in pidd aren't
676
* Failed to load the map file mysqld.map.
677
* Failed to find an external name in the map file mysqld.map.
679
Note: this function is called by run-time as well as __HrLoadAllImportsForDll.
680
So, it has to follow Windows call convention.
681
@return the address of the imported function */
686
PCImgDelayDescr pidd, /*!< in: a const pointer to a
687
ImgDelayDescr, see delayimp.h. */
688
FARPROC* iat_entry) /*!< in/out: A pointer to the slot in
689
the delay load import address table
690
to be updated with the address of the
691
imported function. */
698
/* Set up data used for the hook procs */
699
InternalImgDelayDescr idd = {
701
PFromRva<LPCSTR>(pidd->rvaDLLName),
702
PFromRva<HMODULE*>(pidd->rvaHmod),
703
PFromRva<PImgThunkData>(pidd->rvaIAT),
704
PFromRva<PCImgThunkData>(pidd->rvaINT),
705
PFromRva<PCImgThunkData>(pidd->rvaBoundIAT),
706
PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT),
710
DelayLoadInfo dli = {
711
sizeof(DelayLoadInfo),
721
/* Check the Delay Load Attributes, log an error of invalid
722
parameter, which happens if the attributes in pidd are not
723
specified correctly. */
724
if ((idd.grAttrs & dlattrRva) == 0) {
726
errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB: invalid parameter for delay loader.");
732
/* Calculate the index for the IAT entry in the import address table.
733
The INT entries are ordered the same as the IAT entries so the
734
calculation can be done on the IAT side. */
735
iIAT = (PCImgThunkData) iat_entry - idd.pIAT;
738
pitd = &(idd.pINT[iINT]);
740
dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal);
742
if (dli.dlp.fImportByName) {
744
dli.dlp.szProcName = (LPCSTR) (PFromRva<PIMAGE_IMPORT_BY_NAME>
745
((RVA) ((UINT_PTR) pitd->u1.AddressOfData))->Name);
748
dli.dlp.dwOrdinal = (ulint) IMAGE_ORDINAL(pitd->u1.Ordinal);
751
/* Now, load the mapfile, if it has not been done yet */
754
hmod = wdl_get_mysqld_mapfile();
758
/* LoadLibrary failed. */
759
PDelayLoadInfo rgpdli[1] = {&dli};
761
dli.dwLastError = ::GetLastError();
763
errmsg_printf(ERRMSG_LVL_ERROR,
764
"InnoDB: failed to load mysqld.map with error %d.",
770
/* Store the library handle. */
773
/* Go for the procedure now. */
776
if (pidd->rvaBoundIAT && pidd->dwTimeStamp) {
778
/* Bound imports exist, check the timestamp from the target
780
PIMAGE_NT_HEADERS pinh;
782
pinh = (PIMAGE_NT_HEADERS) ((byte*) hmod
783
+ ((PIMAGE_DOS_HEADER) hmod)->e_lfanew);
785
if (pinh->Signature == IMAGE_NT_SIGNATURE
786
&& pinh->FileHeader.TimeDateStamp == idd.dwTimeStamp
787
&& (DWORD) hmod == pinh->OptionalHeader.ImageBase) {
789
/* We have a decent address in the bound IAT. */
790
fun = (FARPROC) (UINT_PTR)
791
idd.pBoundIAT[iIAT].u1.Function;
801
fun = wdl_get_procaddr_from_map(hmod, dli.dlp.szProcName);
812
/*******************************************************************//**
813
Unload a DLL that was delay loaded. This function is called by run-time.
814
@return TRUE is returned if the DLL is found and the IAT matches the
818
__FUnloadDelayLoadedDLL2(
819
/*=====================*/
820
LPCSTR module_name) /*!< in: DLL name */
825
/**************************************************************//**
826
Load all imports from a DLL that was specified with the /delayload linker
828
Note: this function is called by run-time. So, it has to follow Windows call
830
@return S_OK if the DLL matches, otherwise ERROR_MOD_NOT_FOUND is returned. */
833
__HrLoadAllImportsForDll(
834
/*=====================*/
835
LPCSTR module_name) /*!< in: DLL name */
837
PIMAGE_NT_HEADERS img;
838
PCImgDelayDescr pidd;
839
IMAGE_DATA_DIRECTORY* image_data;
840
LPCSTR current_module;
841
HRESULT ret = ERROR_MOD_NOT_FOUND;
842
HMODULE hmod = (HMODULE) &__ImageBase;
844
img = (PIMAGE_NT_HEADERS) ((byte*) hmod
845
+ ((PIMAGE_DOS_HEADER) hmod)->e_lfanew);
847
&img->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];
849
/* Scan the delay load IAT/INT for the DLL */
850
if (image_data->Size) {
852
pidd = PFromRva<PCImgDelayDescr>(image_data->VirtualAddress);
854
/* Check all of the listed DLLs we want to load. */
855
while (pidd->rvaDLLName) {
857
current_module = PFromRva<LPCSTR>(pidd->rvaDLLName);
859
if (stricmp(module_name, current_module) == 0) {
861
/* Found it, break out with pidd and
862
current_module set appropriately */
866
/* To the next delay import descriptor */
870
if (pidd->rvaDLLName) {
872
/* Found a matching DLL, now process it. */
876
iat_entry = PFromRva<FARPROC*>(pidd->rvaIAT);
877
count = wdl_import_count((PCImgThunkData) iat_entry);
879
/* now load all the imports from the DLL */
882
/* No need to check the return value */
883
__delayLoadHelper2(pidd, iat_entry);
895
/**************************************************************//**
896
The main function of a DLL
897
@return TRUE if the call succeeds */
902
HINSTANCE hinstDLL, /*!< in: handle to the DLL module */
903
DWORD fdwReason, /*!< Reason code that indicates why the
904
DLL entry-point function is being
906
LPVOID lpvReserved) /*!< in: additional parameter based on
913
case DLL_PROCESS_ATTACH:
914
success = wdl_get_external_variables();
917
case DLL_PROCESS_DETACH:
926
/**************************************************************//**
927
Process entry point to user function. It makes the call to _db_enter_
928
in mysqld.exe. The DBUG functions are defined in my_dbug.h. */
929
extern "C" UNIV_INTERN
932
const char* _func_, /*!< in: current function name */
933
const char* _file_, /*!< in: current file name */
934
uint _line_, /*!< in: current source line number */
935
const char** _sfunc_, /*!< out: previous _func_ */
936
const char** _sfile_, /*!< out: previous _file_ */
937
uint* _slevel_, /*!< out: previous nesting level */
938
char*** _sframep_) /*!< out: previous frame pointer */
940
if (wdl_db_enter_ != NULL) {
942
wdl_db_enter_(_func_, _file_, _line_, _sfunc_, _sfile_,
943
_slevel_, _sframep_);
947
/**************************************************************//**
948
Process exit from user function. It makes the call to _db_return_()
950
extern "C" UNIV_INTERN
953
uint _line_, /*!< in: current source line number */
954
const char** _sfunc_, /*!< out: previous _func_ */
955
const char** _sfile_, /*!< out: previous _file_ */
956
uint* _slevel_) /*!< out: previous level */
958
if (wdl_db_return_ != NULL) {
960
wdl_db_return_(_line_, _sfunc_, _sfile_, _slevel_);
964
/**************************************************************//**
965
Log arguments for subsequent use. It makes the call to _db_pargs_()
967
extern "C" UNIV_INTERN
970
uint _line_, /*!< in: current source line number */
971
const char* keyword) /*!< in: keyword for current macro */
973
if (wdl_db_pargs_ != NULL) {
975
wdl_db_pargs_(_line_, keyword);
979
/**************************************************************//**
980
Handle print of debug lines. It saves the text into a buffer first,
981
then makes the call to _db_doprnt_() in the server. The text is
982
truncated to the size of buffer. */
983
extern "C" UNIV_INTERN
986
const char* format, /*!< in: the format string */
987
...) /*!< in: list of arguments */
992
if (wdl_db_doprnt_ != NULL) {
994
va_start(argp, format);
995
/* it is ok to ignore the trunction. */
996
_vsnprintf(buffer, sizeof(buffer), format, argp);
997
wdl_db_doprnt_(buffer);
1002
/**************************************************************//**
1003
Dump a string in hex. It makes the call to _db_dump_() in the server. */
1004
extern "C" UNIV_INTERN
1007
uint _line_, /*!< in: current source line
1009
const char* keyword, /*!< in: keyword list */
1010
const unsigned char* memory, /*!< in: memory to dump */
1011
size_t length) /*!< in: bytes to dump */
1013
if (wdl_db_dump_ != NULL) {
1015
wdl_db_dump_(_line_, keyword, memory, length);
1019
#endif /* !DBUG_OFF */
1020
#endif /* defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN) */