log.cc 25 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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; either version 2 of the License, or
   (at your option) any later version.
   
   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 */


/* logging of commands */
19
/* TODO: Abort logging when we get an error in reading or writing log files */
unknown's avatar
unknown committed
20

21 22 23 24
#ifdef __EMX__
#include <io.h>
#endif

unknown's avatar
unknown committed
25 26
#include "mysql_priv.h"
#include "sql_acl.h"
unknown's avatar
unknown committed
27
#include "sql_repl.h"
unknown's avatar
unknown committed
28 29 30 31 32 33 34

#include <my_dir.h>
#include <stdarg.h>
#include <m_ctype.h>				// For test_if_number

MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
extern I_List<i_string> binlog_do_db, binlog_ignore_db;
35
extern ulong max_binlog_size;
unknown's avatar
unknown committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

static bool test_if_number(const char *str,
			   long *res, bool allow_wildcards);

/****************************************************************************
** Find a uniq filename for 'filename.#'.
** Set # to a number as low as possible
** returns != 0 if not possible to get uniq filename
****************************************************************************/

static int find_uniq_filename(char *name)
{
  long		number;
  uint		i,length;
  char		buff[FN_REFLEN];
  struct st_my_dir *dir_info;
  reg1 struct fileinfo *file_info;
  ulong		max_found=0;
  DBUG_ENTER("find_uniq_filename");

  length=dirname_part(buff,name);
  char *start=name+length,*end=strend(start);
  *end='.';
unknown's avatar
unknown committed
59
  length= (uint) (end-start+1);
unknown's avatar
unknown committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

  if (!(dir_info = my_dir(buff,MYF(MY_DONT_SORT))))
  {						// This shouldn't happen
    strmov(end,".1");				// use name+1
    DBUG_RETURN(0);
  }
  file_info= dir_info->dir_entry;
  for (i=dir_info->number_off_files ; i-- ; file_info++)
  {
    if (bcmp(file_info->name,start,length) == 0 &&
	test_if_number(file_info->name+length, &number,0))
    {
      set_if_bigger(max_found,(ulong) number);
    }
  }
  my_dirend(dir_info);

  *end++='.';
  sprintf(end,"%03ld",max_found+1);
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
82
MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),index_file(-1),
83
			name(0), log_type(LOG_CLOSED),write_error(0),
84
			inited(0), no_rotate(0)
unknown's avatar
unknown committed
85 86 87 88 89
{
  /*
    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'.
  */
90 91
  index_file_name[0] = 0;
  bzero((char*) &log_file,sizeof(log_file));
unknown's avatar
unknown committed
92 93 94 95 96
}

MYSQL_LOG::~MYSQL_LOG()
{
  if (inited)
97 98 99 100
  {
    (void) pthread_mutex_destroy(&LOCK_log);
    (void) pthread_mutex_destroy(&LOCK_index);
  }
unknown's avatar
unknown committed
101 102 103 104 105
}

void MYSQL_LOG::set_index_file_name(const char* index_file_name)
{
  if (index_file_name)
106
    fn_format(this->index_file_name,index_file_name,mysql_data_home,".index",
unknown's avatar
unknown committed
107 108 109 110 111
	      4);
  else
    this->index_file_name[0] = 0;
}

112

unknown's avatar
unknown committed
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
{      
  if (log_type == LOG_NORMAL)
    fn_format(new_name,log_name,mysql_data_home,"",4);
  else
  {
    fn_format(new_name,log_name,mysql_data_home,"",4);
    if (!fn_ext(log_name)[0])
    {
      if (find_uniq_filename(new_name))
      {
	sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
	return 1;
      }
    }
  }
  return 0;
}

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
bool MYSQL_LOG::open_index( int options)
{
  return (index_file < 0 && 
	 (index_file = my_open(index_file_name, options | O_BINARY ,
			       MYF(MY_WME))) < 0);
}

void MYSQL_LOG::init(enum_log_type log_type_arg)
{
  log_type = log_type_arg;
  if (!inited)
  {
    inited=1;
    (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW);
    (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW);
  }
}

void MYSQL_LOG::close_index()
{
  if(index_file >= 0)
    {
      my_close(index_file, MYF(0));
      index_file = -1;
    }
}
unknown's avatar
unknown committed
158 159 160 161

void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
		     const char *new_name)
{
162 163
  MY_STAT tmp_stat;
  char buff[512];
164 165
  File file= -1;
  bool do_magic;
166 167
  
  if (!inited && log_type_arg == LOG_BIN && *fn_ext(log_name))
unknown's avatar
unknown committed
168
      no_rotate = 1;
169
  init(log_type_arg);
170 171 172
  
  if (!(name=my_strdup(log_name,MYF(MY_WME))))
    goto err;
unknown's avatar
unknown committed
173 174 175
  if (new_name)
    strmov(log_file_name,new_name);
  else if (generate_new_name(log_file_name, name))
176
    goto err;
unknown's avatar
unknown committed
177 178 179 180 181

  if (log_type == LOG_BIN && !index_file_name[0])
    fn_format(index_file_name, name, mysql_data_home, ".index", 6);
  
  db[0]=0;
182 183
  do_magic = ((log_type == LOG_BIN) && !my_stat(log_file_name,
						&tmp_stat, MYF(0)));
unknown's avatar
unknown committed
184
  
unknown's avatar
unknown committed
185
  if ((file=my_open(log_file_name,O_CREAT | O_APPEND | O_WRONLY | O_BINARY,
186 187 188 189
		    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;
unknown's avatar
unknown committed
190 191 192

  if (log_type == LOG_NORMAL)
  {
193
    char *end;
unknown's avatar
unknown committed
194
#ifdef __NT__
195
    sprintf(buff, "%s, Version: %s, started with:\nTCP Port: %d, Named Pipe: %s\n", my_progname, server_version, mysql_port, mysql_unix_port);
unknown's avatar
unknown committed
196
#else
197
    sprintf(buff, "%s, Version: %s, started with:\nTcp port: %d  Unix socket: %s\n", my_progname,server_version,mysql_port,mysql_unix_port);
unknown's avatar
unknown committed
198
#endif
199
    end=strmov(strend(buff),"Time                 Id Command    Argument\n");
unknown's avatar
unknown committed
200
    if (my_b_write(&log_file, (byte*) buff,(uint) (end-buff)) ||
201 202
	flush_io_cache(&log_file))
      goto err;
unknown's avatar
unknown committed
203 204 205 206 207 208
  }
  else if (log_type == LOG_NEW)
  {
    time_t skr=time(NULL);
    struct tm tm_tmp;
    localtime_r(&skr,&tm_tmp);
209
    sprintf(buff,"# %s, Version: %s at %02d%02d%02d %2d:%02d:%02d\n",
unknown's avatar
unknown committed
210 211 212 213 214 215 216
	    my_progname,server_version,
	    tm_tmp.tm_year % 100,
	    tm_tmp.tm_mon+1,
	    tm_tmp.tm_mday,
	    tm_tmp.tm_hour,
	    tm_tmp.tm_min,
	    tm_tmp.tm_sec);
unknown's avatar
unknown committed
217
    if (my_b_write(&log_file, (byte*) buff,(uint) strlen(buff)) ||
218 219
	flush_io_cache(&log_file))
      goto err;
unknown's avatar
unknown committed
220 221 222
  }
  else if (log_type == LOG_BIN)
  {
unknown's avatar
unknown committed
223 224 225 226 227 228 229
    /*
      Explanation of the boolean black magic:
      if we are supposed to write magic number try write
      clean up if failed
      then if index_file has not been previously opened, try to open it
      clean up if failed
    */
230
    if ((do_magic && my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4)) ||
231
	open_index(O_APPEND | O_RDWR | O_CREAT))
232
      goto err;
unknown's avatar
unknown committed
233
    Start_log_event s;
unknown's avatar
unknown committed
234
    bool error;
235
    s.write(&log_file);
unknown's avatar
unknown committed
236
    flush_io_cache(&log_file);
unknown's avatar
unknown committed
237
    pthread_mutex_lock(&LOCK_index);
unknown's avatar
unknown committed
238
    error=(my_write(index_file, (byte*) log_file_name, strlen(log_file_name),
unknown's avatar
unknown committed
239
		    MYF(MY_NABP | MY_WME)) ||
unknown's avatar
unknown committed
240
	   my_write(index_file, (byte*) "\n", 1, MYF(MY_NABP | MY_WME)));
unknown's avatar
unknown committed
241
    pthread_mutex_unlock(&LOCK_index);
unknown's avatar
unknown committed
242 243
    if (error)
    {
244
      close_index();
unknown's avatar
unknown committed
245 246
      goto err;
    }
unknown's avatar
unknown committed
247
  }
248 249 250
  return;

err:
unknown's avatar
unknown committed
251
  sql_print_error("Could not use %s for logging (error %d)", log_name,errno);
252 253 254 255 256 257 258 259
  if (file >= 0)
    my_close(file,MYF(0));
  end_io_cache(&log_file);
  x_free(name); name=0;
  log_type=LOG_CLOSED;

  return;
  
unknown's avatar
unknown committed
260 261 262 263 264
}

int MYSQL_LOG::get_current_log(LOG_INFO* linfo)
{
  pthread_mutex_lock(&LOCK_log);
265 266
  strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1);
  linfo->pos = my_b_tell(&log_file);
unknown's avatar
unknown committed
267 268 269 270 271 272 273
  pthread_mutex_unlock(&LOCK_log);
  return 0;
}

// if log_name is "" we stop at the first entry
int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name)
{
274 275
  if (index_file < 0)
    return LOG_INFO_INVALID;
unknown's avatar
unknown committed
276 277
  int error = 0;
  char* fname = linfo->log_file_name;
unknown's avatar
unknown committed
278
  uint log_name_len = (uint) strlen(log_name);
279
  IO_CACHE io_cache;
unknown's avatar
unknown committed
280

281 282
  // mutex needed because we need to make sure the file pointer does not move
  // from under our feet
unknown's avatar
unknown committed
283
  pthread_mutex_lock(&LOCK_index);
284 285
  if (init_io_cache(&io_cache, index_file, IO_SIZE, READ_CACHE, (my_off_t) 0,
		    0, MYF(MY_WME)))
286 287 288 289 290 291
  {
    error = LOG_INFO_SEEK;
    goto err;
  }
  for(;;)
  {
292
    uint length;
unknown's avatar
unknown committed
293
    if (!(length=my_b_gets(&io_cache, fname, FN_REFLEN-1)))
unknown's avatar
unknown committed
294
    {
295
      error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO;
unknown's avatar
unknown committed
296 297 298
      goto err;
    }

299 300
    // if the log entry matches, empty string matching anything
    if (!log_name_len ||
unknown's avatar
unknown committed
301
	(log_name_len == length-1 && fname[log_name_len] == '\n' &&
302
	 !memcmp(fname, log_name, log_name_len)))
unknown's avatar
unknown committed
303
    {
304 305
      fname[length-1]=0;			// remove last \n
      linfo->index_file_offset = my_b_tell(&io_cache);
306
      break;
unknown's avatar
unknown committed
307
    }
308
  }
unknown's avatar
unknown committed
309
  error = 0;
310

unknown's avatar
unknown committed
311 312
err:
  pthread_mutex_unlock(&LOCK_index);
313
  end_io_cache(&io_cache);
unknown's avatar
unknown committed
314 315 316
  return error;
     
}
unknown's avatar
unknown committed
317

318

unknown's avatar
unknown committed
319 320 321 322
int MYSQL_LOG::find_next_log(LOG_INFO* linfo)
{
  // mutex needed because we need to make sure the file pointer does not move
  // from under our feet
unknown's avatar
unknown committed
323
  if (index_file < 0) return LOG_INFO_INVALID;
unknown's avatar
unknown committed
324 325
  int error = 0;
  char* fname = linfo->log_file_name;
326 327
  IO_CACHE io_cache;
  uint length;
unknown's avatar
unknown committed
328

329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
  pthread_mutex_lock(&LOCK_index);
  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 (!(length=my_b_gets(&io_cache, fname, FN_REFLEN)))
  {
    error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO;
    goto err;
  }
  fname[length-1]=0;				// kill /n
  linfo->index_file_offset = my_b_tell(&io_cache);
unknown's avatar
unknown committed
344
  error = 0;
345

unknown's avatar
unknown committed
346 347
err:
  pthread_mutex_unlock(&LOCK_index);
348
  end_io_cache(&io_cache);
unknown's avatar
unknown committed
349 350 351
  return error;
}

unknown's avatar
merge  
unknown committed
352
 
unknown's avatar
unknown committed
353 354
int MYSQL_LOG::purge_logs(THD* thd, const char* to_log)
{
355 356
  if (index_file < 0) return LOG_INFO_INVALID;
  if (no_rotate) return LOG_INFO_PURGE_NO_ROTATE;
unknown's avatar
unknown committed
357 358
  int error;
  char fname[FN_REFLEN];
unknown's avatar
unknown committed
359
  char *p;
unknown's avatar
unknown committed
360 361 362 363
  uint fname_len, i;
  bool logs_to_purge_inited = 0, logs_to_keep_inited = 0, found_log = 0;
  DYNAMIC_ARRAY logs_to_purge, logs_to_keep;
  my_off_t purge_offset ;
unknown's avatar
unknown committed
364
  LINT_INIT(purge_offset);
365
  IO_CACHE io_cache;
unknown's avatar
unknown committed
366
  
unknown's avatar
unknown committed
367 368
  pthread_mutex_lock(&LOCK_index);
  
369 370 371 372 373 374 375 376 377 378 379
  if (init_io_cache(&io_cache,index_file, IO_SIZE*2, READ_CACHE, (my_off_t) 0,
		    0, MYF(MY_WME)))
  {
    error = LOG_INFO_MEM;
    goto err;
  }
  if (init_dynamic_array(&logs_to_purge, sizeof(char*), 1024, 1024))
  {
    error = LOG_INFO_MEM;
    goto err;
  }
unknown's avatar
unknown committed
380 381
  logs_to_purge_inited = 1;
  
382 383 384 385 386
  if (init_dynamic_array(&logs_to_keep, sizeof(char*), 1024, 1024))
  {
    error = LOG_INFO_MEM;
    goto err;
  }
unknown's avatar
unknown committed
387 388 389 390
  logs_to_keep_inited = 1;

  
  for(;;)
391 392 393
  {
    my_off_t init_purge_offset= my_b_tell(&io_cache);
    if (!(fname_len=my_b_gets(&io_cache, fname, FN_REFLEN)))
unknown's avatar
unknown committed
394
    {
395 396 397 398 399 400 401 402 403 404 405 406
      if(!io_cache.error)
	break;
      error = LOG_INFO_IO;
      goto err;
    }

    fname[--fname_len]=0;			// kill \n
    if(!memcmp(fname, to_log, fname_len + 1 ))
    {
      found_log = 1;
      purge_offset = init_purge_offset;
    }
unknown's avatar
unknown committed
407
      
408 409 410 411 412 413
    // if one of the logs before the target is in use
    if(!found_log && log_in_use(fname))
    {
      error = LOG_INFO_IN_USE;
      goto err;
    }
unknown's avatar
unknown committed
414
      
unknown's avatar
unknown committed
415
    if (!(p = sql_memdup(fname, fname_len+1)) ||
416 417
	insert_dynamic(found_log ? &logs_to_keep : &logs_to_purge,
		       (gptr) &p))
unknown's avatar
unknown committed
418
    {
419
      error = LOG_INFO_MEM;
unknown's avatar
unknown committed
420 421
      goto err;
    }
422 423 424 425 426 427 428 429
  }
  
  end_io_cache(&io_cache);
  if(!found_log)
  {
    error = LOG_INFO_EOF;
    goto err;
  }
unknown's avatar
unknown committed
430 431
  
  for(i = 0; i < logs_to_purge.elements; i++)
432 433 434 435 436 437
  {
    char* l;
    get_dynamic(&logs_to_purge, (gptr)&l, i);
    if (my_delete(l, MYF(MY_WME)))
      sql_print_error("Error deleting %s during purge", l);
  }
unknown's avatar
unknown committed
438 439 440 441
  
  // if we get killed -9 here, the sysadmin would have to do a small
  // vi job on the log index file after restart - otherwise, this should
  // be safe
442 443 444
#ifdef HAVE_FTRUNCATE
  if (ftruncate(index_file,0))
  {
unknown's avatar
unknown committed
445
    sql_print_error("Could not truncate the binlog index file \
446 447 448 449 450 451 452 453
during log purge for write");
    error = LOG_INFO_FATAL;
    goto err;
  }
  my_seek(index_file, 0, MY_SEEK_CUR,MYF(MY_WME));
#else
  my_close(index_file, MYF(MY_WME));
  my_delete(index_file_name, MYF(MY_WME));
unknown's avatar
unknown committed
454
  if(!(index_file = my_open(index_file_name,
unknown's avatar
unknown committed
455
			    O_CREAT | O_BINARY | O_RDWR | O_APPEND,
456
			    MYF(MY_WME))))
unknown's avatar
unknown committed
457
  {
unknown's avatar
unknown committed
458
    sql_print_error("Could not re-open the binlog index file \
unknown's avatar
unknown committed
459 460 461 462
during log purge for write");
    error = LOG_INFO_FATAL;
    goto err;
  }
463
#endif
unknown's avatar
unknown committed
464 465
  
  for(i = 0; i < logs_to_keep.elements; i++)
466 467 468
  {
    char* l;
    get_dynamic(&logs_to_keep, (gptr)&l, i);
unknown's avatar
unknown committed
469 470
    if (my_write(index_file, (byte*) l, strlen(l), MYF(MY_WME|MY_NABP)) ||
	my_write(index_file, (byte*) "\n", 1, MYF(MY_WME|MY_NABP)))
unknown's avatar
unknown committed
471
    {
472 473
      error = LOG_INFO_FATAL;
      goto err;
unknown's avatar
unknown committed
474 475
    }
  }
476

unknown's avatar
unknown committed
477 478 479
  // now update offsets
  adjust_linfo_offsets(purge_offset);
  error = 0;
480

unknown's avatar
unknown committed
481 482 483 484 485 486
err:
  pthread_mutex_unlock(&LOCK_index);
  if(logs_to_purge_inited)
    delete_dynamic(&logs_to_purge);
  if(logs_to_keep_inited)
    delete_dynamic(&logs_to_keep);
487
  end_io_cache(&io_cache);
unknown's avatar
unknown committed
488 489 490
  return error;
}

491

unknown's avatar
unknown committed
492 493 494
// we assume that buf has at least FN_REFLEN bytes alloced
void MYSQL_LOG::make_log_name(char* buf, const char* log_ident)
{
495
  buf[0] = 0;					// In case of error
496
  if (inited)
497 498 499 500 501
  {
    int dir_len = dirname_length(log_file_name); 
    int ident_len = (uint) strlen(log_ident);
    if (dir_len + ident_len + 1 > FN_REFLEN)
      return; // protection agains malicious buffer overflow
unknown's avatar
unknown committed
502
      
503 504 505 506
    memcpy(buf, log_file_name, dir_len);
    // copy filename + end null
    memcpy(buf + dir_len, log_ident, ident_len + 1);
  }
unknown's avatar
unknown committed
507 508 509 510 511 512 513 514 515
}

bool MYSQL_LOG::is_active(const char* log_file_name)
{
  return inited && !strcmp(log_file_name, this->log_file_name);
}

void MYSQL_LOG::new_file()
{
516 517 518
  // only rotate open logs that are marked non-rotatable
  // (binlog with constant name are non-rotatable)
  if (is_open() && ! no_rotate)
unknown's avatar
unknown committed
519 520 521 522
  {
    char new_name[FN_REFLEN], *old_name=name;
    VOID(pthread_mutex_lock(&LOCK_log));
    if (generate_new_name(new_name, name))
523 524
    {
      VOID(pthread_mutex_unlock(&LOCK_log));
unknown's avatar
unknown committed
525
      return;					// Something went wrong
526
    }
unknown's avatar
unknown committed
527 528 529 530 531 532 533
    if (log_type == LOG_BIN)
    {
      /*
	We log the whole file name for log file as the user may decide
	to change base names at some point.
      */
      Rotate_log_event r(new_name+dirname_length(new_name));
534
      r.write(&log_file);
unknown's avatar
unknown committed
535 536 537 538 539 540 541 542 543 544 545 546 547
      VOID(pthread_cond_broadcast(&COND_binlog_update));
    }
    name=0;
    close();
    open(old_name, log_type, new_name);
    my_free(old_name,MYF(0));
    last_time=query_start=0;
    write_error=0;
    VOID(pthread_mutex_unlock(&LOCK_log));
  }
}


548
bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
unknown's avatar
unknown committed
549 550
		      const char *format,...)
{
551
  if (is_open() && (what_to_log & (1L << (uint) command)))
unknown's avatar
unknown committed
552
  {
553
    int error=0;
unknown's avatar
unknown committed
554
    VOID(pthread_mutex_lock(&LOCK_log));
555 556

    /* Test if someone closed after the is_open test */
unknown's avatar
unknown committed
557 558 559 560
    if (log_type != LOG_CLOSED)
    {
      time_t skr;
      ulong id;
561 562 563 564
      va_list args;
      va_start(args,format);
      char buff[32];

unknown's avatar
unknown committed
565 566 567 568 569 570
      if (thd)
      {						// Normal thread
	if ((thd->options & OPTION_LOG_OFF) &&
	    (thd->master_access & PROCESS_ACL))
	{
	  VOID(pthread_mutex_unlock(&LOCK_log));
571
	  return 0;				// No logging
unknown's avatar
unknown committed
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
	}
	id=thd->thread_id;
	if (thd->user_time || !(skr=thd->query_start()))
	  skr=time(NULL);			// Connected
      }
      else
      {						// Log from connect handler
	skr=time(NULL);
	id=0;
      }
      if (skr != last_time)
      {
	last_time=skr;
	struct tm tm_tmp;
	struct tm *start;
	localtime_r(&skr,&tm_tmp);
	start=&tm_tmp;
589 590 591 592 593 594 595 596
	/* 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);
unknown's avatar
unknown committed
597
	if (my_b_write(&log_file, (byte*) buff,16))
unknown's avatar
unknown committed
598 599
	  error=errno;
      }
unknown's avatar
unknown committed
600
      else if (my_b_write(&log_file, (byte*) "\t\t",2) < 0)
unknown's avatar
unknown committed
601
	error=errno;
602
      sprintf(buff,"%7ld %-11.11s", id,command_name[(uint) command]);
unknown's avatar
unknown committed
603
      if (my_b_write(&log_file, (byte*) buff,strlen(buff)))
unknown's avatar
unknown committed
604 605 606
	error=errno;
      if (format)
      {
unknown's avatar
unknown committed
607
	if (my_b_write(&log_file, (byte*) " ",1) ||
608
	    my_b_vprintf(&log_file,format,args) == (uint) -1)
unknown's avatar
unknown committed
609 610
	  error=errno;
      }
unknown's avatar
unknown committed
611
      if (my_b_write(&log_file, (byte*) "\n",1) ||
612
	  flush_io_cache(&log_file))
unknown's avatar
unknown committed
613 614 615 616 617 618
	error=errno;
      if (error && ! write_error)
      {
	write_error=1;
	sql_print_error(ER(ER_ERROR_ON_WRITE),name,error);
      }
619
      va_end(args);
unknown's avatar
unknown committed
620 621
    }
    VOID(pthread_mutex_unlock(&LOCK_log));
622
    return error != 0;
unknown's avatar
unknown committed
623
  }
624
  return 0;
unknown's avatar
unknown committed
625 626 627 628
}

/* Write to binary log in a format to be used for replication */

629
bool MYSQL_LOG::write(Query_log_event* event_info)
unknown's avatar
unknown committed
630
{
631
  /* In most cases this is only called if 'is_open()' is true */
unknown's avatar
unknown committed
632
  bool error=0;
633 634
  bool should_rotate = 0;
  
unknown's avatar
unknown committed
635 636
  if (!inited)					// Can't use mutex if not init
    return 0;
637
  VOID(pthread_mutex_lock(&LOCK_log));
638
  if (is_open())
unknown's avatar
unknown committed
639
  {
640 641 642 643
    THD *thd=event_info->thd;
    IO_CACHE *file = (event_info->cache_stmt ? &thd->transaction.trans_log :
		      &log_file);
    if ((!(thd->options & OPTION_BIN_LOG) &&
644
	 (thd->master_access & PROCESS_ACL)) ||
645
	!db_ok(event_info->db, binlog_do_db, binlog_ignore_db))
unknown's avatar
unknown committed
646
    {
647 648 649
      VOID(pthread_mutex_unlock(&LOCK_log));
      return 0;
    }
unknown's avatar
unknown committed
650 651
    error=1;

652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
    if (thd->last_insert_id_used)
    {
      Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->last_insert_id);
      if (e.write(file))
	goto err;
    }
    if (thd->insert_id_used)
    {
      Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id);
      if (e.write(file))
	goto err;
    }
    if (thd->convert_set)
    {
      char buf[1024] = "SET CHARACTER SET ";
      char* p = strend(buf);
      p = strmov(p, thd->convert_set->name);
      int save_query_length = thd->query_length;
      // just in case somebody wants it later
      thd->query_length = (uint)(p - buf);
      Query_log_event e(thd, buf);
      if (e.write(file))
	goto err;
      thd->query_length = save_query_length; // clean up
    }
    if (event_info->write(file) ||
	file == &log_file && flush_io_cache(file))
      goto err;
    error=0;
681
    should_rotate = (file == &log_file && my_b_tell(file) >= max_binlog_size); 
682 683 684 685 686 687 688 689 690 691 692 693 694
err:
    if (error)
    {
      if (my_errno == EFBIG)
	my_error(ER_TRANS_CACHE_FULL, MYF(0));
      else
	my_error(ER_ERROR_ON_WRITE, MYF(0), name, errno);
      write_error=1;
    }
    if (file == &log_file)
      VOID(pthread_cond_broadcast(&COND_binlog_update));
  }
  VOID(pthread_mutex_unlock(&LOCK_log));
695 696
  if(should_rotate)
    new_file();
697 698 699 700 701 702 703 704 705 706 707 708 709
  return error;
}

/*
  Write a cached log entry to the binary log
  We only come here if there is something in the cache.
  'cache' needs to be reinitialized after this functions returns.
*/

bool MYSQL_LOG::write(IO_CACHE *cache)
{
  VOID(pthread_mutex_lock(&LOCK_log));
  bool error=1;
710
  
711 712 713 714
  if (is_open())
  {
    uint length;

unknown's avatar
unknown committed
715
    if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
716
    {
unknown's avatar
unknown committed
717
      sql_print_error(ER(ER_ERROR_ON_WRITE), cache->file_name, errno);
718 719
      goto err;
    }
720 721
    length=my_b_bytes_in_cache(cache);
    do
722 723
    {
      if (my_b_write(&log_file, cache->rc_pos, length))
724
      {
725
	if (!write_error)
726
	  sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
727
	goto err;
728
      }
729
      cache->rc_pos=cache->rc_end;		// Mark buffer used up
730
    } while ((length=my_b_fill(cache)));
731 732 733
    if (flush_io_cache(&log_file))
    {
      if (!write_error)
unknown's avatar
unknown committed
734
	sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
735 736 737 738
      goto err;
    }
    if (cache->error)				// Error on read
    {
unknown's avatar
unknown committed
739
      sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno);
740
      goto err;
unknown's avatar
unknown committed
741 742
    }
  }
743 744 745 746 747 748 749 750
  error=0;

err:
  if (error)
    write_error=1;
  else
    VOID(pthread_cond_broadcast(&COND_binlog_update));
    
751 752
  VOID(pthread_mutex_unlock(&LOCK_log));
  
753
  return error;
unknown's avatar
unknown committed
754 755
}

756 757

bool MYSQL_LOG::write(Load_log_event* event_info)
unknown's avatar
unknown committed
758
{
759
  bool error=0;
760 761
  bool should_rotate = 0;
  
unknown's avatar
unknown committed
762
  if (inited)
unknown's avatar
unknown committed
763
  {
unknown's avatar
unknown committed
764 765
    VOID(pthread_mutex_lock(&LOCK_log));
    if (is_open())
unknown's avatar
unknown committed
766
    {
unknown's avatar
unknown committed
767 768 769
      THD *thd=event_info->thd;
      if ((thd->options & OPTION_BIN_LOG) ||
	  !(thd->master_access & PROCESS_ACL))
unknown's avatar
unknown committed
770
      {
unknown's avatar
unknown committed
771 772 773 774 775 776
	if (event_info->write(&log_file) || flush_io_cache(&log_file))
	{
	  if (!write_error)
	    sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
	  error=write_error=1;
	}
777
	should_rotate = (my_b_tell(&log_file) >= max_binlog_size);
unknown's avatar
unknown committed
778
	VOID(pthread_cond_broadcast(&COND_binlog_update));
unknown's avatar
unknown committed
779 780
      }
    }
unknown's avatar
unknown committed
781
    VOID(pthread_mutex_unlock(&LOCK_log));
unknown's avatar
unknown committed
782
  }
783 784 785 786

  if(should_rotate)
    new_file();
  
787
  return error;
unknown's avatar
unknown committed
788 789 790 791 792
}


/* Write update log in a format suitable for incremental backup */

793
bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
794
		      time_t query_start)
unknown's avatar
unknown committed
795
{
796
  bool error=0;
797
  if (is_open())
unknown's avatar
unknown committed
798
  {
799
    time_t current_time;
unknown's avatar
unknown committed
800
    VOID(pthread_mutex_lock(&LOCK_log));
801
    if (is_open())
unknown's avatar
unknown committed
802
    {						// Safety agains reopen
803
      int tmp_errno=0;
unknown's avatar
unknown committed
804 805 806 807 808 809
      char buff[80],*end;
      end=buff;
      if (!(thd->options & OPTION_UPDATE_LOG) &&
	  (thd->master_access & PROCESS_ACL))
      {
	VOID(pthread_mutex_unlock(&LOCK_log));
810
	return 0;
unknown's avatar
unknown committed
811
      }
unknown's avatar
unknown committed
812
      if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start)
unknown's avatar
unknown committed
813
      {
814 815
	current_time=time(NULL);
	if (current_time != last_time)
unknown's avatar
unknown committed
816
	{
817
	  last_time=current_time;
unknown's avatar
unknown committed
818 819
	  struct tm tm_tmp;
	  struct tm *start;
820
	  localtime_r(&current_time,&tm_tmp);
unknown's avatar
unknown committed
821
	  start=&tm_tmp;
822 823 824 825 826 827 828 829
	  /* 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);
unknown's avatar
unknown committed
830
	  if (my_b_write(&log_file, (byte*) buff,24))
831
	    tmp_errno=errno;
unknown's avatar
unknown committed
832
	}
unknown's avatar
unknown committed
833
	if (my_b_printf(&log_file, "# User@Host: %s[%s] @ %s [%s]\n",
834 835 836
			thd->priv_user,
			thd->user,
			thd->host ? thd->host : "",
unknown's avatar
unknown committed
837
			thd->ip ? thd->ip : "") == (uint) -1)
838
	  tmp_errno=errno;
unknown's avatar
unknown committed
839
      }
840 841 842
      if (query_start)
      {
	/* For slow query log */
843 844 845 846
	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),
unknown's avatar
unknown committed
847
			(ulong) thd->sent_row_count) == (uint) -1)
848
	    tmp_errno=errno;
849
      }
unknown's avatar
unknown committed
850 851
      if (thd->db && strcmp(thd->db,db))
      {						// Database changed
unknown's avatar
unknown committed
852
	if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
853
	  tmp_errno=errno;
unknown's avatar
unknown committed
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
	strmov(db,thd->db);
      }
      if (thd->last_insert_id_used)
      {
	end=strmov(end,",last_insert_id=");
	end=longlong10_to_str((longlong) thd->current_insert_id,end,-10);
      }
      // Save value if we do an insert.
      if (thd->insert_id_used)
      {
	if (specialflag & SPECIAL_LONG_LOG_FORMAT)
	{
	  end=strmov(end,",insert_id=");
	  end=longlong10_to_str((longlong) thd->last_insert_id,end,-10);
	}
      }
      if (thd->query_start_used)
      {
	if (query_start != thd->query_start())
	{
	  query_start=thd->query_start();
	  end=strmov(end,",timestamp=");
	  end=int10_to_str((long) query_start,end,10);
	}
      }
      if (end != buff)
      {
	*end++=';';
	*end++='\n';
	*end=0;
unknown's avatar
unknown committed
884 885
	if (my_b_write(&log_file, (byte*) "SET ",4) ||
	    my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff)-1))
886
	  tmp_errno=errno;
unknown's avatar
unknown committed
887 888 889 890 891 892
      }
      if (!query)
      {
	query="#adminstrator command";
	query_length=21;
      }
unknown's avatar
unknown committed
893 894
      if (my_b_write(&log_file, (byte*) query,query_length) ||
	  my_b_write(&log_file, (byte*) ";\n",2) ||
895
	  flush_io_cache(&log_file))
896 897
	tmp_errno=errno;
      if (tmp_errno)
unknown's avatar
unknown committed
898
      {
899 900 901 902 903 904
	error=1;
	if (! write_error)
	{
	  write_error=1;
	  sql_print_error(ER(ER_ERROR_ON_WRITE),name,error);
	}
unknown's avatar
unknown committed
905 906 907 908
      }
    }
    VOID(pthread_mutex_unlock(&LOCK_log));
  }
909
  return error;
unknown's avatar
unknown committed
910 911 912 913 914
}


void MYSQL_LOG::close(bool exiting)
{					// One can't set log_type here!
915
  if (is_open())
unknown's avatar
unknown committed
916
  {
917
    File file=log_file.file;
unknown's avatar
unknown committed
918 919 920
    if (log_type == LOG_BIN)
    {
      Stop_log_event s;
921
      s.write(&log_file);
unknown's avatar
unknown committed
922 923
      VOID(pthread_cond_broadcast(&COND_binlog_update));
    }
924 925
    end_io_cache(&log_file);
    if (my_close(file,MYF(0)) < 0 && ! write_error)
unknown's avatar
unknown committed
926 927 928 929 930
    {
      write_error=1;
      sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
    }
  }
931
  if (exiting && index_file >= 0)
932
  {
933
    if (my_close(index_file,MYF(0)) < 0 && ! write_error)
unknown's avatar
unknown committed
934
    {
935 936
      write_error=1;
      sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
unknown's avatar
unknown committed
937
    }
unknown's avatar
unknown committed
938 939
    index_file=-1;
    log_type=LOG_CLOSED;
940
  }
941
  safeFree(name);
unknown's avatar
unknown committed
942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
}


	/* Check if a string is a valid number */
	/* Output: TRUE -> number */

static bool test_if_number(register const char *str,
			   long *res, bool allow_wildcards)
{
  reg2 int flag;
  const char *start;
  DBUG_ENTER("test_if_number");

  flag=0; start=str;
  while (*str++ == ' ') ;
  if (*--str == '-' || *str == '+')
    str++;
  while (isdigit(*str) || (allow_wildcards &&
			   (*str == wild_many || *str == wild_one)))
  {
    flag=1;
    str++;
  }
  if (*str == '.')
  {
    for (str++ ;
	 isdigit(*str) ||
	   (allow_wildcards && (*str == wild_many || *str == wild_one)) ;
	 str++, flag=1) ;
  }
  if (*str != 0 || flag == 0)
    DBUG_RETURN(0);
  if (res)
    *res=atol(start);
  DBUG_RETURN(1);			/* Number ok */
} /* test_if_number */


void sql_print_error(const char *format,...)
{
  va_list args;
  time_t skr;
  struct tm tm_tmp;
  struct tm *start;
  va_start(args,format);
  DBUG_ENTER("sql_print_error");

  VOID(pthread_mutex_lock(&LOCK_error_log));
#ifndef DBUG_OFF
  {
    char buff[1024];
993
    my_vsnprintf(buff,sizeof(buff)-1,format,args);
unknown's avatar
unknown committed
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
    DBUG_PRINT("error",("%s",buff));
  }
#endif
  skr=time(NULL);
  localtime_r(&skr,&tm_tmp);
  start=&tm_tmp;
  fprintf(stderr,"%02d%02d%02d %2d:%02d:%02d  ",
	  start->tm_year % 100,
	  start->tm_mon+1,
	  start->tm_mday,
	  start->tm_hour,
	  start->tm_min,
	  start->tm_sec);
  (void) vfprintf(stderr,format,args);
  (void) fputc('\n',stderr);
  fflush(stderr);
  va_end(args);

  VOID(pthread_mutex_unlock(&LOCK_error_log));
  DBUG_VOID_RETURN;
}



void sql_perror(const char *message)
{
#ifdef HAVE_STRERROR
  sql_print_error("%s: %s",message, strerror(errno));
#else
  perror(message);
#endif
}