mariadb/mysys/my_error.c
unknown b5df1d3446 Bugs fixed:
- If not in autocommit mode, delete rows one by one so that we can roll back if necessary
- bitmap->used_size was not correctly set, which caused bitmap pages to be overwritten
- Fixed bug in bitmap handling when allocation tail pages
- Ensure we reserve place for directory entry when calculation place for head and tail pages
- Fixed wrong value in bitmap->size[0]
- Fixed wrong assert in flush_log_for_bitmap
- Fixed bug in _ma_bitmap_release_unused() where tail blocks could be wrongly reset
- Mark new pages as changed (Required to get repair() to work)
- Fixed problem with advancing log horizon pointer within one page bounds
- Fixed DBUG_ASSERT() when enable_indexes failes for end_bulk_insert()
- Fixed bug in logging of rows with more than one big blob
- Fixed DBUG_ASSERTS() in pagecache to allow change of WRITE_LOCK to READ_LOCK in unlock() calls
- Flush pagecache when we change from logging to not logging (if not, pagecache code breaks)
- Ensure my_errno is set on return from write/delete/update
- Fixed bug when using FIELD_SKIP_PRESPACE

New features:
- mysql_fix_privilege_tables now first uses binaries and scripts from source distribution, then in installed distribution
- Fix that optimize works for Maria tables
- maria_check --zerofill now also clear freed blob pages
- maria_check -di now prints more information about record page utilization

Optimizations:
- Use pagecache_unlock_by_link() instead of pagecache_write() if possible. (Avoids a memory copy and a find_block)
- Simplify code to abort when we found optimal bit pattern
- Skip also full head page bit patterns when searching for tail
- Increase default repair buffer to 128M for maria_chk and maria_read_log
- Increase default sort buffer for maria_chk to 64M
- Increase size of sortbuffer and pagecache for mysqld to 64M
- VARCHAR/CHAR fields are stored in increasing length order for BLOCK_RECORD tables

Better reporting:
- Fixed test of error condition for flush (for better error code)
- More error messages to mysqld if Maria recovery fails
- Always print warning if rows are deleted in repair
- Added global function _db_force_flush() that is usable when doing debugging in gdb
- Added call to my_debug_put_break_here() in case of some errors (for debugging)
- Remove used testfiles in unittest as these was written in different directories depending on from where the test was started

This should fix the bugs found when importing a big table with many varchars and one/many blobs to Maria


dbug/dbug.c:
  Added global function _db_force_flush() that is usable when doing debugging in gdbine
extra/replace.c:
  Fixed memory leak
include/my_dbug.h:
  Prototype for _db_force_flush()
include/my_global.h:
  Added stdarg.h as my_sys.h now depends on it.
include/my_sys.h:
  Make my_dbug_put_break_here() a NOP if not DBUG build
  Added my_printv_error()
include/myisamchk.h:
  Added entry 'lost' to be able to count space that is lost forever
mysql-test/r/maria.result:
  Updated results
mysql-test/t/maria.test:
  Reset autocommit after test
  New test to check if delete_all_rows is used (verified with --debug)
mysys/my_error.c:
  Added my_printv_error()
scripts/mysql_fix_privilege_tables.sh:
  First use binaries and scripts from source distribution, then in installed distribution
  This ensures that a development branch doesn't pick up wrong scripts)
sql/mysqld.cc:
  Fix that one can break maria recovery with ^C when debugging
sql/sql_class.cc:
  Removed #ifdef that has no effect
  (The preceeding DBUG_ASSERT() ensures that the following code will not be exectued)
storage/maria/ha_maria.cc:
  Increase size of sortbuffer and pagecache to 64M
  Fix that optimize works for Maria tables
  Fixed DBUG_ASSERT() when enable_indexes failes for end_bulk_insert()
  If not in autocommit mode, delete rows one by one so that we can roll back if necessary
  Fixed variable comments
storage/maria/ma_bitmap.c:
  More ASSERTS to detect overwrite of bitmap pages
  bitmap->used_size was not correctly set, which caused bitmap pages to be overwritten
  Ensure we reserve place for directory entry when calculation place for head and tail pages
  bitmap->size[0] should not include space for directory entry
  Simplify code to abort when we found optimal bit pattern
  Skip also full head page bit patterns when searching for tail (should speed up some common cases)
  Fixed bug in allocate_tail() when block->used was not aligned on 6 bytes
  Fixed wrong assert in flush_log_for_bitmap
  Fixed bug in _ma_bitmap_release_unused() where tail blocks could be wrongly reset
storage/maria/ma_blockrec.c:
  Ensure my_errno is set on return
  Fixed not optimal setting of row->min_length if we don't have variable length fields
  Use pagecache_unlock_by_link() instead of pagecache_write() if possible. (Avoids a memory copy and a find_block)
  Added DBUG_ASSERT() if we read or write wrong VARCHAR data
  Added DBUG_ASSERT() to find out if row sizes are calculated wrong
  Fixed bug in logging of rows with more than one big blob
storage/maria/ma_check.c:
  Disable logging while normal repair is done to avoid logging of index changes
  Fixed bug that caused CHECKSUM part of key page to be used
  Fixed that deleted of wrong records also works for BLOCK_RECORD
  Clear unallocated pages:
  - BLOB pages are not automaticly cleared on delete, so we need to use the bitmap to know if page is used or not
  Better error reporting
  More information about record page utilization
  Change printing of file position to printing of pages to make output more readable
  Always print warning if rows are deleted
storage/maria/ma_create.c:
  Calculate share.base_max_pack_length more accurately for BLOCK_RECORD pages (for future)
  Fixed that FIELD_SKIP_PRESPACE is recorded as FIELD_NORMAL; Fixed bug where fields could be used in wrong order
  Store FIELD_SKIP_ZERO fields before CHAR and VARCHAR fields (optimization)
  Store other fields in length order (to get better utilization of head block)
storage/maria/ma_delete.c:
  Ensure my_errno is set on return
storage/maria/ma_dynrec.c:
  Indentation fix
storage/maria/ma_locking.c:
  Set changed if open_count is counted down.
  (To avoid getting error "client is using or hasn't closed the table properly" with transactional tables
storage/maria/ma_loghandler.c:
  Fixed problem with advancing log horizon pointer within one page bounds (Patch from Sanja)
  Added more DBUG
  Indentation fixes
storage/maria/ma_open.c:
  Removed wrong casts
storage/maria/ma_page.c:
  Fixed usage of PAGECACHE_LOCK_WRITE_UNLOCK with _ma_new()
  Mark new pages as changed (Required to get repair() to work)
storage/maria/ma_pagecache.c:
  Fixed test of error condition for flush
  Fixed problem when using PAGECACHE_LOCK_WRITE_TO_READ with unlock()
  Added call to my_debug_put_break_here() in case of errors (for debugging)
storage/maria/ma_pagecrc.c:
  Ensure we get same crc for 32 and 64 bit systems by forcing argument to maria_page_crc to uint32
storage/maria/ma_recovery.c:
  Call my_printv_error() from eprint() to get critical errors to mysqld log
  Removed \n from error strings to eprint() to get nicer output in mysqld
  Added simple test in _ma_reenable_logging_for_table() to not do any work if not needed
storage/maria/ma_update.c:
  Ensure my_errno is set on return
storage/maria/ma_write.c:
  Ensure my_errno is set on return
storage/maria/maria_chk.c:
  Use DEBUGGER_OFF if --debug is not use (to get slightly faster execution for debug binaries)
  Added option --skip-safemalloc
  Don't write exponents for rec/key
storage/maria/maria_def.h:
  Increase default repair buffer to 128M for maria_chk and maria_read_log
  Increase default sort buffer for maria_chk to 64M
storage/maria/unittest/Makefile.am:
  Don't update files automaticly from bitkeeper
storage/maria/unittest/ma_pagecache_consist.c:
  Remove testfile at end
storage/maria/unittest/ma_pagecache_single.c:
  Remove testfile at end
storage/maria/unittest/ma_test_all-t:
  More tests
  Safer checking if test caused error
2008-01-07 18:54:41 +02:00

284 lines
8.2 KiB
C

/* Copyright (C) 2000 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysys_priv.h"
#include "mysys_err.h"
#include <m_string.h>
#include <stdarg.h>
#include <m_ctype.h>
/* Define some external variables for error handling */
/*
WARNING!
my_error family functions have to be used according following rules:
- if message have not parameters use my_message(ER_CODE, ER(ER_CODE), MYF(N))
- if message registered use my_error(ER_CODE, MYF(N), ...).
- With some special text of errror message use:
my_printf_error(ER_CODE, format, MYF(N), ...)
*/
char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
/*
Message texts are registered into a linked list of 'my_err_head' structs.
Each struct contains (1.) an array of pointers to C character strings with
'\0' termination, (2.) the error number for the first message in the array
(array index 0) and (3.) the error number for the last message in the array
(array index (last - first)).
The array may contain gaps with NULL pointers and pointers to empty strings.
Both kinds of gaps will be translated to "Unknown error %d.", if my_error()
is called with a respective error number.
The list of header structs is sorted in increasing order of error numbers.
Negative error numbers are allowed. Overlap of error numbers is not allowed.
Not registered error numbers will be translated to "Unknown error %d.".
*/
static struct my_err_head
{
struct my_err_head *meh_next; /* chain link */
const char **meh_errmsgs; /* error messages array */
int meh_first; /* error number matching array slot 0 */
int meh_last; /* error number matching last slot */
} my_errmsgs_globerrs = {NULL, globerrs, EE_ERROR_FIRST, EE_ERROR_LAST};
static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs;
/*
Error message to user
SYNOPSIS
my_error()
nr Errno
MyFlags Flags
... variable list
RETURN
What (*error_handler_hook)() returns:
0 OK
*/
int my_error(int nr, myf MyFlags, ...)
{
const char *format;
struct my_err_head *meh_p;
va_list args;
char ebuff[ERRMSGSIZE + 20];
DBUG_ENTER("my_error");
DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno));
/* Search for the error messages array, which could contain the message. */
for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next)
if (nr <= meh_p->meh_last)
break;
/* get the error message string. Default, if NULL or empty string (""). */
if (! (format= (meh_p && (nr >= meh_p->meh_first)) ?
meh_p->meh_errmsgs[nr - meh_p->meh_first] : NULL) || ! *format)
(void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr);
else
{
va_start(args,MyFlags);
(void) my_vsnprintf(ebuff, sizeof(ebuff), format, args);
va_end(args);
}
DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags));
}
/*
Error as printf
SYNOPSIS
my_printf_error()
error Errno
format Format string
MyFlags Flags
... variable list
*/
int my_printf_error(uint error, const char *format, myf MyFlags, ...)
{
va_list args;
char ebuff[ERRMSGSIZE+20];
DBUG_ENTER("my_printf_error");
DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d format: %s",
error, MyFlags, errno, format));
va_start(args,MyFlags);
(void) my_vsnprintf(ebuff, sizeof(ebuff), format, args);
va_end(args);
DBUG_RETURN((*error_handler_hook)(error, ebuff, MyFlags));
}
/*
Error with va_list
SYNOPSIS
my_printv_error()
error Errno
format Format string
MyFlags Flags
... variable list
*/
int my_printv_error(uint error, const char *format, myf MyFlags, va_list ap)
{
char ebuff[ERRMSGSIZE+20];
DBUG_ENTER("my_printv_error");
DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d format: %s",
error, MyFlags, errno, format));
(void) my_vsnprintf(ebuff, sizeof(ebuff), format, ap);
DBUG_RETURN((*error_handler_hook)(error, ebuff, MyFlags));
}
/*
Give message using error_handler_hook
SYNOPSIS
my_message()
error Errno
str Error message
MyFlags Flags
*/
int my_message(uint error, const char *str, register myf MyFlags)
{
return (*error_handler_hook)(error, str, MyFlags);
}
/*
Register error messages for use with my_error().
SYNOPSIS
my_error_register()
errmsgs array of pointers to error messages
first error number of first message in the array
last error number of last message in the array
DESCRIPTION
The pointer array is expected to contain addresses to NUL-terminated
C character strings. The array contains (last - first + 1) pointers.
NULL pointers and empty strings ("") are allowed. These will be mapped to
"Unknown error" when my_error() is called with a matching error number.
This function registers the error numbers 'first' to 'last'.
No overlapping with previously registered error numbers is allowed.
RETURN
0 OK
!= 0 Error
*/
int my_error_register(const char **errmsgs, int first, int last)
{
struct my_err_head *meh_p;
struct my_err_head **search_meh_pp;
/* Allocate a new header structure. */
if (! (meh_p= (struct my_err_head*) my_malloc(sizeof(struct my_err_head),
MYF(MY_WME))))
return 1;
meh_p->meh_errmsgs= errmsgs;
meh_p->meh_first= first;
meh_p->meh_last= last;
/* Search for the right position in the list. */
for (search_meh_pp= &my_errmsgs_list;
*search_meh_pp;
search_meh_pp= &(*search_meh_pp)->meh_next)
{
if ((*search_meh_pp)->meh_last > first)
break;
}
/* Error numbers must be unique. No overlapping is allowed. */
if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last))
{
my_free((uchar*)meh_p, MYF(0));
return 1;
}
/* Insert header into the chain. */
meh_p->meh_next= *search_meh_pp;
*search_meh_pp= meh_p;
return 0;
}
/*
Unregister formerly registered error messages.
SYNOPSIS
my_error_unregister()
first error number of first message
last error number of last message
DESCRIPTION
This function unregisters the error numbers 'first' to 'last'.
These must have been previously registered by my_error_register().
'first' and 'last' must exactly match the registration.
If a matching registration is present, the header is removed from the
list and the pointer to the error messages pointers array is returned.
Otherwise, NULL is returned.
RETURN
non-NULL OK, returns address of error messages pointers array.
NULL Error, no such number range registered.
*/
const char **my_error_unregister(int first, int last)
{
struct my_err_head *meh_p;
struct my_err_head **search_meh_pp;
const char **errmsgs;
/* Search for the registration in the list. */
for (search_meh_pp= &my_errmsgs_list;
*search_meh_pp;
search_meh_pp= &(*search_meh_pp)->meh_next)
{
if (((*search_meh_pp)->meh_first == first) &&
((*search_meh_pp)->meh_last == last))
break;
}
if (! *search_meh_pp)
return NULL;
/* Remove header from the chain. */
meh_p= *search_meh_pp;
*search_meh_pp= meh_p->meh_next;
/* Save the return value and free the header. */
errmsgs= meh_p->meh_errmsgs;
my_free((uchar*) meh_p, MYF(0));
return errmsgs;
}
void my_error_unregister_all(void)
{
struct my_err_head *list, *next;
for (list= my_errmsgs_globerrs.meh_next; list; list= next)
{
next= list->meh_next;
my_free((uchar*) list, MYF(0));
}
my_errmsgs_list= &my_errmsgs_globerrs;
}