Commit e69becf1 authored by monty@narttu.mysql.fi's avatar monty@narttu.mysql.fi

changed to use IO_CACHE instead of FILE

parent 7a013339
monty@tik.mysql.com
monty@narttu.mysql.fi
......@@ -3610,10 +3610,13 @@ similar system. In the worst case, we may require access to your system
to be able to create a binary distribution.
@item
If you can provide accommodations and pay for traveler fares, you can even
get a @strong{MySQL} developer to visit you and offer you help with your
troubles. Extended login support entitles you to one personal
encounter per year, but we are always very flexible towards our customers!
If you can provide accommodations and pay for traveler fares, you can
even get a @strong{MySQL} developer to visit you and offer you help with
your troubles. Extended login support entitles you to one personal
encounter per year, but we are always very flexible towards our
customers! If the visit takes 16 hours or more, the first 8 hours is
without charge. For the hours above 8 hours, you will be charged with a
rate that is at least 20 % less than our standard rates.
@end itemize
@node Installing, Compatibility, Licensing and Support, Top
......@@ -38367,6 +38370,9 @@ though, so 3.23 is not released as a stable version yet.
@appendixsubsec Changes in release 3.23.28
@itemize @bullet
@item
Changed all log files to use our own IO_CACHE mechanism instead of
FILE:s to avoid OS problems when there is many files open.
@item
Added options @code{--open-files} and @code{--timezone} to @code{safe_mysqld}.
@item
Fixed fatal bug in @code{CREATE TEMPORARY TABLE ...SELECT ...}.
......@@ -25,7 +25,7 @@
** * *
** *************************
*/
#define IMPORT_VERSION "2.5"
#define IMPORT_VERSION "2.6"
#include <global.h>
#include <my_sys.h>
......@@ -125,7 +125,7 @@ file. The SQL command 'LOAD DATA INFILE' is used to import the rows.\n");
Give the column names in a comma separated list.\n\
This is same as giving columns to LOAD DATA INFILE.\n\
-C, --compress Use compression in server/client protocol\n\
-d, --delete Deletes first all rows from table.\n\
-d, --delete First delete all rows from table.\n\
-f, --force Continue even if we get an sql-error.\n\
-h, --host=... Connect to host.\n\
-i, --ignore If duplicate unique key was found, keep old row.\n\
......
......@@ -95,17 +95,6 @@ bool String::realloc(uint32 alloc_length)
return FALSE;
}
#ifdef NOT_NEEDED
bool String::set(long num)
{
if (alloc(14))
return TRUE;
str_length=(uint32) (int10_to_str(num,Ptr,-10)-Ptr);
return FALSE;
}
#endif
bool String::set(longlong num)
{
if (alloc(21))
......@@ -274,6 +263,7 @@ bool String::append(const char *s,uint32 arg_length)
return FALSE;
}
#ifdef TO_BE_REMOVED
bool String::append(FILE* file, uint32 arg_length, myf my_flags)
{
if (realloc(str_length+arg_length))
......@@ -286,6 +276,20 @@ bool String::append(FILE* file, uint32 arg_length, myf my_flags)
str_length+=arg_length;
return FALSE;
}
#endif
bool String::append(IO_CACHE* file, uint32 arg_length)
{
if (realloc(str_length+arg_length))
return TRUE;
if (my_b_read(file, (byte*) Ptr + str_length, arg_length))
{
shrink(str_length);
return TRUE;
}
str_length+=arg_length;
return FALSE;
}
uint32 String::numchars()
{
......
......@@ -107,9 +107,9 @@ public:
Alloced_length=0;
my_free(Ptr,MYF(0));
Ptr=0;
str_length=0; /* Safety */
}
}
inline bool alloc(uint32 arg_length)
{
if (arg_length < Alloced_length)
......@@ -152,7 +152,7 @@ public:
bool copy(const char *s,uint32 arg_length); // Allocate new string
bool append(const String &s);
bool append(const char *s,uint32 arg_length=0);
bool append(FILE* file, uint32 arg_length, myf my_flags);
bool append(IO_CACHE* file, uint32 arg_length);
int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
bool replace(uint32 offset,uint32 arg_length,const String &to);
......
......@@ -59,6 +59,7 @@ extern int NEAR my_errno; /* Last error in mysys */
#define MY_WME 16 /* Write message on error */
#define MY_WAIT_IF_FULL 32 /* Wait and try again if disk full error */
#define MY_RAID 64 /* Support for RAID (not the "Johnson&Johnson"-s one ;) */
#define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */
#define MY_LINK_WARNING 32 /* my_redel() gives warning if links */
#define MY_COPYTIME 64 /* my_redel() copys time */
#define MY_HOLD_ORIGINAL_MODES 128 /* my_copy() holds to file modes */
......@@ -505,6 +506,10 @@ extern int my_block_write(IO_CACHE *info, const byte *Buffer,
uint Count, my_off_t pos);
extern int flush_io_cache(IO_CACHE *info);
extern int end_io_cache(IO_CACHE *info);
extern uint my_b_fill(IO_CACHE *info);
extern void my_b_seek(IO_CACHE *info,my_off_t pos);
extern uint my_b_gets(IO_CACHE *info, char *to, uint max_length);
extern uint my_b_printf(IO_CACHE *info, const char* fmt, ...);
extern my_bool open_cached_file(IO_CACHE *cache,const char *dir,
const char *prefix, uint cache_size,
myf cache_myflags);
......
No preview for this file type
......@@ -26,7 +26,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\
mf_path.c mf_loadpath.c\
my_open.c my_create.c my_seek.c my_read.c \
my_pread.c my_write.c \
mf_reccache.c mf_keycache.c \
mf_keycache.c \
mf_iocache.c mf_cache.c mf_tempfile.c \
my_lock.c mf_brkhant.c my_alarm.c \
my_malloc.c my_realloc.c my_once.c mulalloc.c \
......
......@@ -74,7 +74,7 @@ my_bool open_cached_file(IO_CACHE *cache, const char* dir, const char *prefix,
}
my_free(cache->dir, MYF(MY_ALLOW_ZERO_PTR));
my_free(cache->prefix,MYF(MY_ALLOW_ZERO_PTR));
DBUG_RETURN(0);
DBUG_RETURN(1);
}
/* Create the temporary file */
......@@ -101,10 +101,12 @@ void close_cached_file(IO_CACHE *cache)
DBUG_ENTER("close_cached_file");
if (my_b_inited(cache))
{
File file=cache->file;
cache->file= -1; /* Don't flush data */
(void) end_io_cache(cache);
if (cache->file >= 0)
if (file >= 0)
{
(void) my_close(cache->file,MYF(0));
(void) my_close(file,MYF(0));
#ifdef CANT_DELETE_OPEN_FILES
if (cache->file_name)
{
......
......@@ -22,10 +22,13 @@
(and get a EOF-error).
Possibly use of asyncronic io.
macros for read and writes for faster io.
Used instead of FILE when reading or writing hole files.
This shall make mf_rec_cache obsolite.
One can change info->pos_in_file to a higer value to skipp bytes in file if
Used instead of FILE when reading or writing whole files.
This will make mf_rec_cache obsolete.
One can change info->pos_in_file to a higher value to skip bytes in file if
also info->rc_pos is set to info->rc_end.
If called through open_cached_file(), then the temporary file will
only be created if a write exeeds the file buffer or if one calls
flush_io_cache().
*/
#define MAP_TO_USE_RAID
......@@ -40,7 +43,7 @@ static void my_aiowait(my_aio_result *result);
/*
** if cachesize == 0 then use default cachesize (from s-file)
** if file == -1 then real_open_cached_file() will be called to
** if file == -1 then real_open_cached_file() will be called.
** returns 0 if ok
*/
......@@ -59,6 +62,12 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
if (type == READ_CACHE)
{ /* Assume file isn't growing */
if (cache_myflags & MY_DONT_CHECK_FILESIZE)
{
cache_myflags &= ~MY_DONT_CHECK_FILESIZE;
}
else
{
my_off_t file_pos,end_of_file;
if ((file_pos=my_tell(file,MYF(0)) == MY_FILEPOS_ERROR))
DBUG_RETURN(1);
......@@ -72,6 +81,7 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
use_async_io=0; /* No nead to use async */
}
}
}
for (;;)
{
......@@ -545,7 +555,6 @@ int my_block_write(register IO_CACHE *info, const byte *Buffer, uint Count,
return error;
}
/* Flush write cache */
int flush_io_cache(IO_CACHE *info)
......@@ -565,7 +574,9 @@ int flush_io_cache(IO_CACHE *info)
length=(uint) (info->rc_pos - info->buffer);
if (info->seek_not_done)
{ /* File touched, do seek */
VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)));
if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)) ==
MY_FILEPOS_ERROR)
DBUG_RETURN((info->error= -1));
info->seek_not_done=0;
}
info->rc_pos=info->buffer;
......
......@@ -21,74 +21,49 @@
#include <stdarg.h>
#include <m_ctype.h>
int my_vsnprintf(char* str, size_t n, const char* fmt, va_list ap)
int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap)
{
uint olen = 0, plen;
const char *tpos;
reg1 char *endpos;
reg2 char * par;
char* ebuff = str;
char *start=to, *end=to+n-1;
endpos=ebuff;
tpos = fmt;
while (*tpos)
for (; *fmt ; fmt++)
{
if (tpos[0] != '%')
if (fmt[0] != '%')
{
if(olen + 1 >= n)
if (to == end) /* End of buffer */
break;
*endpos++= *tpos++; /* Copy ordinary char */
olen++;
*to++= *fmt; /* Copy ordinary char */
continue;
}
if (*++tpos == '%') /* test if %% */
{
olen--;
}
else
{
/* Skipp if max size is used (to be compatible with printf) */
while (isdigit(*tpos) || *tpos == '.' || *tpos == '-')
tpos++;
if (*tpos == 's') /* String parameter */
while (isdigit(*fmt) || *fmt == '.' || *fmt == '-')
fmt++;
if (*fmt == 's') /* String parameter */
{
par = va_arg(ap, char *);
plen = (uint) strlen(par);
if (olen + plen < n) /* Replace if possible */
reg2 char *par = va_arg(ap, char *);
uint plen = (uint) strlen(par);
if ((uint) (end-to) > plen) /* Replace if possible */
{
endpos=strmov(endpos,par);
tpos++;
olen+=plen;
to=strmov(to,par);
continue;
}
}
else if (*tpos == 'd' || *tpos == 'u') /* Integer parameter */
else if (*fmt == 'd' || *fmt == 'u') /* Integer parameter */
{
register int iarg;
if ((uint) (end-to) < 16)
break;
iarg = va_arg(ap, int);
if(olen + 16 >= n) break;
if (*tpos == 'd')
plen= (uint) (int2str((long) iarg,endpos, -10) - endpos);
if (*fmt == 'd')
to=int10_to_str((long) iarg,to, -10);
else
plen= (uint) (int2str((long) (uint) iarg,endpos,10)- endpos);
if (olen + plen < n) /* Replace parameter if possible */
{
endpos+=plen;
tpos++;
olen+=plen;
to=int10_to_str((long) (uint) iarg,to,10);
continue;
}
/* We come here on '%%', unknown code or too long parameter */
if (to == end)
break;
*to++='%'; /* % used as % or unknown code */
}
}
*endpos++='%'; /* % used as % or unknown code */
}
*endpos='\0';
/* End of errmessage */
return olen;
*to='\0'; /* End of errmessage */
return (uint) (to - start);
}
......@@ -72,17 +72,16 @@ static int find_uniq_filename(char *name)
DBUG_RETURN(0);
}
MYSQL_LOG::MYSQL_LOG(): file(-1),index_file(-1),last_time(0),query_start(0),
name(0), log_type(LOG_CLOSED),write_error(0),inited(0),
no_rotate(0)
MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),
name(0), log_type(LOG_CLOSED),write_error(0),
inited(0), opened(0), no_rotate(0)
{
/*
We don't want to intialize LOCK_Log here as the thread system may
not have been initailized yet. We do it instead at 'open'.
*/
index_file_name[0] = 0;
bzero((char*) &log_file,sizeof(log_file));
}
MYSQL_LOG::~MYSQL_LOG()
......@@ -103,6 +102,7 @@ void MYSQL_LOG::set_index_file_name(const char* index_file_name)
this->index_file_name[0] = 0;
}
int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
{
if (log_type == LOG_NORMAL)
......@@ -128,6 +128,9 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
{
MY_STAT tmp_stat;
char buff[512];
File file= -1;
bool do_magic;
if (!inited)
{
inited=1;
......@@ -138,27 +141,25 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
}
log_type=log_type_arg;
name=my_strdup(log_name,MYF(0));
if (!(name=my_strdup(log_name,MYF(MY_WME))))
goto err;
if (new_name)
strmov(log_file_name,new_name);
else if (generate_new_name(log_file_name, name))
return;
goto err;
if (log_type == LOG_BIN && !index_file_name[0])
fn_format(index_file_name, name, mysql_data_home, ".index", 6);
db[0]=0;
bool do_magic = ((log_type == LOG_BIN) && !my_stat(log_file_name,
do_magic = ((log_type == LOG_BIN) && !my_stat(log_file_name,
&tmp_stat, MYF(0)));
if ((file=my_open(log_file_name,O_APPEND | O_WRONLY | O_BINARY,
MYF(MY_WME | ME_WAITTANG)) < 0)
{
my_free(name,MYF(0));
name=0;
log_type=LOG_CLOSED;
return;
}
MYF(MY_WME | ME_WAITTANG))) < 0 ||
init_io_cache(&log_file, file, IO_SIZE, WRITE_CACHE,
my_tell(file,MYF(MY_WME)), 0, MYF(MY_WME | MY_NABP)))
goto err;
if (log_type == LOG_NORMAL)
{
......@@ -169,7 +170,9 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
sprintf(buff, "%s, Version: %s, started with:\nTcp port: %d Unix socket: %s\n", my_progname,server_version,mysql_port,mysql_unix_port);
#endif
end=strmov(strend(buff),"Time Id Command Argument\n");
my_write(file,buff,(uint) (end-buff),MYF(0));
if (my_b_write(&log_file,buff,(uint) (end-buff)) ||
flush_io_cache(&log_file))
goto err;
}
else if (log_type == LOG_NEW)
{
......@@ -184,11 +187,12 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
tm_tmp.tm_hour,
tm_tmp.tm_min,
tm_tmp.tm_sec);
my_write(file,buff,(uint) strlen(buff),MYF(0));
if (my_b_write(&log_file,buff,(uint) strlen(buff)) ||
flush_io_cache(&log_file))
goto err;
}
else if (log_type == LOG_BIN)
{
// Explanation of the boolean black magic:
//
// if we are supposed to write magic number try write
......@@ -196,34 +200,36 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
// then if index_file has not been previously opened, try to open it
// clean up if failed
if ((do_magic && my_write(file, (byte*) BINLOG_MAGIC, 4,
MYF(MY_NABP|MY_WME)) ||
if ((do_magic && my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4)) ||
(index_file < 0 &&
(index_file = my_fopen(index_file_name,O_APPEND | O_BINARY | O_RDWR,
MYF(MY_WME))) < 0)))
{
my_close(file,MYF(MY_WME));
my_free(name,MYF(0));
name=0;
file= -1;
log_type=LOG_CLOSED;
return;
}
(index_file = my_open(index_file_name,O_APPEND | O_BINARY | O_RDWR,
MYF(MY_WME))) < 0))
goto err;
Start_log_event s;
s.write(file);
s.write(&log_file);
pthread_mutex_lock(&LOCK_index);
my_seek(index_file, 0L, MY_SEEK_END, MYF(MY_WME));
my_write(index_file, log_file_name,strlen(log_file_name), MYF(0));
my_write(index_file, "\n",1, MYF(0));
pthread_mutex_unlock(&LOCK_index);
}
return;
err:
if (file >= 0)
my_close(file,MYF(0));
end_io_cache(&log_file);
x_free(name); name=0;
log_type=LOG_CLOSED;
return;
}
int MYSQL_LOG::get_current_log(LOG_INFO* linfo)
{
pthread_mutex_lock(&LOCK_log);
strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name));
linfo->pos = my_tell(file, MYF(MY_WME));
strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1);
linfo->pos = my_b_tell(&log_file);
pthread_mutex_unlock(&LOCK_log);
return 0;
}
......@@ -231,46 +237,46 @@ int MYSQL_LOG::get_current_log(LOG_INFO* linfo)
// if log_name is "" we stop at the first entry
int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name)
{
// mutex needed because we need to make sure the file pointer does not move
// from under our feet
if (index_file < 0) return LOG_INFO_INVALID;
if (index_file < 0)
return LOG_INFO_INVALID;
int error = 0;
char* fname = linfo->log_file_name;
int log_name_len = (uint) strlen(log_name);
IO_CACHE io_cache;
// mutex needed because we need to make sure the file pointer does not move
// from under our feet
pthread_mutex_lock(&LOCK_index);
if (my_seek(index_file, 0L, MY_SEEK_SET, MYF(MY_WME) ) == MY_FILEPOS_ERROR)
if (init_io_cache(&io_cache, index_file, IO_SIZE, READ_CACHE, (my_off_t) 0,
0, MYF(MY_WME)))
{
error = LOG_INFO_SEEK;
goto err;
}
for(;;)
{
if (!fgets(fname, FN_REFLEN, index_file))
uint length;
if (!(length=my_b_gets(&io_cache, fname, FN_REFLEN)))
{
error = feof(index_file) ? LOG_INFO_EOF : LOG_INFO_IO;
error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO;
goto err;
}
// if the log entry matches, empty string matching anything
if (!log_name_len ||
(fname[log_name_len] == '\n' &&
(log_name_len == length+1 && fname[log_name_len] == '\n' &&
!memcmp(fname, log_name, log_name_len)))
{
if (log_name_len)
fname[log_name_len] = 0; // to kill \n
else
{
*(strend(fname) - 1) = 0;
}
linfo->index_file_offset = my_tell(index_file, MYF(MY_WME));
fname[length-1]=0; // remove last \n
linfo->index_file_offset = my_b_tell(&io_cache);
break;
}
}
error = 0;
err:
pthread_mutex_unlock(&LOCK_index);
end_io_cache(&io_cache);
return error;
}
......@@ -283,27 +289,29 @@ int MYSQL_LOG::find_next_log(LOG_INFO* linfo)
if (!index_file) return LOG_INFO_INVALID;
int error = 0;
char* fname = linfo->log_file_name;
char* end ;
IO_CACHE io_cache;
uint length;
pthread_mutex_lock(&LOCK_index);
if (my_fseek(index_file, linfo->index_file_offset, MY_SEEK_SET, MYF(MY_WME) ) == MY_FILEPOS_ERROR)
if (init_io_cache(&io_cache, index_file, IO_SIZE,
READ_CACHE, (my_off_t) linfo->index_file_offset, 0,
MYF(MY_WME)))
{
error = LOG_INFO_SEEK;
goto err;
}
if (!fgets(fname, FN_REFLEN, index_file))
if (!(length=my_b_gets(&io_cache, fname, FN_REFLEN)))
{
error = feof(index_file) ? LOG_INFO_EOF : LOG_INFO_IO;
error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO;
goto err;
}
end = strend(fname) - 1;
*end = 0; // kill /n
linfo->index_file_offset = ftell(index_file);
fname[length-1]=0; // kill /n
linfo->index_file_offset = my_b_tell(&io_cache);
error = 0;
err:
pthread_mutex_unlock(&LOCK_index);
end_io_cache(&io_cache);
return error;
}
......@@ -311,22 +319,18 @@ err:
// we assume that buf has at least FN_REFLEN bytes alloced
void MYSQL_LOG::make_log_name(char* buf, const char* log_ident)
{
buf[0] = 0; // In case of error
if (inited)
{
int dir_len = dirname_length(log_file_name);
int ident_len = (uint) strlen(log_ident);
if (dir_len + ident_len + 1 > FN_REFLEN)
{
buf[0] = 0;
return; // protection agains malicious buffer overflow
}
memcpy(buf, log_file_name, dir_len);
memcpy(buf + dir_len, log_ident, ident_len + 1); // this takes care of \0
// at the end
// copy filename + end null
memcpy(buf + dir_len, log_ident, ident_len + 1);
}
else
buf[0] = 0;
}
bool MYSQL_LOG::is_active(const char* log_file_name)
......@@ -336,15 +340,17 @@ bool MYSQL_LOG::is_active(const char* log_file_name)
void MYSQL_LOG::new_file()
{
if (file)
// only rotate open logs that are marked non-rotatable
// (binlog with constant name are non-rotatable)
if (is_open() && ! no_rotate)
{
if (no_rotate) // do not rotate logs that are marked non-rotatable
return; // ( for binlog with constant name)
char new_name[FN_REFLEN], *old_name=name;
VOID(pthread_mutex_lock(&LOCK_log));
if (generate_new_name(new_name, name))
{
VOID(pthread_mutex_unlock(&LOCK_log));
return; // Something went wrong
}
if (log_type == LOG_BIN)
{
/*
......@@ -352,15 +358,13 @@ void MYSQL_LOG::new_file()
to change base names at some point.
*/
Rotate_log_event r(new_name+dirname_length(new_name));
r.write(file);
r.write(&log_file);
VOID(pthread_cond_broadcast(&COND_binlog_update));
}
name=0;
close();
open(old_name, log_type, new_name);
my_free(old_name,MYF(0));
if (!file) // Something went wrong
log_type=LOG_CLOSED;
last_time=query_start=0;
write_error=0;
VOID(pthread_mutex_unlock(&LOCK_log));
......@@ -375,7 +379,10 @@ void MYSQL_LOG::write(THD *thd,enum enum_server_command command,
{
va_list args;
va_start(args,format);
char buff[32];
VOID(pthread_mutex_lock(&LOCK_log));
/* Test if someone closed after the is_open test */
if (log_type != LOG_CLOSED)
{
time_t skr;
......@@ -405,28 +412,30 @@ void MYSQL_LOG::write(THD *thd,enum enum_server_command command,
struct tm *start;
localtime_r(&skr,&tm_tmp);
start=&tm_tmp;
if (fprintf(file,"%02d%02d%02d %2d:%02d:%02d\t",
/* Note that my_b_write() assumes it knows the length for this */
sprintf(buff,"%02d%02d%02d %2d:%02d:%02d\t",
start->tm_year % 100,
start->tm_mon+1,
start->tm_mday,
start->tm_hour,
start->tm_min,
start->tm_sec) < 0)
start->tm_sec);
if (my_b_write(&log_file,buff,16))
error=errno;
}
else if (fputs("\t\t",file) < 0)
else if (my_b_write(&log_file,"\t\t",2) < 0)
error=errno;
if (fprintf(file,"%7ld %-10.10s",
id,command_name[(uint) command]) < 0)
sprintf(buff,"%7ld %-10.10s", id,command_name[(uint) command]);
if (my_b_write(&log_file,buff,strlen(buff)))
error=errno;
if (format)
{
if (fputc(' ',file) < 0 || vfprintf(file,format,args) < 0)
if (my_b_write(&log_file," ",1) ||
my_b_printf(&log_file,format,args) == (uint) -1)
error=errno;
}
if (fputc('\n',file) < 0)
error=errno;
if (fflush(file) < 0)
if (my_b_write(&log_file,"\n",1) ||
flush_io_cache(&log_file))
error=errno;
if (error && ! write_error)
{
......@@ -446,7 +455,7 @@ void MYSQL_LOG::write(Query_log_event* event_info)
if (is_open())
{
VOID(pthread_mutex_lock(&LOCK_log));
if (file)
if (is_open())
{
THD *thd=event_info->thd;
if ((!(thd->options & OPTION_BIN_LOG) &&
......@@ -460,23 +469,21 @@ void MYSQL_LOG::write(Query_log_event* event_info)
if (thd->last_insert_id_used)
{
Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->last_insert_id);
if (e.write(file))
if (e.write(&log_file))
{
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
goto err;
}
}
if (thd->insert_id_used)
{
Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id);
if (e.write(file))
if (e.write(&log_file))
{
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
goto err;
}
}
if (thd->convert_set)
{
char buf[1024] = "SET CHARACTER SET ";
......@@ -486,17 +493,14 @@ void MYSQL_LOG::write(Query_log_event* event_info)
// just in case somebody wants it later
thd->query_length = (uint)(p - buf);
Query_log_event e(thd, buf);
if (e.write(file))
if (e.write(&log_file))
{
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
goto err;
}
thd->query_length = save_query_length; // clean up
}
if (event_info->write(file))
if (event_info->write(&log_file) || flush_io_cache(&log_file))
{
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
}
......@@ -512,13 +516,13 @@ void MYSQL_LOG::write(Load_log_event* event_info)
if (is_open())
{
VOID(pthread_mutex_lock(&LOCK_log));
if (file)
if (is_open())
{
THD *thd=event_info->thd;
if ((thd->options & OPTION_BIN_LOG) ||
!(thd->master_access & PROCESS_ACL))
{
if (event_info->write(file))
if (event_info->write(&log_file) || flush_io_cache(&log_file))
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
VOID(pthread_cond_broadcast(&COND_binlog_update));
}
......@@ -537,7 +541,7 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
{
time_t current_time;
VOID(pthread_mutex_lock(&LOCK_log));
if (file)
if (is_open())
{ // Safety agains reopen
int error=0;
char buff[80],*end;
......@@ -556,37 +560,42 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
last_time=current_time;
struct tm tm_tmp;
struct tm *start;
char buff[32];
localtime_r(&current_time,&tm_tmp);
start=&tm_tmp;
if (fprintf(file,"# Time: %02d%02d%02d %2d:%02d:%02d\n",
/* Note that my_b_write() assumes it knows the length for this */
sprintf(buff,"# Time: %02d%02d%02d %2d:%02d:%02d\n",
start->tm_year % 100,
start->tm_mon+1,
start->tm_mday,
start->tm_hour,
start->tm_min,
start->tm_sec) < 0)
start->tm_sec);
if (my_b_write(&log_file,buff,24))
error=errno;
}
if (fprintf(file, "# User@Host: %s [%s] @ %s [%s]\n",
if (my_b_printf(&log_file, "# User@Host: %s [%s] @ %s [%s]\n",
thd->priv_user,
thd->user,
thd->host ? thd->host : "",
thd->ip ? thd->ip : "") < 0)
error=errno;;
thd->ip ? thd->ip : ""))
error=errno;
}
if (query_start)
{
/* For slow query log */
if (!(specialflag & SPECIAL_LONG_LOG_FORMAT))
current_time=time(NULL);
fprintf(file,"# Time: %lu Lock_time: %lu Rows_sent: %lu\n",
if (my_b_printf(&log_file,
"# Time: %lu Lock_time: %lu Rows_sent: %lu\n",
(ulong) (current_time - query_start),
(ulong) (thd->time_after_lock - query_start),
(ulong) thd->sent_row_count);
(ulong) thd->sent_row_count))
error=errno;
}
if (thd->db && strcmp(thd->db,db))
{ // Database changed
if (fprintf(file,"use %s;\n",thd->db) < 0)
if (my_b_printf(&log_file,"use %s;\n",thd->db))
error=errno;
strmov(db,thd->db);
}
......@@ -618,7 +627,8 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
*end++=';';
*end++='\n';
*end=0;
if (fputs("SET ",file) < 0 || fputs(buff+1,file) < 0)
if (my_b_write(&log_file,"SET ",4) ||
my_b_write(&log_file,buff+1,(uint) (end-buff)-1))
error=errno;
}
if (!query)
......@@ -626,10 +636,9 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
query="#adminstrator command";
query_length=21;
}
if (my_fwrite(file,(byte*) query,query_length,MYF(MY_NABP)) ||
fputc(';',file) < 0 || fputc('\n',file) < 0)
error=errno;
if (fflush(file) < 0)
if (my_b_write(&log_file,(byte*) query,query_length) ||
my_b_write(&log_file,";\n",2) ||
flush_io_cache(&log_file))
error=errno;
if (error && ! write_error)
{
......@@ -641,51 +650,48 @@ void MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
}
}
#ifdef TO_BE_REMOVED
void MYSQL_LOG::flush()
{
if (file)
if (fflush(file) < 0 && ! write_error)
if (is_open())
if (flush_io_cache(log_file) && ! write_error)
{
write_error=1;
sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
}
}
#endif
void MYSQL_LOG::close(bool exiting)
{ // One can't set log_type here!
if (file)
if (is_open())
{
File file=log_file.file;
if (log_type == LOG_BIN)
{
Stop_log_event s;
s.write(file);
s.write(&log_file);
VOID(pthread_cond_broadcast(&COND_binlog_update));
}
if (my_fclose(file,MYF(0)) < 0 && ! write_error)
end_io_cache(&log_file);
if (my_close(file,MYF(0)) < 0 && ! write_error)
{
write_error=1;
sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
}
file=0;
}
if (name)
{
my_free(name,MYF(0));
name=0;
log_type=LOG_CLOSED;
}
if (exiting && index_file)
if (exiting && index_file >= 0)
{
if (my_fclose(index_file,MYF(0)) < 0 && ! write_error)
if (my_close(index_file,MYF(0)) < 0 && ! write_error)
{
write_error=1;
sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
}
index_file=0;
}
safeFree(name);
}
......
......@@ -26,8 +26,7 @@
static void pretty_print_char(FILE* file, int c)
{
fputc('\'', file);
switch(c)
{
switch(c) {
case '\n': fprintf(file, "\\n"); break;
case '\r': fprintf(file, "\\r"); break;
case '\\': fprintf(file, "\\\\"); break;
......@@ -38,72 +37,67 @@ static void pretty_print_char(FILE* file, int c)
fputc(c, file);
break;
}
fputc( '\'', file);
fputc('\'', file);
}
int Query_log_event::write(FILE* file)
int Query_log_event::write(IO_CACHE* file)
{
return query ? Log_event::write(file) : -1;
}
int Log_event::write(FILE* file)
int Log_event::write(IO_CACHE* file)
{
if (write_header(file)
|| write_data(file) || fflush(file)) return -1;
return 0;
return (write_header(file) || write_data(file)) ? -1 : 0;
}
int Log_event::write_header(FILE* file)
int Log_event::write_header(IO_CACHE* file)
{
char buf[LOG_EVENT_HEADER_LEN];
// make sure to change this when the header gets bigger
char buf[LOG_EVENT_HEADER_LEN];
char* pos = buf;
int4store(pos, when); // timestamp
pos += 4;
*pos++ = get_type_code(); // event type code
int4store(pos, server_id);
pos += 4;
int4store(pos, get_data_size() + LOG_EVENT_HEADER_LEN);
long tmp=get_data_size() + LOG_EVENT_HEADER_LEN;
int4store(pos, tmp);
pos += 4;
return (my_fwrite(file, (byte*) buf, (uint) (pos - buf),
MYF(MY_NABP | MY_WME)));
return (my_b_write(file, (byte*) buf, (uint) (pos - buf)));
}
#ifndef MYSQL_CLIENT
int Log_event::read_log_event(FILE* file, String* packet,
int Log_event::read_log_event(IO_CACHE* file, String* packet,
pthread_mutex_t* log_lock)
{
ulong data_len;
char buf[LOG_EVENT_HEADER_LEN];
if(log_lock)
if (log_lock)
pthread_mutex_lock(log_lock);
if (my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP)))
if (my_b_read(file, (byte*) buf, sizeof(buf)))
{
if(log_lock) pthread_mutex_unlock(log_lock);
return feof(file) ? LOG_READ_EOF: LOG_READ_IO;
if (log_lock) pthread_mutex_unlock(log_lock);
return file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO;
}
data_len = uint4korr(buf + EVENT_LEN_OFFSET);
if (data_len < LOG_EVENT_HEADER_LEN || data_len > MAX_EVENT_LEN)
{
if(log_lock) pthread_mutex_unlock(log_lock);
if (log_lock) pthread_mutex_unlock(log_lock);
return LOG_READ_BOGUS;
}
packet->append(buf, sizeof(buf));
data_len -= LOG_EVENT_HEADER_LEN;
if (!data_len)
if (data_len)
{
if(log_lock) pthread_mutex_unlock(log_lock);
return 0; // the event does not have a data section
}
if (packet->append(file, data_len, MYF(MY_WME|MY_NABP)))
if (packet->append(file, data_len))
{
if(log_lock)
pthread_mutex_unlock(log_lock);
return feof(file) ? LOG_READ_TRUNC: LOG_READ_IO;
return file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO;
}
if(log_lock) pthread_mutex_unlock(log_lock);
}
if (log_lock) pthread_mutex_unlock(log_lock);
return 0;
}
......@@ -111,16 +105,16 @@ int Log_event::read_log_event(FILE* file, String* packet,
// allocates memory - the caller is responsible for clean-up
Log_event* Log_event::read_log_event(FILE* file, pthread_mutex_t* log_lock)
Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock)
{
time_t timestamp;
uint32 server_id;
char buf[LOG_EVENT_HEADER_LEN-4];
if(log_lock) pthread_mutex_lock(log_lock);
if (my_fread(file, (byte *) buf, sizeof(buf), MY_NABP))
if (my_b_read(file, (byte *) buf, sizeof(buf)))
{
if(log_lock) pthread_mutex_unlock(log_lock);
if (log_lock) pthread_mutex_unlock(log_lock);
return NULL;
}
timestamp = uint4korr(buf);
......@@ -132,13 +126,11 @@ Log_event* Log_event::read_log_event(FILE* file, pthread_mutex_t* log_lock)
{
Query_log_event* q = new Query_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
if (!q->query)
{
delete q;
return NULL;
q=NULL;
}
return q;
}
......@@ -146,13 +138,11 @@ Log_event* Log_event::read_log_event(FILE* file, pthread_mutex_t* log_lock)
{
Load_log_event* l = new Load_log_event(file, timestamp, server_id);
if(log_lock) pthread_mutex_unlock(log_lock);
if (!l->table_name)
{
delete l;
return NULL;
l=NULL;
}
return l;
}
......@@ -165,9 +155,8 @@ Log_event* Log_event::read_log_event(FILE* file, pthread_mutex_t* log_lock)
if (!r->new_log_ident)
{
delete r;
return NULL;
r=NULL;
}
return r;
}
......@@ -179,9 +168,8 @@ Log_event* Log_event::read_log_event(FILE* file, pthread_mutex_t* log_lock)
if (e->type == INVALID_INT_EVENT)
{
delete e;
return NULL;
e=NULL;
}
return e;
}
......@@ -198,12 +186,11 @@ Log_event* Log_event::read_log_event(FILE* file, pthread_mutex_t* log_lock)
return e;
}
default:
if(log_lock) pthread_mutex_unlock(log_lock);
return NULL;
break;
}
//impossible
if(log_lock) pthread_mutex_unlock(log_lock);
// default
if (log_lock) pthread_mutex_unlock(log_lock);
return NULL;
}
......@@ -320,26 +307,24 @@ void Rotate_log_event::print(FILE* file, bool short_form)
fflush(file);
}
Rotate_log_event::Rotate_log_event(FILE* file, time_t when_arg,
Rotate_log_event::Rotate_log_event(IO_CACHE* file, time_t when_arg,
uint32 server_id):
Log_event(when_arg, 0, 0, server_id),new_log_ident(NULL),alloced(0)
{
char *tmp_ident;
char buf[4];
if (my_fread(file, (byte*) buf, sizeof(buf), MYF(MY_NABP | MY_WME)))
if (my_b_read(file, (byte*) buf, sizeof(buf)))
return;
ulong event_len;
event_len = uint4korr(buf);
if(event_len < ROTATE_EVENT_OVERHEAD)
if (event_len < ROTATE_EVENT_OVERHEAD)
return;
ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD);
if (!(tmp_ident = (char*) my_malloc((uint)ident_len, MYF(MY_WME))))
return;
if (my_fread( file, (byte*) tmp_ident, (uint)ident_len, MYF(MY_NABP | MY_WME)))
if (my_b_read( file, (byte*) tmp_ident, (uint) ident_len))
{
my_free((gptr) tmp_ident, MYF(0));
return;
......@@ -373,21 +358,18 @@ Rotate_log_event::Rotate_log_event(const char* buf, int max_buf):
alloced = 1;
}
int Rotate_log_event::write_data(FILE* file)
int Rotate_log_event::write_data(IO_CACHE* file)
{
if (my_fwrite(file, (byte*) new_log_ident, (uint) ident_len,
MYF(MY_NABP | MY_WME)))
return -1;
return 0;
return my_b_write(file, (byte*) new_log_ident, (uint) ident_len) ? -1 :0;
}
Query_log_event::Query_log_event(FILE* file, time_t when_arg,
Query_log_event::Query_log_event(IO_CACHE* file, time_t when_arg,
uint32 server_id):
Log_event(when_arg,0,0,server_id),data_buf(0),query(NULL),db(NULL)
{
char buf[QUERY_HEADER_LEN + 4];
ulong data_len;
if (my_fread(file, (byte*) buf, sizeof(buf), MYF(MY_NABP | MY_WME)))
if (my_b_read(file, (byte*) buf, sizeof(buf)))
return; // query == NULL will tell the
// caller there was a problem
data_len = uint4korr(buf);
......@@ -399,9 +381,10 @@ Query_log_event::Query_log_event(FILE* file, time_t when_arg,
db_len = (uint)buf[12];
error_code = uint2korr(buf + 13);
/* Allocate one byte extra for end \0 */
if (!(data_buf = (char*) my_malloc(data_len+1, MYF(MY_WME))))
return;
if (my_fread( file, (byte*) data_buf, data_len, MYF(MY_NABP | MY_WME)))
if (my_b_read( file, (byte*) data_buf, data_len))
{
my_free((gptr) data_buf, MYF(0));
data_buf = 0;
......@@ -412,7 +395,7 @@ Query_log_event::Query_log_event(FILE* file, time_t when_arg,
db = data_buf;
query=data_buf + db_len + 1;
q_len = data_len - 1 - db_len;
*((char*)query + q_len) = 0;
*((char*) query + q_len) = 0; // Safety
}
Query_log_event::Query_log_event(const char* buf, int max_buf):
......@@ -428,7 +411,7 @@ Query_log_event::Query_log_event(const char* buf, int max_buf):
exec_time = uint4korr(buf + 8);
error_code = uint2korr(buf + 13);
if (!(data_buf = (char*) my_malloc( data_len + 1, MYF(MY_WME))))
if (!(data_buf = (char*) my_malloc(data_len + 1, MYF(MY_WME))))
return;
memcpy(data_buf, buf + QUERY_HEADER_LEN + 4, data_len);
......@@ -455,9 +438,9 @@ void Query_log_event::print(FILE* file, bool short_form)
fprintf(file, ";\n");
}
int Query_log_event::write_data(FILE* file)
int Query_log_event::write_data(IO_CACHE* file)
{
if(!query) return -1;
if (!query) return -1;
char buf[QUERY_HEADER_LEN];
char* pos = buf;
......@@ -469,23 +452,21 @@ int Query_log_event::write_data(FILE* file)
int2store(pos, error_code);
pos += 2;
if (my_fwrite(file, (byte*) buf, (uint)(pos - buf), MYF(MY_NABP | MY_WME)) ||
my_fwrite(file, (db) ? (byte*) db : (byte*)"",
db_len + 1, MYF(MY_NABP | MY_WME)) ||
my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME)))
return -1;
return 0;
return (my_b_write(file, (byte*) buf, (uint)(pos - buf)) ||
my_b_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
my_b_write(file, (byte*) query, q_len)) ? -1 : 0;
}
Intvar_log_event:: Intvar_log_event(FILE* file, time_t when_arg,
Intvar_log_event:: Intvar_log_event(IO_CACHE* file, time_t when_arg,
uint32 server_id)
:Log_event(when_arg,0,0,server_id), type(INVALID_INT_EVENT)
{
my_fseek(file, 4L, MY_SEEK_CUR, MYF(MY_WME)); // skip the event length
char buf[9];
if(my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME))) return;
type = buf[0];
val = uint8korr(buf+1);
char buf[9+4];
if (!my_b_read(file, (byte*) buf, sizeof(buf)))
{
type = buf[4];
val = uint8korr(buf+1+4);
}
}
Intvar_log_event::Intvar_log_event(const char* buf):Log_event(buf)
......@@ -495,12 +476,12 @@ Intvar_log_event::Intvar_log_event(const char* buf):Log_event(buf)
val = uint8korr(buf+1);
}
int Intvar_log_event::write_data(FILE* file)
int Intvar_log_event::write_data(IO_CACHE* file)
{
char buf[9];
buf[0] = type;
int8store(buf + 1, val);
return my_fwrite(file, (byte*) buf, sizeof(buf), MYF(MY_NABP|MY_WME));
return my_b_write(file, (byte*) buf, sizeof(buf));
}
void Intvar_log_event::print(FILE* file, bool short_form)
......@@ -527,7 +508,7 @@ void Intvar_log_event::print(FILE* file, bool short_form)
}
int Load_log_event::write_data(FILE* file __attribute__((unused)))
int Load_log_event::write_data(IO_CACHE* file)
{
char buf[LOAD_HEADER_LEN];
int4store(buf, thread_id);
......@@ -537,77 +518,46 @@ int Load_log_event::write_data(FILE* file __attribute__((unused)))
buf[13] = (char)db_len;
int4store(buf + 14, num_fields);
if(my_fwrite(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)&sql_ex, sizeof(sql_ex), MYF(MY_NABP|MY_WME)))
if(my_b_write(file, (byte*)buf, sizeof(buf)) ||
my_b_write(file, (byte*)&sql_ex, sizeof(sql_ex)))
return 1;
if(num_fields && fields && field_lens)
if (num_fields && fields && field_lens)
{
if(my_fwrite(file, (byte*)field_lens, num_fields, MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)fields, field_block_len, MYF(MY_NABP|MY_WME)))
if(my_b_write(file, (byte*)field_lens, num_fields) ||
my_b_write(file, (byte*)fields, field_block_len))
return 1;
}
if(my_fwrite(file, (byte*)table_name, table_name_len + 1, MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)db, db_len + 1, MYF(MY_NABP|MY_WME)) ||
my_fwrite(file, (byte*)fname, fname_len, MYF(MY_NABP|MY_WME)) )
if(my_b_write(file, (byte*)table_name, table_name_len + 1) ||
my_b_write(file, (byte*)db, db_len + 1) ||
my_b_write(file, (byte*)fname, fname_len))
return 1;
return 0;
}
Load_log_event::Load_log_event(FILE* file, time_t when, uint32 server_id):
Load_log_event::Load_log_event(IO_CACHE* file, time_t when, uint32 server_id):
Log_event(when,0,0,server_id),data_buf(0),num_fields(0),
fields(0),field_lens(0),field_block_len(0),
table_name(0),db(0),fname(0)
{
char buf[LOAD_HEADER_LEN + 4];
ulong data_len;
if(my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME)) ||
my_fread(file, (byte*)&sql_ex, sizeof(sql_ex), MYF(MY_NABP|MY_WME)))
return;
data_len = uint4korr(buf);
thread_id = uint4korr(buf+4);
exec_time = uint4korr(buf+8);
skip_lines = uint4korr(buf + 12);
table_name_len = (uint)buf[16];
db_len = (uint)buf[17];
num_fields = uint4korr(buf + 18);
data_len -= LOAD_EVENT_OVERHEAD;
if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
if (my_b_read(file, (byte*)buf, sizeof(buf)) ||
my_b_read(file, (byte*)&sql_ex, sizeof(sql_ex)))
return;
if(my_fread(file, (byte*)data_buf, data_len, MYF(MY_NABP|MY_WME)))
data_len = uint4korr(buf) - LOAD_EVENT_OVERHEAD;
if (!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
return;
if(num_fields > data_len) // simple sanity check against corruption
if (my_b_read(file, (byte*)data_buf, data_len))
return;
field_lens = (uchar*)data_buf;
uint i;
for(i = 0; i < num_fields; i++)
{
field_block_len += (uint)field_lens[i] + 1;
}
fields = (char*)field_lens + num_fields;
*((char*)data_buf+data_len) = 0;
table_name = fields + field_block_len;
db = table_name + table_name_len + 1;
fname = db + db_len + 1;
fname_len = data_len - 2 - db_len - table_name_len - num_fields - field_block_len;
copy_log_event(buf,data_len);
}
Load_log_event::Load_log_event(const char* buf, int max_buf):
Log_event(when,0,0,server_id),data_buf(0),num_fields(0),fields(0),
field_lens(0),field_block_len(0),
table_name(0),db(0),fname(0)
{
ulong data_len;
......@@ -617,9 +567,19 @@ Load_log_event::Load_log_event(const char* buf, int max_buf):
buf += EVENT_LEN_OFFSET;
data_len = uint4korr(buf);
if((uint)data_len > (uint)max_buf)
if ((uint)data_len > (uint) max_buf)
return;
data_len -= LOAD_EVENT_OVERHEAD;
memcpy(&sql_ex, buf + 22, sizeof(sql_ex));
if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
return;
memcpy(data_buf, buf + 22 + sizeof(sql_ex), data_len);
copy_log_event(buf, data_len);
}
void Load_log_event::copy_log_event(const char *buf, ulong data_len)
{
thread_id = uint4korr(buf+4);
exec_time = uint4korr(buf+8);
skip_lines = uint4korr(buf + 12);
......@@ -627,21 +587,12 @@ Load_log_event::Load_log_event(const char* buf, int max_buf):
db_len = (uint)buf[17];
num_fields = uint4korr(buf + 18);
data_len -= LOAD_EVENT_OVERHEAD;
memcpy(&sql_ex, buf + 22, sizeof(sql_ex));
if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
if (num_fields > data_len) // simple sanity check against corruption
return;
memcpy(data_buf, buf + 22 + sizeof(sql_ex), data_len);
if(num_fields > data_len) // simple sanity check against corruption
return;
field_lens = (uchar*)data_buf;
field_lens = (uchar*) data_buf;
uint i;
for(i = 0; i < num_fields; i++)
for (i = 0; i < num_fields; i++)
{
field_block_len += (uint)field_lens[i] + 1;
}
......@@ -651,8 +602,8 @@ Load_log_event::Load_log_event(const char* buf, int max_buf):
table_name = fields + field_block_len;
db = table_name + table_name_len + 1;
fname = db + db_len + 1;
fname_len = data_len - 2 - db_len - table_name_len - num_fields - field_block_len;
fname_len = data_len - 2 - db_len - table_name_len - num_fields -
field_block_len;
}
......@@ -711,7 +662,7 @@ void Load_log_event::print(FILE* file, bool short_form)
if((int)skip_lines > 0)
fprintf(file, " IGNORE %ld LINES ", skip_lines);
if(num_fields)
if (num_fields)
{
uint i;
const char* field = fields;
......
......@@ -65,9 +65,9 @@ public:
int valid_exec_time; // if false, the exec time setting is bogus
uint32 server_id;
int write(FILE* file);
int write_header(FILE* file);
virtual int write_data(FILE* file __attribute__((unused))) { return 0; }
int write(IO_CACHE* file);
int write_header(IO_CACHE* file);
virtual int write_data(IO_CACHE* file __attribute__((unused))) { return 0; }
virtual Log_event_type get_type_code() = 0;
Log_event(time_t when_arg, ulong exec_time_arg = 0,
int valid_exec_time_arg = 0, uint32 server_id = 0): when(when_arg),
......@@ -92,11 +92,11 @@ public:
void print_header(FILE* file);
// if mutex is 0, the read will proceed without mutex
static Log_event* read_log_event(FILE* file, pthread_mutex_t* log_lock);
static Log_event* read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock);
static Log_event* read_log_event(const char* buf, int max_buf);
#ifndef MYSQL_CLIENT
static int read_log_event(FILE* file, String* packet,
static int read_log_event(IO_CACHE* file, String* packet,
pthread_mutex_t* log_lock);
#endif
......@@ -132,18 +132,18 @@ public:
}
#endif
Query_log_event(FILE* file, time_t when, uint32 server_id);
Query_log_event(IO_CACHE* file, time_t when, uint32 server_id);
Query_log_event(const char* buf, int max_buf);
~Query_log_event()
{
if (data_buf)
{
my_free((gptr)data_buf, MYF(0));
my_free((gptr) data_buf, MYF(0));
}
}
Log_event_type get_type_code() { return QUERY_EVENT; }
int write(FILE* file);
int write_data(FILE* file); // returns 0 on success, -1 on error
int write(IO_CACHE* file);
int write_data(IO_CACHE* file); // returns 0 on success, -1 on error
int get_data_size()
{
return q_len + db_len + 2 +
......@@ -183,6 +183,8 @@ class Load_log_event: public Log_event
{
protected:
char* data_buf;
void Load_log_event::copy_log_event(const char *buf, ulong data_len);
public:
int thread_id;
uint32 table_name_len;
......@@ -272,17 +274,17 @@ public:
void set_fields(List<Item> &fields);
#endif
Load_log_event(FILE* file, time_t when, uint32 server_id);
Load_log_event(IO_CACHE * file, time_t when, uint32 server_id);
Load_log_event(const char* buf, int max_buf);
~Load_log_event()
{
if (data_buf)
{
my_free((gptr)data_buf, MYF(0));
my_free((gptr) data_buf, MYF(0));
}
}
Log_event_type get_type_code() { return LOAD_EVENT; }
int write_data(FILE* file); // returns 0 on success, -1 on error
int write_data(IO_CACHE* file); // returns 0 on success, -1 on error
int get_data_size()
{
return table_name_len + 2 + db_len + 2 + fname_len
......@@ -311,30 +313,26 @@ public:
created = (uint32) when;
memcpy(server_version, ::server_version, sizeof(server_version));
}
Start_log_event(FILE* file, time_t when_arg, uint32 server_id) :
Start_log_event(IO_CACHE* file, time_t when_arg, uint32 server_id) :
Log_event(when_arg, 0, 0, server_id)
{
char buf[sizeof(server_version) + sizeof(binlog_version) +
sizeof(created)];
my_fseek(file, 4L, MY_SEEK_CUR, MYF(MY_WME)); // skip the event length
if (my_fread(file, (byte*) buf, sizeof(buf), MYF(MY_NABP | MY_WME)))
sizeof(created)+4];
if (my_b_read(file, (byte*) buf, sizeof(buf)))
return;
binlog_version = uint2korr(buf);
memcpy(server_version, buf + 2, sizeof(server_version));
created = uint4korr(buf + 2 + sizeof(server_version));
binlog_version = uint2korr(buf+4);
memcpy(server_version, buf + 6, sizeof(server_version));
created = uint4korr(buf + 6 + sizeof(server_version));
}
Start_log_event(const char* buf);
~Start_log_event() {}
Log_event_type get_type_code() { return START_EVENT;}
int write_data(FILE* file)
int write_data(IO_CACHE* file)
{
if(my_fwrite(file, (byte*) &binlog_version, sizeof(binlog_version),
MYF(MY_NABP | MY_WME)) ||
my_fwrite(file, (byte*) server_version, sizeof(server_version),
MYF(MY_NABP | MY_WME)) ||
my_fwrite(file, (byte*) &created, sizeof(created),
MYF(MY_NABP | MY_WME)))
if (my_b_write(file, (byte*) &binlog_version, sizeof(binlog_version)) ||
my_b_write(file, (byte*) server_version, sizeof(server_version)) ||
my_b_write(file, (byte*) &created, sizeof(created)))
return -1;
return 0;
}
......@@ -354,12 +352,12 @@ public:
Intvar_log_event(uchar type_arg, ulonglong val_arg)
:Log_event(time(NULL)),val(val_arg),type(type_arg)
{}
Intvar_log_event(FILE* file, time_t when, uint32 server_id);
Intvar_log_event(IO_CACHE* file, time_t when, uint32 server_id);
Intvar_log_event(const char* buf);
~Intvar_log_event() {}
Log_event_type get_type_code() { return INTVAR_EVENT;}
int get_data_size() { return sizeof(type) + sizeof(val);}
int write_data(FILE* file);
int write_data(IO_CACHE* file);
void print(FILE* file, bool short_form = 0);
......@@ -370,10 +368,11 @@ class Stop_log_event: public Log_event
public:
Stop_log_event() :Log_event(time(NULL))
{}
Stop_log_event(FILE* file, time_t when_arg, uint32 server_id):
Stop_log_event(IO_CACHE* file, time_t when_arg, uint32 server_id):
Log_event(when_arg,0,0,server_id)
{
my_fseek(file, 4L, MY_SEEK_CUR, MYF(MY_WME)); // skip the event length
char skip[4];
my_b_read(file, skip, sizeof(skip)); // skip the event length
}
Stop_log_event(const char* buf):Log_event(buf)
{
......@@ -397,7 +396,7 @@ public:
alloced(0)
{}
Rotate_log_event(FILE* file, time_t when, uint32 server_id) ;
Rotate_log_event(IO_CACHE* file, time_t when, uint32 server_id) ;
Rotate_log_event(const char* buf, int max_buf);
~Rotate_log_event()
{
......@@ -406,7 +405,7 @@ public:
}
Log_event_type get_type_code() { return ROTATE_EVENT;}
int get_data_size() { return ident_len;}
int write_data(FILE* file);
int write_data(IO_CACHE* file);
void print(FILE* file, bool short_form = 0);
};
......
......@@ -32,17 +32,19 @@
#define MAP_TO_USE_RAID
#include "mysql_priv.h"
#include <mysys_err.h>
#ifdef HAVE_AIOWAIT
#include <mysys_err.h>
#include <errno.h>
static void my_aiowait(my_aio_result *result);
#endif
/* if cachesize == 0 then use default cachesize (from s-file) */
/* returns 0 if we have enough memory */
extern "C" {
/*
** if cachesize == 0 then use default cachesize (from s-file)
** if file == -1 then real_open_cached_file() will be called.
** returns 0 if ok
*/
int init_io_cache(IO_CACHE *info, File file, uint cachesize,
enum cache_type type, my_off_t seek_offset,
......@@ -60,6 +62,12 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
if (type == READ_CACHE)
{ /* Assume file isn't growing */
if (cache_myflags & MY_DONT_CHECK_FILESIZE)
{
cache_myflags &= ~MY_DONT_CHECK_FILESIZE;
}
else
{
my_off_t file_pos,end_of_file;
if ((file_pos=my_tell(file,MYF(0)) == MY_FILEPOS_ERROR))
DBUG_RETURN(1);
......@@ -73,7 +81,7 @@ int init_io_cache(IO_CACHE *info, File file, uint cachesize,
use_async_io=0; /* No nead to use async */
}
}
}
if ((int) type < (int) READ_NET)
{
for (;;)
......@@ -167,7 +175,8 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
DBUG_ENTER("reinit_io_cache");
info->seek_not_done= test(info->file >= 0); /* Seek not done */
if (!clear_cache && seek_offset >= info->pos_in_file &&
if (! clear_cache &&
seek_offset >= info->pos_in_file &&
seek_offset <= info->pos_in_file +
(uint) (info->rc_end - info->rc_request_pos))
{ /* use current buffer */
......@@ -231,6 +240,7 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count)
{
uint length,diff_length,left_length;
my_off_t max_length, pos_in_file;
memcpy(Buffer,info->rc_pos,
(size_t) (left_length=(uint) (info->rc_end-info->rc_pos)));
Buffer+=left_length;
......@@ -607,7 +617,9 @@ int flush_io_cache(IO_CACHE *info)
length=(uint) (info->rc_pos - info->buffer);
if (info->seek_not_done)
{ /* File touched, do seek */
VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)));
if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)) ==
MY_FILEPOS_ERROR)
DBUG_RETURN((info->error= -1));
info->seek_not_done=0;
}
info->rc_pos=info->buffer;
......@@ -644,4 +656,4 @@ int end_io_cache(IO_CACHE *info)
DBUG_RETURN(error);
} /* end_io_cache */
}
} /* extern "C" */
......@@ -55,7 +55,7 @@ static struct option long_options[] =
{"password", required_argument,0, 'p'},
{"position", required_argument,0, 'j'},
#ifndef DBUG_OFF
{"debug", required_argument, 0, '#'}
{"debug", optional_argument, 0, '#'}
#endif
};
......@@ -151,7 +151,7 @@ static int parse_args(int *argc, char*** argv)
{
int c, opt_index = 0;
while((c = getopt_long(*argc, *argv, "so:#:h:j:u:p:P:t:?", long_options,
while((c = getopt_long(*argc, *argv, "so:#::h:j:u:p:P:t:?", long_options,
&opt_index)) != EOF)
{
switch(c)
......@@ -310,24 +310,43 @@ Unfortunately, no sweepstakes today, adjusted position to 4\n");
static void dump_local_log_entries(const char* logname)
{
FILE* file;
File fd;
IO_CACHE cache,*file= &cache;
int rec_count = 0;
if(logname && logname[0] != '-')
file = my_fopen(logname, O_RDONLY|O_BINARY, MYF(MY_WME));
if (logname && logname[0] != '-')
{
if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
exit(1);
if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0,
MYF(MY_WME | MY_NABP)))
exit(1);
}
else
file = stdin;
if(!file)
die("Could not open log file %s", logname);
if(my_fseek(file, position, MY_SEEK_SET, MYF(MY_WME)))
die("failed on my_fseek()");
{
if (init_io_cache(file, fileno(stdout), 0, READ_CACHE, (my_off_t) 0,
0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
exit(1);
if (position)
{
/* skip 'position' characters from stdout */
char buff[IO_SIZE];
my_off_t length,tmp;
for (length=position ; length > 0 ; length-=tmp)
{
tmp=min(length,sizeof(buff));
if (my_b_read(file,buff,tmp))
exit(1);
}
}
file->pos_in_file=position;
file->seek_not_done=0;
}
if(!position)
if (!position)
{
char magic[4];
if (my_fread(file, (byte*) magic, sizeof(magic), MYF(MY_NABP|MY_WME)))
if (my_b_read(file, (byte*) magic, sizeof(magic)))
die("I/O error reading binlog magic number");
if(memcmp(magic, BINLOG_MAGIC, 4))
die("Bad magic number");
......@@ -336,23 +355,24 @@ static void dump_local_log_entries(const char* logname)
while(1)
{
Log_event* ev = Log_event::read_log_event(file, 0);
if(!ev)
if(!feof(file))
if (!ev)
{
if (file->error)
die("Could not read entry at offset %ld : Error in log format or \
read error",
my_ftell(file, MYF(MY_WME)));
else
my_b_tell(file));
break;
if(rec_count >= offset)
}
if (rec_count >= offset)
ev->print(stdout, short_form);
rec_count++;
delete ev;
}
my_fclose(file, MYF(MY_WME));
my_close(fd, MYF(MY_WME));
end_io_cache(file);
}
int main(int argc, char** argv)
{
MY_INIT(argv[0]);
......@@ -370,7 +390,7 @@ int main(int argc, char** argv)
mysql = safe_connect();
}
if(table)
if (table)
{
if(!use_remote)
die("You must specify connection parameter to get table dump");
......@@ -382,14 +402,14 @@ int main(int argc, char** argv)
dump_remote_table(&mysql->net, db, tbl);
}
else
{
while(--argc >= 0)
{
dump_log_entries(*(argv++));
}
if(use_remote)
}
if (use_remote)
mc_mysql_close(mysql);
return 0;
}
......
......@@ -113,11 +113,17 @@ THD::THD()
ull=0;
system_thread=0;
bzero((char*) &mem_root,sizeof(mem_root));
#if defined(HAVE_BDB) || defined(HAVE_INNOBASE) || defined(HAVE_GEMENI)
if (open_cached_file(&transactions.trans_log,
mysql_tempdir,LOG_PREFIX,0,MYF(MY_WME)))
killed=1;
transaction.bdb_lock_count=0;
#endif
transaction.bdb_tid=0;
#ifdef __WIN__
real_id = 0 ;
#endif
transaction.bdb_lock_count=0;
transaction.bdb_tid=0;
}
THD::~THD()
......@@ -136,6 +142,9 @@ THD::~THD()
close_thread_tables(this);
}
close_temporary_tables(this);
#if defined(HAVE_BDB) || defined(HAVE_INNOBASE) || defined(HAVE_GEMENI)
close_cached_file(transactions.trans_log);
#endif
if (global_read_lock)
{
pthread_mutex_lock(&LOCK_open);
......
......@@ -82,16 +82,16 @@ typedef struct st_master_info
} MASTER_INFO;
class MYSQL_LOG {
public:
private:
pthread_mutex_t LOCK_log, LOCK_index;
File file, index_file;
time_t last_time,query_start;
IO_CACHE log_file;
File index_file;
char *name;
enum_log_type log_type;
volatile enum_log_type log_type;
char time_buff[20],db[NAME_LEN+1];
char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN];
bool write_error,inited;
bool write_error,inited,opened;
bool no_rotate; // for binlog - if log name can never change
// we should not try to rotate it or write any rotation events
// the user should use FLUSH MASTER instead of FLUSH LOGS for
......@@ -114,7 +114,7 @@ public:
int generate_new_name(char *new_name,const char *old_name);
void make_log_name(char* buf, const char* log_ident);
bool is_active(const char* log_file_name);
void flush(void);
// void flush(void);
void close(bool exiting = 0); // if we are exiting, we also want to close the
// index file
......@@ -270,6 +270,7 @@ public:
thr_lock_type update_lock_default;
delayed_insert *di;
struct st_transactions {
IO_CACHE trans_log;
void *bdb_tid;
uint bdb_lock_count;
} transaction;
......
......@@ -2439,7 +2439,7 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables)
bool result=0;
select_errors=0; /* Write if more errors */
mysql_log.flush(); // Flush log
// mysql_log.flush(); // Flush log
if (options & REFRESH_GRANT)
{
acl_reload();
......
......@@ -93,54 +93,74 @@ static int send_file(THD *thd)
DBUG_RETURN(error);
}
static File open_log(IO_CACHE *log, const char *log_file_name,
const char **errmsg)
{
File file;
char magic[4];
if ((file = my_open(log_file_name, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0 ||
init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0,
MYF(MY_WME)))
{
*errmsg = "Could not open log file"; // This will not be sent
goto err;
}
if (my_b_read(log, (byte*) magic, sizeof(magic)))
{
*errmsg = "I/O error reading binlog magic number";
goto err;
}
if (memcmp(magic, BINLOG_MAGIC, 4))
{
*errmsg = "Binlog has bad magic number, fire your magician";
goto err;
}
return file;
err:
if (file > 0)
my_close(file,MYF(0));
end_io_cache(log);
return -1;
}
void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags)
{
LOG_INFO linfo;
char *log_file_name = linfo.log_file_name;
char search_file_name[FN_REFLEN];
char magic[4];
FILE* log = NULL;
IO_CACHE log;
File file = -1;
String* packet = &thd->packet;
int error;
const char *errmsg = "Unknown error";
NET* net = &thd->net;
DBUG_ENTER("mysql_binlog_send");
bzero((char*) &log,sizeof(log));
if(!mysql_bin_log.is_open())
{
errmsg = "Binary log is not open";
goto err;
}
if(log_ident[0])
if (log_ident[0])
mysql_bin_log.make_log_name(search_file_name, log_ident);
else
search_file_name[0] = 0;
if(mysql_bin_log.find_first_log(&linfo, search_file_name))
if (mysql_bin_log.find_first_log(&linfo, search_file_name))
{
errmsg = "Could not find first log";
goto err;
}
log = my_fopen(log_file_name, O_RDONLY|O_BINARY, MYF(MY_WME));
if(!log)
{
errmsg = "Could not open log file";
goto err;
}
if(my_fread(log, (byte*) magic, sizeof(magic), MYF(MY_NABP|MY_WME)))
{
errmsg = "I/O error reading binlog magic number";
goto err;
}
if(memcmp(magic, BINLOG_MAGIC, 4))
{
errmsg = "Binlog has bad magic number, fire your magician";
if ((file=open_log(&log, log_file_name, &errmsg)) < 0)
goto err;
}
if(pos < 4)
{
......@@ -149,13 +169,7 @@ sweepstakes if you report the bug";
goto err;
}
if(my_fseek(log, pos, MY_SEEK_SET, MYF(MY_WME)) == MY_FILEPOS_ERROR )
{
errmsg = "Error on fseek()";
goto err;
}
my_b_seek(&log, pos); // Seek will done on next read
packet->length(0);
packet->append("\0", 1);
// we need to start a packet with something other than 255
......@@ -165,7 +179,7 @@ sweepstakes if you report the bug";
{
pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock();
while(!(error = Log_event::read_log_event(log, packet, log_lock)))
while (!(error = Log_event::read_log_event(&log, packet, log_lock)))
{
if(my_net_write(net, (char*)packet->ptr(), packet->length()) )
{
......@@ -221,7 +235,7 @@ sweepstakes if you report the bug";
// if we did not miss anything, we just wait for other threads
// to signal us
{
clearerr(log);
log.error=0;
// tell the kill thread how to wake us up
pthread_mutex_lock(&thd->mysys_var->mutex);
......@@ -235,7 +249,7 @@ sweepstakes if you report the bug";
// no one will update the log while we are reading
// now, but we'll be quick and just read one record
switch(Log_event::read_log_event(log, packet, log_lock))
switch(Log_event::read_log_event(&log, packet, log_lock))
{
case 0:
read_packet = 1;
......@@ -288,8 +302,7 @@ sweepstakes if you report the bug";
errmsg = "error reading log entry";
goto err;
}
clearerr(log);
log.error=0;
}
}
else
......@@ -312,25 +325,10 @@ sweepstakes if you report the bug";
if(loop_breaker)
break;
(void) my_fclose(log, MYF(MY_WME));
log = my_fopen(log_file_name, O_RDONLY|O_BINARY, MYF(MY_WME));
if(!log)
{
errmsg = "Could not open next log";
end_io_cache(&log);
(void) my_close(file, MYF(MY_WME));
if ((file=open_log(&log, log_file_name, &errmsg)) < 0)
goto err;
}
//check the magic
if(my_fread(log, (byte*) magic, sizeof(magic), MYF(MY_NABP|MY_WME)))
{
errmsg = "I/O error reading binlog magic number";
goto err;
}
if(memcmp(magic, BINLOG_MAGIC, 4))
{
errmsg = "Binlog has bad magic number, fire your magician";
goto err;
}
// fake Rotate_log event just in case it did not make it to the log
// otherwise the slave make get confused about the offset
......@@ -362,15 +360,17 @@ sweepstakes if you report the bug";
}
}
(void)my_fclose(log, MYF(MY_WME));
end_io_cache(&log);
(void)my_close(file, MYF(MY_WME));
send_eof(&thd->net);
thd->proc_info = "waiting to finalize termination";
DBUG_VOID_RETURN;
err:
thd->proc_info = "waiting to finalize termination";
if(log)
(void) my_fclose(log, MYF(MY_WME));
end_io_cache(&log);
if (file >= 0)
(void) my_close(file, MYF(MY_WME));
send_error(&thd->net, 0, errmsg);
DBUG_VOID_RETURN;
}
......
......@@ -95,17 +95,6 @@ bool String::realloc(uint32 alloc_length)
return FALSE;
}
#ifdef NOT_NEEDED
bool String::set(long num)
{
if (alloc(14))
return TRUE;
str_length=(uint32) (int10_to_str(num,Ptr,-10)-Ptr);
return FALSE;
}
#endif
bool String::set(longlong num)
{
if (alloc(21))
......@@ -274,6 +263,7 @@ bool String::append(const char *s,uint32 arg_length)
return FALSE;
}
#ifdef TO_BE_REMOVED
bool String::append(FILE* file, uint32 arg_length, myf my_flags)
{
if (realloc(str_length+arg_length))
......@@ -286,6 +276,20 @@ bool String::append(FILE* file, uint32 arg_length, myf my_flags)
str_length+=arg_length;
return FALSE;
}
#endif
bool String::append(IO_CACHE* file, uint32 arg_length)
{
if (realloc(str_length+arg_length))
return TRUE;
if (my_b_read(file, (byte*) Ptr + str_length, arg_length))
{
shrink(str_length);
return TRUE;
}
str_length+=arg_length;
return FALSE;
}
uint32 String::numchars()
{
......
......@@ -152,7 +152,7 @@ public:
bool copy(const char *s,uint32 arg_length); // Allocate new string
bool append(const String &s);
bool append(const char *s,uint32 arg_length=0);
bool append(FILE* file, uint32 arg_length, myf my_flags);
bool append(IO_CACHE* file, uint32 arg_length);
int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
bool replace(uint32 offset,uint32 arg_length,const String &to);
......
......@@ -413,16 +413,6 @@ ulong convert_month_to_period(ulong month)
return year*100+month%12+1;
}
#ifdef NOT_NEEDED
ulong add_to_period(ulong period,int months)
{
if (period == 0L)
return 0L;
return convert_month_to_period(convert_period_to_month(period)+months);
}
#endif
/*****************************************************************************
** convert a timestamp string to a TIME value.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment