Merge gbichot3.local:/home/mysql_src/mysql-5.1-for-maria
into gbichot3.local:/home/mysql_src/mysql-maria BitKeeper/etc/ignore: auto-union BUILD/SETUP.sh: Auto merged client/mysqldump.c: Auto merged config/ac-macros/plugins.m4: Auto merged configure.in: Auto merged include/Makefile.am: Auto merged include/atomic/nolock.h: Auto merged include/atomic/rwlock.h: Auto merged include/atomic/x86-gcc.h: Auto merged include/atomic/x86-msvc.h: Auto merged include/ft_global.h: Auto merged include/keycache.h: Auto merged include/m_string.h: Auto merged include/my_atomic.h: Auto merged include/my_base.h: Auto merged include/my_dbug.h: Auto merged include/my_global.h: Auto merged include/my_handler.h: Auto merged include/my_sys.h: Auto merged include/myisam.h: Auto merged libmysql/CMakeLists.txt: Auto merged libmysqld/Makefile.am: Auto merged mysql-test/mysql-test-run.pl: Auto merged mysql-test/r/events_logs_tests.result: Auto merged mysql-test/t/events_logs_tests.test: Auto merged mysys/Makefile.am: Auto merged mysys/array.c: Auto merged mysys/mf_keycache.c: Auto merged mysys/mf_keycaches.c: Auto merged mysys/my_atomic.c: Auto merged mysys/my_bit.c: Auto merged mysys/my_bitmap.c: Auto merged mysys/my_create.c: Auto merged mysys/my_delete.c: Auto merged mysys/my_getsystime.c: Auto merged mysys/my_handler.c: Auto merged mysys/my_init.c: Auto merged mysys/my_open.c: Auto merged mysys/my_pread.c: Auto merged mysys/my_rename.c: Auto merged mysys/my_symlink.c: Auto merged mysys/my_sync.c: Auto merged plugin/daemon_example/daemon_example.cc: Auto merged sql/Makefile.am: Auto merged sql/filesort.cc: Auto merged sql/gen_lex_hash.cc: Auto merged sql/ha_ndbcluster.cc: Auto merged sql/handler.h: Auto merged sql/item_func.cc: Auto merged sql/item_func.h: Auto merged sql/log.cc: Auto merged sql/mysql_priv.h: Auto merged sql/set_var.h: Auto merged sql/sql_class.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_select.cc: Auto merged sql/sql_sort.h: Auto merged sql/sql_test.cc: Auto merged sql/uniques.cc: Auto merged sql/unireg.cc: Auto merged storage/Makefile.am: Auto merged storage/csv/ha_tina.cc: Auto merged storage/myisam/Makefile.am: Auto merged storage/myisam/ft_boolean_search.c: Auto merged storage/myisam/ft_nlq_search.c: Auto merged storage/myisam/ft_parser.c: Auto merged storage/myisam/ft_static.c: Auto merged storage/myisam/ft_stopwords.c: Auto merged storage/myisam/ft_update.c: Auto merged storage/myisam/fulltext.h: Auto merged storage/myisam/ha_myisam.h: Auto merged storage/myisam/mi_check.c: Auto merged storage/myisam/mi_create.c: Auto merged storage/myisam/mi_delete.c: Auto merged storage/myisam/mi_delete_all.c: Auto merged storage/myisam/mi_dynrec.c: Auto merged storage/myisam/mi_key.c: Auto merged storage/myisam/mi_log.c: Auto merged storage/myisam/mi_open.c: Auto merged storage/myisam/mi_packrec.c: Auto merged storage/myisam/mi_range.c: Auto merged storage/myisam/mi_rsamepos.c: Auto merged storage/myisam/mi_search.c: Auto merged storage/myisam/mi_test1.c: Auto merged storage/myisam/mi_test2.c: Auto merged storage/myisam/mi_unique.c: Auto merged storage/myisam/mi_update.c: Auto merged storage/myisam/mi_write.c: Auto merged storage/myisam/myisamchk.c: Auto merged storage/myisam/myisamlog.c: Auto merged storage/myisam/myisampack.c: Auto merged storage/myisam/rt_index.c: Auto merged storage/myisam/sort.c: Auto merged storage/myisammrg/ha_myisammrg.h: Auto merged unittest/mytap/tap.c: Auto merged mysql-test/r/view.result: manual merge mysql-test/t/view.test: manual merge Makefile.am: manual merge mysql-test/t/disabled.def: manual merge sql/mysqld.cc: manual merge sql/set_var.cc: manual merge sql/udf_example.c: manual merge storage/myisam/ha_myisam.cc: manual merge storage/myisam/myisamdef.h: manual merge storage/ndb/src/mgmapi/mgmapi.cpp: manual merge unittest/Makefile.am: manual merge unittest/mysys/Makefile.am: manual merge unittest/mysys/my_atomic-t.c: manual merge
This commit is contained in:
commit
ea57b3d4a0
28
.bzrignore
28
.bzrignore
@ -1,6 +1,7 @@
|
||||
*-t
|
||||
*.Plo
|
||||
*.Po
|
||||
*.Tpo
|
||||
*.a
|
||||
*.bb
|
||||
*.bbg
|
||||
@ -1058,6 +1059,7 @@ libmysqld/ha_innobase.cc
|
||||
libmysqld/ha_innodb.cc
|
||||
libmysqld/ha_isam.cc
|
||||
libmysqld/ha_isammrg.cc
|
||||
libmysqld/ha_maria.cc
|
||||
libmysqld/ha_myisam.cc
|
||||
libmysqld/ha_myisammrg.cc
|
||||
libmysqld/ha_ndbcluster.cc
|
||||
@ -2385,6 +2387,20 @@ storage/innobase/ut/.deps/ut0rnd.Po
|
||||
storage/innobase/ut/.deps/ut0ut.Po
|
||||
storage/innobase/ut/.deps/ut0vec.Po
|
||||
storage/innobase/ut/.deps/ut0wqueue.Po
|
||||
storage/maria/*.MAD
|
||||
storage/maria/*.MAI
|
||||
storage/maria/ma_rt_test
|
||||
storage/maria/ma_sp_test
|
||||
storage/maria/ma_test1
|
||||
storage/maria/ma_test2
|
||||
storage/maria/ma_test3
|
||||
storage/maria/ma_test_all
|
||||
storage/maria/maria.log
|
||||
storage/maria/maria_chk
|
||||
storage/maria/maria_ftdump
|
||||
storage/maria/maria_log
|
||||
storage/maria/maria_pack
|
||||
storage/maria/unittest/maria_control
|
||||
storage/myisam/.deps/ft_boolean_search.Po
|
||||
storage/myisam/.deps/ft_nlq_search.Po
|
||||
storage/myisam/.deps/ft_parser.Po
|
||||
@ -2922,13 +2938,25 @@ unittest/examples/.deps/simple-t.Po
|
||||
unittest/examples/.deps/skip-t.Po
|
||||
unittest/examples/.deps/skip_all-t.Po
|
||||
unittest/examples/.deps/todo-t.Po
|
||||
unittest/maria_control
|
||||
unittest/mysys/*.t
|
||||
unittest/mysys/.deps/base64-t.Po
|
||||
unittest/mysys/.deps/bitmap-t.Po
|
||||
unittest/mysys/.deps/my_atomic-t.Po
|
||||
unittest/mysys/mf_pagecache_consist_1k-t-big
|
||||
unittest/mysys/mf_pagecache_consist_1kHC-t-big
|
||||
unittest/mysys/mf_pagecache_consist_1kRD-t-big
|
||||
unittest/mysys/mf_pagecache_consist_1kWR-t-big
|
||||
unittest/mysys/mf_pagecache_consist_64k-t-big
|
||||
unittest/mysys/mf_pagecache_consist_64kHC-t-big
|
||||
unittest/mysys/mf_pagecache_consist_64kRD-t-big
|
||||
unittest/mysys/mf_pagecache_consist_64kWR-t-big
|
||||
unittest/mysys/mf_pagecache_single_64k-t-big
|
||||
unittest/mytap/.deps/tap.Po
|
||||
unittest/mytap/t/*.t
|
||||
unittest/mytap/t/.deps/basic-t.Po
|
||||
unittest/page_cache_test_file_1
|
||||
unittest/pagecache_debug.log
|
||||
unittest/unit
|
||||
vi.h
|
||||
vio/*.ds?
|
||||
|
@ -121,8 +121,9 @@ valgrind_flags="-USAFEMALLOC -UFORCE_INIT_OF_VARS -DHAVE_purify "
|
||||
valgrind_flags="$valgrind_flags -DMYSQL_SERVER_SUFFIX=-valgrind-max"
|
||||
#
|
||||
# Used in -debug builds
|
||||
debug_cflags="-DUNIV_MUST_NOT_INLINE -DEXTRA_DEBUG -DFORCE_INIT_OF_VARS "
|
||||
debug_cflags="-DUNIV_MUST_NOT_INLINE -DEXTRA_DEBUG -DFORCE_INIT_OF_VARS"
|
||||
debug_cflags="$debug_cflags -DSAFEMALLOC -DPEDANTIC_SAFEMALLOC -DSAFE_MUTEX"
|
||||
debug_cflags="$debug_cflags -DMY_LF_EXTRA_DEBUG"
|
||||
error_inject="--with-error-inject "
|
||||
#
|
||||
# Base C++ flags for all builds
|
||||
@ -154,8 +155,6 @@ then
|
||||
base_configs="$base_configs --with-libedit"
|
||||
fi
|
||||
|
||||
static_link="--with-mysqld-ldflags=-all-static "
|
||||
static_link="$static_link --with-client-ldflags=-all-static"
|
||||
# we need local-infile in all binaries for rpl000001
|
||||
# if you need to disable local-infile in the client, write a build script
|
||||
# and unset local_infile_configs
|
||||
|
@ -5,7 +5,7 @@ FROM=$USER@mysql.com
|
||||
COMMITS=commits@lists.mysql.com
|
||||
DOCS=docs-commit@mysql.com
|
||||
LIMIT=10000
|
||||
VERSION="5.1"
|
||||
VERSION="maria"
|
||||
BKROOT=`bk root`
|
||||
|
||||
if [ -x /usr/sbin/sendmail ]; then
|
||||
@ -76,55 +76,6 @@ EOF
|
||||
) > $BKROOT/BitKeeper/tmp/dev_public.txt
|
||||
|
||||
$SENDMAIL -t < $BKROOT/BitKeeper/tmp/dev_public.txt
|
||||
|
||||
#++
|
||||
# commits@ mail
|
||||
#--
|
||||
echo "Notifying commits list at $COMMITS"
|
||||
(
|
||||
cat <<EOF
|
||||
List-ID: <bk.mysql-$VERSION>
|
||||
From: $FROM
|
||||
To: $COMMITS
|
||||
Subject: bk commit into $VERSION tree ($CHANGESET)$BS
|
||||
X-CSetKey: <$CSETKEY>
|
||||
$BH
|
||||
Below is the list of changes that have just been committed into a local
|
||||
$VERSION repository of $USER. When $USER does a push these changes will
|
||||
be propagated to the main repository and, within 24 hours after the
|
||||
push, to the public repository.
|
||||
For information on how to access the public repository
|
||||
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
|
||||
|
||||
EOF
|
||||
bk changes -v -r+
|
||||
bk cset -r+ -d
|
||||
) | bk sed -e ${LIMIT}q > $BKROOT/BitKeeper/tmp/commits.txt
|
||||
|
||||
$SENDMAIL -t < $BKROOT/BitKeeper/tmp/commits.txt
|
||||
|
||||
#++
|
||||
# docs-commit@ mail
|
||||
# Picks up anything under the Docs subdirectory (relevant for docs team).
|
||||
#--
|
||||
bk changes -v -r+ | grep -q " Docs/"
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "Notifying docs list at $DOCS"
|
||||
(
|
||||
cat <<EOF
|
||||
List-ID: <bk.mysql-$VERSION>
|
||||
From: $FROM
|
||||
To: $DOCS
|
||||
Subject: bk commit - $VERSION tree (Manual) ($CHANGESET)$BS
|
||||
|
||||
EOF
|
||||
bk changes -v -r+
|
||||
bk cset -r+ -d
|
||||
) > $BKROOT/BitKeeper/tmp/docs.txt
|
||||
$SENDMAIL -t < $BKROOT/BitKeeper/tmp/docs.txt
|
||||
fi
|
||||
|
||||
else
|
||||
echo "commit failed because '$BK_STATUS', you may need to re-clone..."
|
||||
fi
|
||||
|
@ -130,7 +130,7 @@ test-binlog-statement:
|
||||
cd mysql-test ; \
|
||||
@PERL@ ./mysql-test-run.pl $(force) --mysqld=--binlog-format=statement
|
||||
|
||||
test: test-unit test-ns test-pr
|
||||
test: test-ns test-pr
|
||||
|
||||
test-full: test test-nr test-ps
|
||||
|
||||
|
@ -2298,9 +2298,9 @@ AC_ARG_WITH(man,
|
||||
if test "$with_man" = "yes"
|
||||
then
|
||||
man_dirs="man"
|
||||
man1_files=`ls -1 $srcdir/man/*.1 | sed -e 's;^.*man/;;'`
|
||||
man1_files=`ls -1 $srcdir/man/*.1 2>/dev/null| sed -e 's;^.*man/;;'`
|
||||
man1_files=`echo $man1_files`
|
||||
man8_files=`ls -1 $srcdir/man/*.8 | sed -e 's;^.*man/;;'`
|
||||
man8_files=`ls -1 $srcdir/man/*.8 2>/dev/null| sed -e 's;^.*man/;;'`
|
||||
man8_files=`echo $man8_files`
|
||||
else
|
||||
man_dirs=""
|
||||
@ -2436,7 +2436,6 @@ AC_SUBST(readline_basedir)
|
||||
AC_SUBST(readline_link)
|
||||
AC_SUBST(readline_h_ln_cmd)
|
||||
|
||||
|
||||
# If we have threads generate some library functions and test programs
|
||||
sql_server_dirs=
|
||||
sql_server=
|
||||
|
@ -27,8 +27,8 @@ pkginclude_HEADERS = $(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \
|
||||
my_getopt.h sslopt-longopts.h my_dir.h \
|
||||
sslopt-vars.h sslopt-case.h sql_common.h keycache.h \
|
||||
m_ctype.h mysql/plugin.h $(HEADERS_GEN)
|
||||
noinst_HEADERS = config-win.h config-netware.h \
|
||||
heap.h my_bitmap.h my_uctype.h \
|
||||
noinst_HEADERS = config-win.h config-netware.h lf.h my_bit.h \
|
||||
heap.h maria.h myisamchk.h my_bitmap.h my_uctype.h \
|
||||
myisam.h myisampack.h myisammrg.h ft_global.h\
|
||||
mysys_err.h my_base.h help_start.h help_end.h \
|
||||
my_nosys.h my_alarm.h queues.h rijndael.h sha1.h \
|
||||
@ -37,7 +37,7 @@ noinst_HEADERS = config-win.h config-netware.h \
|
||||
mysql_version.h.in my_handler.h my_time.h decimal.h \
|
||||
my_vle.h my_user.h my_atomic.h atomic/nolock.h \
|
||||
atomic/rwlock.h atomic/x86-gcc.h atomic/x86-msvc.h \
|
||||
my_libwrap.h
|
||||
my_libwrap.h pagecache.h
|
||||
|
||||
# Remove built files and the symlinked directories
|
||||
CLEANFILES = $(BUILT_SOURCES) readline openssl
|
||||
|
@ -13,24 +13,25 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#if defined(__i386__) || defined(_M_IX86)
|
||||
#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)
|
||||
|
||||
#ifdef MY_ATOMIC_MODE_DUMMY
|
||||
# define LOCK ""
|
||||
#else
|
||||
# define LOCK "lock"
|
||||
#endif
|
||||
# ifdef MY_ATOMIC_MODE_DUMMY
|
||||
# define LOCK_prefix ""
|
||||
# else
|
||||
# define LOCK_prefix "lock"
|
||||
# endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include "x86-gcc.h"
|
||||
#elif defined(_MSC_VER)
|
||||
#include "x86-msvc.h"
|
||||
#endif
|
||||
# ifdef __GNUC__
|
||||
# include "x86-gcc.h"
|
||||
# elif defined(_MSC_VER)
|
||||
# error Broken!
|
||||
# include "x86-msvc.h"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef make_atomic_cas_body
|
||||
|
||||
typedef struct { } my_atomic_rwlock_t;
|
||||
typedef struct { } my_atomic_rwlock_t __attribute__ ((unused));
|
||||
#define my_atomic_rwlock_destroy(name)
|
||||
#define my_atomic_rwlock_init(name)
|
||||
#define my_atomic_rwlock_rdlock(name)
|
||||
|
@ -13,7 +13,7 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t;
|
||||
typedef struct {pthread_mutex_t rw;} my_atomic_rwlock_t;
|
||||
|
||||
#ifdef MY_ATOMIC_MODE_DUMMY
|
||||
/*
|
||||
@ -31,17 +31,22 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t;
|
||||
#define my_atomic_rwlock_wrunlock(name)
|
||||
#define MY_ATOMIC_MODE "dummy (non-atomic)"
|
||||
#else
|
||||
#define my_atomic_rwlock_destroy(name) pthread_rwlock_destroy(& (name)->rw)
|
||||
#define my_atomic_rwlock_init(name) pthread_rwlock_init(& (name)->rw, 0)
|
||||
#define my_atomic_rwlock_rdlock(name) pthread_rwlock_rdlock(& (name)->rw)
|
||||
#define my_atomic_rwlock_wrlock(name) pthread_rwlock_wrlock(& (name)->rw)
|
||||
#define my_atomic_rwlock_rdunlock(name) pthread_rwlock_unlock(& (name)->rw)
|
||||
#define my_atomic_rwlock_wrunlock(name) pthread_rwlock_unlock(& (name)->rw)
|
||||
#define MY_ATOMIC_MODE "rwlocks"
|
||||
/*
|
||||
we're using read-write lock macros but map them to mutex locks, and they're
|
||||
faster. Still, having semantically rich API we can change the
|
||||
underlying implementation, if necessary.
|
||||
*/
|
||||
#define my_atomic_rwlock_destroy(name) pthread_mutex_destroy(& (name)->rw)
|
||||
#define my_atomic_rwlock_init(name) pthread_mutex_init(& (name)->rw, 0)
|
||||
#define my_atomic_rwlock_rdlock(name) pthread_mutex_lock(& (name)->rw)
|
||||
#define my_atomic_rwlock_wrlock(name) pthread_mutex_lock(& (name)->rw)
|
||||
#define my_atomic_rwlock_rdunlock(name) pthread_mutex_unlock(& (name)->rw)
|
||||
#define my_atomic_rwlock_wrunlock(name) pthread_mutex_unlock(& (name)->rw)
|
||||
#define MY_ATOMIC_MODE "mutex"
|
||||
#endif
|
||||
|
||||
#define make_atomic_add_body(S) int ## S sav; sav= *a; *a+= v; v=sav;
|
||||
#define make_atomic_swap_body(S) int ## S sav; sav= *a; *a= v; v=sav;
|
||||
#define make_atomic_fas_body(S) int ## S sav; sav= *a; *a= v; v=sav;
|
||||
#define make_atomic_cas_body(S) if ((ret= (*a == *cmp))) *a= set; else *cmp=*a;
|
||||
#define make_atomic_load_body(S) ret= *a;
|
||||
#define make_atomic_store_body(S) *a= v;
|
||||
|
@ -19,10 +19,18 @@
|
||||
architectures support double-word (128-bit) cas.
|
||||
*/
|
||||
|
||||
#ifdef MY_ATOMIC_NO_XADD
|
||||
#define MY_ATOMIC_MODE "gcc-x86" LOCK "-no-xadd"
|
||||
#ifdef __x86_64__
|
||||
# ifdef MY_ATOMIC_NO_XADD
|
||||
# define MY_ATOMIC_MODE "gcc-amd64" LOCK_prefix "-no-xadd"
|
||||
# else
|
||||
# define MY_ATOMIC_MODE "gcc-amd64" LOCK_prefix
|
||||
# endif
|
||||
#else
|
||||
#define MY_ATOMIC_MODE "gcc-x86" LOCK
|
||||
# ifdef MY_ATOMIC_NO_XADD
|
||||
# define MY_ATOMIC_MODE "gcc-x86" LOCK_prefix "-no-xadd"
|
||||
# else
|
||||
# define MY_ATOMIC_MODE "gcc-x86" LOCK_prefix
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* fix -ansi errors while maintaining readability */
|
||||
@ -32,12 +40,12 @@
|
||||
|
||||
#ifndef MY_ATOMIC_NO_XADD
|
||||
#define make_atomic_add_body(S) \
|
||||
asm volatile (LOCK "; xadd %0, %1;" : "+r" (v) , "+m" (*a))
|
||||
asm volatile (LOCK_prefix "; xadd %0, %1;" : "+r" (v) , "+m" (*a))
|
||||
#endif
|
||||
#define make_atomic_swap_body(S) \
|
||||
asm volatile ("; xchg %0, %1;" : "+r" (v) , "+m" (*a))
|
||||
#define make_atomic_fas_body(S) \
|
||||
asm volatile ("xchg %0, %1;" : "+r" (v) , "+m" (*a))
|
||||
#define make_atomic_cas_body(S) \
|
||||
asm volatile (LOCK "; cmpxchg %3, %0; setz %2;" \
|
||||
asm volatile (LOCK_prefix "; cmpxchg %3, %0; setz %2;" \
|
||||
: "+m" (*a), "+a" (*cmp), "=q" (ret): "r" (set))
|
||||
|
||||
#ifdef MY_ATOMIC_MODE_DUMMY
|
||||
@ -46,13 +54,16 @@
|
||||
#else
|
||||
/*
|
||||
Actually 32-bit reads/writes are always atomic on x86
|
||||
But we add LOCK here anyway to force memory barriers
|
||||
But we add LOCK_prefix here anyway to force memory barriers
|
||||
*/
|
||||
#define make_atomic_load_body(S) \
|
||||
ret=0; \
|
||||
asm volatile (LOCK "; cmpxchg %2, %0" \
|
||||
asm volatile (LOCK_prefix "; cmpxchg %2, %0" \
|
||||
: "+m" (*a), "+a" (ret): "r" (ret))
|
||||
#define make_atomic_store_body(S) \
|
||||
asm volatile ("; xchg %0, %1;" : "+m" (*a) : "r" (v))
|
||||
asm volatile ("; xchg %0, %1;" : "+m" (*a), "+r" (v))
|
||||
#endif
|
||||
|
||||
/* TODO test on intel whether the below helps. on AMD it makes no difference */
|
||||
//#define LF_BACKOFF ({asm volatile ("rep; nop"); 1; })
|
||||
|
||||
|
@ -25,24 +25,24 @@
|
||||
#ifndef _atomic_h_cleanup_
|
||||
#define _atomic_h_cleanup_ "atomic/x86-msvc.h"
|
||||
|
||||
#define MY_ATOMIC_MODE "msvc-x86" LOCK
|
||||
#define MY_ATOMIC_MODE "msvc-x86" LOCK_prefix
|
||||
|
||||
#define make_atomic_add_body(S) \
|
||||
_asm { \
|
||||
_asm mov reg_ ## S, v \
|
||||
_asm LOCK xadd *a, reg_ ## S \
|
||||
_asm LOCK_prefix xadd *a, reg_ ## S \
|
||||
_asm movzx v, reg_ ## S \
|
||||
}
|
||||
#define make_atomic_cas_body(S) \
|
||||
_asm { \
|
||||
_asm mov areg_ ## S, *cmp \
|
||||
_asm mov reg2_ ## S, set \
|
||||
_asm LOCK cmpxchg *a, reg2_ ## S \
|
||||
_asm LOCK_prefix cmpxchg *a, reg2_ ## S \
|
||||
_asm mov *cmp, areg_ ## S \
|
||||
_asm setz al \
|
||||
_asm movzx ret, al \
|
||||
}
|
||||
#define make_atomic_swap_body(S) \
|
||||
#define make_atomic_fas_body(S) \
|
||||
_asm { \
|
||||
_asm mov reg_ ## S, v \
|
||||
_asm xchg *a, reg_ ## S \
|
||||
@ -55,13 +55,13 @@
|
||||
#else
|
||||
/*
|
||||
Actually 32-bit reads/writes are always atomic on x86
|
||||
But we add LOCK here anyway to force memory barriers
|
||||
But we add LOCK_prefix here anyway to force memory barriers
|
||||
*/
|
||||
#define make_atomic_load_body(S) \
|
||||
_asm { \
|
||||
_asm mov areg_ ## S, 0 \
|
||||
_asm mov reg2_ ## S, areg_ ## S \
|
||||
_asm LOCK cmpxchg *a, reg2_ ## S \
|
||||
_asm LOCK_prefix cmpxchg *a, reg2_ ## S \
|
||||
_asm mov ret, areg_ ## S \
|
||||
}
|
||||
#define make_atomic_store_body(S) \
|
||||
|
@ -65,6 +65,17 @@ void ft_free_stopwords(void);
|
||||
FT_INFO *ft_init_search(uint,void *, uint, byte *, uint,CHARSET_INFO *, byte *);
|
||||
my_bool ft_boolean_check_syntax_string(const byte *);
|
||||
|
||||
/* Internal symbols for fulltext between maria and MyISAM */
|
||||
|
||||
#define HA_FT_WTYPE HA_KEYTYPE_FLOAT
|
||||
#define HA_FT_WLEN 4
|
||||
#define FT_SEGS 2
|
||||
|
||||
#define ft_sintXkorr(A) mi_sint4korr(A)
|
||||
#define ft_intXstore(T,A) mi_int4store(T,A)
|
||||
|
||||
extern const HA_KEYSEG ft_keysegs[FT_SEGS];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -127,7 +127,8 @@ extern void end_key_cache(KEY_CACHE *keycache, my_bool cleanup);
|
||||
/* Functions to handle multiple key caches */
|
||||
extern my_bool multi_keycache_init(void);
|
||||
extern void multi_keycache_free(void);
|
||||
extern KEY_CACHE *multi_key_cache_search(byte *key, uint length);
|
||||
extern KEY_CACHE *multi_key_cache_search(byte *key, uint length,
|
||||
KEY_CACHE *def);
|
||||
extern my_bool multi_key_cache_set(const byte *key, uint length,
|
||||
KEY_CACHE *key_cache);
|
||||
extern void multi_key_cache_change(KEY_CACHE *old_data,
|
||||
|
258
include/lf.h
Normal file
258
include/lf.h
Normal file
@ -0,0 +1,258 @@
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; 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 */
|
||||
|
||||
#ifndef _lf_h
|
||||
#define _lf_h
|
||||
|
||||
#include <my_atomic.h>
|
||||
|
||||
/*
|
||||
Helpers to define both func() and _func(), where
|
||||
func() is a _func() protected by my_atomic_rwlock_wrlock()
|
||||
*/
|
||||
|
||||
#define lock_wrap(f, t, proto_args, args, lock) \
|
||||
t _ ## f proto_args; \
|
||||
static inline t f proto_args \
|
||||
{ \
|
||||
t ret; \
|
||||
my_atomic_rwlock_wrlock(lock); \
|
||||
ret= _ ## f args; \
|
||||
my_atomic_rwlock_wrunlock(lock); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define lock_wrap_void(f, proto_args, args, lock) \
|
||||
void _ ## f proto_args; \
|
||||
static inline void f proto_args \
|
||||
{ \
|
||||
my_atomic_rwlock_wrlock(lock); \
|
||||
_ ## f args; \
|
||||
my_atomic_rwlock_wrunlock(lock); \
|
||||
}
|
||||
|
||||
#define nolock_wrap(f, t, proto_args, args) \
|
||||
t _ ## f proto_args; \
|
||||
static inline t f proto_args \
|
||||
{ \
|
||||
return _ ## f args; \
|
||||
}
|
||||
|
||||
#define nolock_wrap_void(f, proto_args, args) \
|
||||
void _ ## f proto_args; \
|
||||
static inline void f proto_args \
|
||||
{ \
|
||||
_ ## f args; \
|
||||
}
|
||||
|
||||
/*
|
||||
wait-free dynamic array, see lf_dynarray.c
|
||||
|
||||
4 levels of 256 elements each mean 4311810304 elements in an array - it
|
||||
should be enough for a while
|
||||
*/
|
||||
#define LF_DYNARRAY_LEVEL_LENGTH 256
|
||||
#define LF_DYNARRAY_LEVELS 4
|
||||
|
||||
typedef struct {
|
||||
void * volatile level[LF_DYNARRAY_LEVELS];
|
||||
uint size_of_element;
|
||||
my_atomic_rwlock_t lock;
|
||||
} LF_DYNARRAY;
|
||||
|
||||
typedef int (*lf_dynarray_func)(void *, void *);
|
||||
|
||||
void lf_dynarray_init(LF_DYNARRAY *array, uint element_size);
|
||||
void lf_dynarray_destroy(LF_DYNARRAY *array);
|
||||
|
||||
nolock_wrap(lf_dynarray_value, void *,
|
||||
(LF_DYNARRAY *array, uint idx),
|
||||
(array, idx))
|
||||
lock_wrap(lf_dynarray_lvalue, void *,
|
||||
(LF_DYNARRAY *array, uint idx),
|
||||
(array, idx),
|
||||
&array->lock)
|
||||
nolock_wrap(lf_dynarray_iterate, int,
|
||||
(LF_DYNARRAY *array, lf_dynarray_func func, void *arg),
|
||||
(array, func, arg))
|
||||
|
||||
/*
|
||||
pin manager for memory allocator, lf_alloc-pin.c
|
||||
*/
|
||||
|
||||
#define LF_PINBOX_PINS 4
|
||||
#define LF_PURGATORY_SIZE 10
|
||||
|
||||
typedef void lf_pinbox_free_func(void *, void *);
|
||||
|
||||
typedef struct {
|
||||
LF_DYNARRAY pinstack;
|
||||
lf_pinbox_free_func *free_func;
|
||||
void *free_func_arg;
|
||||
uint free_ptr_offset;
|
||||
uint32 volatile pinstack_top_ver; /* this is a versioned pointer */
|
||||
uint32 volatile pins_in_stack; /* number of elements in array */
|
||||
} LF_PINBOX;
|
||||
|
||||
typedef struct {
|
||||
void * volatile pin[LF_PINBOX_PINS];
|
||||
LF_PINBOX *pinbox;
|
||||
void *purgatory;
|
||||
uint32 purgatory_count;
|
||||
uint32 volatile link;
|
||||
/* we want sizeof(LF_PINS) to be 128 to avoid false sharing */
|
||||
char pad[128-sizeof(uint32)*2
|
||||
-sizeof(LF_PINBOX *)
|
||||
-sizeof(void *)*(LF_PINBOX_PINS+1)];
|
||||
} LF_PINS;
|
||||
|
||||
/*
|
||||
shortcut macros to do an atomic_wrlock on a structure that uses pins
|
||||
(e.g. lf_hash).
|
||||
*/
|
||||
#define lf_rwlock_by_pins(PINS) \
|
||||
my_atomic_rwlock_wrlock(&(PINS)->pinbox->pinstack.lock)
|
||||
#define lf_rwunlock_by_pins(PINS) \
|
||||
my_atomic_rwlock_wrunlock(&(PINS)->pinbox->pinstack.lock)
|
||||
|
||||
/*
|
||||
compile-time assert, to require "no less than N" pins
|
||||
it's enough if it'll fail on at least one compiler, so
|
||||
we'll enable it on GCC only, which supports zero-length arrays.
|
||||
*/
|
||||
#if defined(__GNUC__) && defined(MY_LF_EXTRA_DEBUG)
|
||||
#define LF_REQUIRE_PINS(N) \
|
||||
static const char require_pins[LF_PINBOX_PINS-N] __attribute__ ((unused)); \
|
||||
static const int LF_NUM_PINS_IN_THIS_FILE= N
|
||||
|
||||
#define _lf_pin(PINS, PIN, ADDR) \
|
||||
( \
|
||||
assert(PIN < LF_NUM_PINS_IN_THIS_FILE), \
|
||||
my_atomic_storeptr(&(PINS)->pin[PIN], (ADDR)) \
|
||||
)
|
||||
#else
|
||||
#define LF_REQUIRE_PINS(N)
|
||||
#define _lf_pin(PINS, PIN, ADDR) my_atomic_storeptr(&(PINS)->pin[PIN], (ADDR))
|
||||
#endif
|
||||
|
||||
#define _lf_unpin(PINS, PIN) _lf_pin(PINS, PIN, NULL)
|
||||
#define lf_pin(PINS, PIN, ADDR) \
|
||||
do { \
|
||||
lf_rwlock_by_pins(PINS); \
|
||||
_lf_pin(PINS, PIN, ADDR); \
|
||||
lf_rwunlock_by_pins(PINS); \
|
||||
} while (0)
|
||||
#define lf_unpin(PINS, PIN) lf_pin(PINS, PIN, NULL)
|
||||
#define _lf_assert_pin(PINS, PIN) assert((PINS)->pin[PIN] != 0)
|
||||
#define _lf_assert_unpin(PINS, PIN) assert((PINS)->pin[PIN] == 0)
|
||||
|
||||
void lf_pinbox_init(LF_PINBOX *pinbox, uint free_ptr_offset,
|
||||
lf_pinbox_free_func *free_func, void * free_func_arg);
|
||||
void lf_pinbox_destroy(LF_PINBOX *pinbox);
|
||||
|
||||
lock_wrap(lf_pinbox_get_pins, LF_PINS *,
|
||||
(LF_PINBOX *pinbox),
|
||||
(pinbox),
|
||||
&pinbox->pinstack.lock)
|
||||
lock_wrap_void(lf_pinbox_put_pins,
|
||||
(LF_PINS *pins),
|
||||
(pins),
|
||||
&pins->pinbox->pinstack.lock)
|
||||
lock_wrap_void(lf_pinbox_free,
|
||||
(LF_PINS *pins, void *addr),
|
||||
(pins, addr),
|
||||
&pins->pinbox->pinstack.lock)
|
||||
|
||||
/*
|
||||
memory allocator, lf_alloc-pin.c
|
||||
*/
|
||||
|
||||
struct st_lf_alloc_node {
|
||||
struct st_lf_alloc_node *next;
|
||||
};
|
||||
|
||||
typedef struct st_lf_allocator {
|
||||
LF_PINBOX pinbox;
|
||||
struct st_lf_alloc_node * volatile top;
|
||||
uint element_size;
|
||||
uint32 volatile mallocs;
|
||||
} LF_ALLOCATOR;
|
||||
|
||||
void lf_alloc_init(LF_ALLOCATOR *allocator, uint size, uint free_ptr_offset);
|
||||
void lf_alloc_destroy(LF_ALLOCATOR *allocator);
|
||||
uint lf_alloc_in_pool(LF_ALLOCATOR *allocator);
|
||||
/*
|
||||
shortcut macros to access underlying pinbox functions from an LF_ALLOCATOR
|
||||
see _lf_pinbox_get_pins() and _lf_pinbox_put_pins()
|
||||
*/
|
||||
#define _lf_alloc_free(PINS, PTR) _lf_pinbox_free((PINS), (PTR))
|
||||
#define lf_alloc_free(PINS, PTR) lf_pinbox_free((PINS), (PTR))
|
||||
#define _lf_alloc_get_pins(ALLOC) _lf_pinbox_get_pins(&(ALLOC)->pinbox)
|
||||
#define lf_alloc_get_pins(ALLOC) lf_pinbox_get_pins(&(ALLOC)->pinbox)
|
||||
#define _lf_alloc_put_pins(PINS) _lf_pinbox_put_pins(PINS)
|
||||
#define lf_alloc_put_pins(PINS) lf_pinbox_put_pins(PINS)
|
||||
#define lf_alloc_real_free(ALLOC, ADDR) my_free((gptr)(ADDR), MYF(0))
|
||||
|
||||
lock_wrap(lf_alloc_new, void *,
|
||||
(LF_PINS *pins),
|
||||
(pins),
|
||||
&pins->pinbox->pinstack.lock)
|
||||
|
||||
/*
|
||||
extendible hash, lf_hash.c
|
||||
*/
|
||||
#include <hash.h>
|
||||
|
||||
#define LF_HASH_UNIQUE 1
|
||||
|
||||
typedef struct {
|
||||
LF_DYNARRAY array; /* hash itself */
|
||||
LF_ALLOCATOR alloc; /* allocator for elements */
|
||||
hash_get_key get_key; /* see HASH */
|
||||
CHARSET_INFO *charset; /* see HASH */
|
||||
uint key_offset, key_length; /* see HASH */
|
||||
uint element_size, flags; /* LF_HASH_UNIQUE, etc */
|
||||
int32 volatile size; /* size of array */
|
||||
int32 volatile count; /* number of elements in the hash */
|
||||
} LF_HASH;
|
||||
|
||||
void lf_hash_init(LF_HASH *hash, uint element_size, uint flags,
|
||||
uint key_offset, uint key_length, hash_get_key get_key,
|
||||
CHARSET_INFO *charset);
|
||||
void lf_hash_destroy(LF_HASH *hash);
|
||||
int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data);
|
||||
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen);
|
||||
int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen);
|
||||
/*
|
||||
shortcut macros to access underlying pinbox functions from an LF_HASH
|
||||
see _lf_pinbox_get_pins() and _lf_pinbox_put_pins()
|
||||
*/
|
||||
#define _lf_hash_get_pins(HASH) _lf_alloc_get_pins(&(HASH)->alloc)
|
||||
#define lf_hash_get_pins(HASH) lf_alloc_get_pins(&(HASH)->alloc)
|
||||
#define _lf_hash_put_pins(PINS) _lf_pinbox_put_pins(PINS)
|
||||
#define lf_hash_put_pins(PINS) lf_pinbox_put_pins(PINS)
|
||||
|
||||
/*
|
||||
cleanup
|
||||
*/
|
||||
|
||||
#undef lock_wrap_void
|
||||
#undef lock_wrap
|
||||
#undef nolock_wrap_void
|
||||
#undef nolock_wrap
|
||||
|
||||
#endif
|
||||
|
@ -67,7 +67,7 @@
|
||||
# define bcopy(s, d, n) memcpy((d), (s), (n))
|
||||
# define bcmp(A,B,C) memcmp((A),(B),(C))
|
||||
# define bzero(A,B) memset((A),0,(B))
|
||||
# define bmove_align(A,B,C) memcpy((A),(B),(C))
|
||||
# define bmove_align(A,B,C) memcpy((A),(B),(C))
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
@ -126,7 +126,10 @@ extern int bcmp(const char *s1,const char *s2,uint len);
|
||||
extern int my_bcmp(const char *s1,const char *s2,uint len);
|
||||
#undef bcmp
|
||||
#define bcmp(A,B,C) my_bcmp((A),(B),(C))
|
||||
#endif
|
||||
#define bzero_if_purify(A,B) bzero(A,B)
|
||||
#else
|
||||
#define bzero_if_purify(A,B)
|
||||
#endif /* HAVE_purify */
|
||||
|
||||
#ifndef bmove512
|
||||
extern void bmove512(gptr dst,const gptr src,uint len);
|
||||
|
439
include/maria.h
Normal file
439
include/maria.h
Normal file
@ -0,0 +1,439 @@
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; 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 */
|
||||
|
||||
/* This file should be included when using maria_funktions */
|
||||
|
||||
#ifndef _maria_h
|
||||
#define _maria_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#ifndef _my_base_h
|
||||
#include <my_base.h>
|
||||
#endif
|
||||
#ifndef _m_ctype_h
|
||||
#include <m_ctype.h>
|
||||
#endif
|
||||
#ifndef _keycache_h
|
||||
#include "keycache.h"
|
||||
#endif
|
||||
#include "my_handler.h"
|
||||
#include "ft_global.h"
|
||||
#include <myisamchk.h>
|
||||
#include <mysql/plugin.h>
|
||||
|
||||
/*
|
||||
Limit max keys according to HA_MAX_POSSIBLE_KEY; See myisamchk.h for details
|
||||
*/
|
||||
|
||||
#if MAX_INDEXES > HA_MAX_POSSIBLE_KEY
|
||||
#define MARIA_MAX_KEY HA_MAX_POSSIBLE_KEY /* Max allowed keys */
|
||||
#else
|
||||
#define MARIA_MAX_KEY MAX_INDEXES /* Max allowed keys */
|
||||
#endif
|
||||
|
||||
#define MARIA_MAX_MSG_BUF 1024 /* used in CHECK TABLE, REPAIR TABLE */
|
||||
#define MARIA_NAME_IEXT ".MAI"
|
||||
#define MARIA_NAME_DEXT ".MAD"
|
||||
/* Max extra space to use when sorting keys */
|
||||
#define MARIA_MAX_TEMP_LENGTH 2*1024L*1024L*1024L
|
||||
/* Possible values for maria_block_size (must be power of 2) */
|
||||
#define MARIA_KEY_BLOCK_LENGTH 8192 /* default key block length */
|
||||
#define MARIA_MIN_KEY_BLOCK_LENGTH 1024 /* Min key block length */
|
||||
#define MARIA_MAX_KEY_BLOCK_LENGTH 32768
|
||||
#define maria_portable_sizeof_char_ptr 8
|
||||
#define MARIA_MAX_KEY_LENGTH 1000 /* Max length in bytes */
|
||||
|
||||
/*
|
||||
In the following macros '_keyno_' is 0 .. keys-1.
|
||||
If there can be more keys than bits in the key_map, the highest bit
|
||||
is for all upper keys. They cannot be switched individually.
|
||||
This means that clearing of high keys is ignored, setting one high key
|
||||
sets all high keys.
|
||||
*/
|
||||
#define MARIA_KEYMAP_BITS (8 * SIZEOF_LONG_LONG)
|
||||
#define MARIA_KEYMAP_HIGH_MASK (ULL(1) << (MARIA_KEYMAP_BITS - 1))
|
||||
#define maria_get_mask_all_keys_active(_keys_) \
|
||||
(((_keys_) < MARIA_KEYMAP_BITS) ? \
|
||||
((ULL(1) << (_keys_)) - ULL(1)) : \
|
||||
(~ ULL(0)))
|
||||
#if MARIA_MAX_KEY > MARIA_KEYMAP_BITS
|
||||
#define maria_is_key_active(_keymap_,_keyno_) \
|
||||
(((_keyno_) < MARIA_KEYMAP_BITS) ? \
|
||||
test((_keymap_) & (ULL(1) << (_keyno_))) : \
|
||||
test((_keymap_) & MARIA_KEYMAP_HIGH_MASK))
|
||||
#define maria_set_key_active(_keymap_,_keyno_) \
|
||||
(_keymap_)|= (((_keyno_) < MARIA_KEYMAP_BITS) ? \
|
||||
(ULL(1) << (_keyno_)) : \
|
||||
MARIA_KEYMAP_HIGH_MASK)
|
||||
#define maria_clear_key_active(_keymap_,_keyno_) \
|
||||
(_keymap_)&= (((_keyno_) < MARIA_KEYMAP_BITS) ? \
|
||||
(~ (ULL(1) << (_keyno_))) : \
|
||||
(~ (ULL(0))) /*ignore*/ )
|
||||
#else
|
||||
#define maria_is_key_active(_keymap_,_keyno_) \
|
||||
test((_keymap_) & (ULL(1) << (_keyno_)))
|
||||
#define maria_set_key_active(_keymap_,_keyno_) \
|
||||
(_keymap_)|= (ULL(1) << (_keyno_))
|
||||
#define maria_clear_key_active(_keymap_,_keyno_) \
|
||||
(_keymap_)&= (~ (ULL(1) << (_keyno_)))
|
||||
#endif
|
||||
#define maria_is_any_key_active(_keymap_) \
|
||||
test((_keymap_))
|
||||
#define maria_is_all_keys_active(_keymap_,_keys_) \
|
||||
((_keymap_) == maria_get_mask_all_keys_active(_keys_))
|
||||
#define maria_set_all_keys_active(_keymap_,_keys_) \
|
||||
(_keymap_)= maria_get_mask_all_keys_active(_keys_)
|
||||
#define maria_clear_all_keys_active(_keymap_) \
|
||||
(_keymap_)= 0
|
||||
#define maria_intersect_keys_active(_to_,_from_) \
|
||||
(_to_)&= (_from_)
|
||||
#define maria_is_any_intersect_keys_active(_keymap1_,_keys_,_keymap2_) \
|
||||
((_keymap1_) & (_keymap2_) & \
|
||||
maria_get_mask_all_keys_active(_keys_))
|
||||
#define maria_copy_keys_active(_to_,_maxkeys_,_from_) \
|
||||
(_to_)= (maria_get_mask_all_keys_active(_maxkeys_) & \
|
||||
(_from_))
|
||||
|
||||
/* Param to/from maria_info */
|
||||
|
||||
typedef ulonglong MARIA_RECORD_POS;
|
||||
|
||||
typedef struct st_maria_isaminfo /* Struct from h_info */
|
||||
{
|
||||
ha_rows records; /* Records in database */
|
||||
ha_rows deleted; /* Deleted records in database */
|
||||
MARIA_RECORD_POS recpos; /* Pos for last used record */
|
||||
MARIA_RECORD_POS newrecpos; /* Pos if we write new record */
|
||||
MARIA_RECORD_POS dup_key_pos; /* Position to record with dup key */
|
||||
my_off_t data_file_length; /* Length of data file */
|
||||
my_off_t max_data_file_length, index_file_length;
|
||||
my_off_t max_index_file_length, delete_length;
|
||||
ulong reclength; /* Recordlength */
|
||||
ulong mean_reclength; /* Mean recordlength (if packed) */
|
||||
ulonglong auto_increment;
|
||||
ulonglong key_map; /* Which keys are used */
|
||||
char *data_file_name, *index_file_name;
|
||||
uint keys; /* Number of keys in use */
|
||||
uint options; /* HA_OPTION_... used */
|
||||
int errkey, /* With key was dupplicated on err */
|
||||
sortkey; /* clustered by this key */
|
||||
File filenr; /* (uniq) filenr for datafile */
|
||||
time_t create_time; /* When table was created */
|
||||
time_t check_time;
|
||||
time_t update_time;
|
||||
uint reflength;
|
||||
ulong record_offset;
|
||||
ulong *rec_per_key; /* for sql optimizing */
|
||||
} MARIA_INFO;
|
||||
|
||||
|
||||
typedef struct st_maria_create_info
|
||||
{
|
||||
const char *index_file_name, *data_file_name; /* If using symlinks */
|
||||
ha_rows max_rows;
|
||||
ha_rows reloc_rows;
|
||||
ulonglong auto_increment;
|
||||
ulonglong data_file_length;
|
||||
ulonglong key_file_length;
|
||||
uint null_bytes;
|
||||
uint old_options;
|
||||
enum data_file_type org_data_file_type;
|
||||
uint8 language;
|
||||
my_bool with_auto_increment, transactional;
|
||||
} MARIA_CREATE_INFO;
|
||||
|
||||
struct st_maria_info; /* For referense */
|
||||
struct st_maria_share;
|
||||
typedef struct st_maria_info MARIA_HA;
|
||||
struct st_maria_s_param;
|
||||
|
||||
typedef struct st_maria_keydef /* Key definition with open & info */
|
||||
{
|
||||
struct st_maria_share *share; /* Pointer to base (set in open) */
|
||||
uint16 keysegs; /* Number of key-segment */
|
||||
uint16 flag; /* NOSAME, PACK_USED */
|
||||
|
||||
uint8 key_alg; /* BTREE, RTREE */
|
||||
uint16 block_length; /* Length of keyblock (auto) */
|
||||
uint16 underflow_block_length; /* When to execute underflow */
|
||||
uint16 keylength; /* Tot length of keyparts (auto) */
|
||||
uint16 minlength; /* min length of (packed) key (auto) */
|
||||
uint16 maxlength; /* max length of (packed) key (auto) */
|
||||
uint32 version; /* For concurrent read/write */
|
||||
uint32 ftparser_nr; /* distinct ftparser number */
|
||||
|
||||
HA_KEYSEG *seg, *end;
|
||||
struct st_mysql_ftparser *parser; /* Fulltext [pre]parser */
|
||||
int (*bin_search)(struct st_maria_info *info,
|
||||
struct st_maria_keydef *keyinfo, byte *page, byte *key,
|
||||
uint key_len, uint comp_flag, byte **ret_pos,
|
||||
byte *buff, my_bool *was_last_key);
|
||||
uint(*get_key)(struct st_maria_keydef *keyinfo, uint nod_flag,
|
||||
byte **page, byte *key);
|
||||
int (*pack_key)(struct st_maria_keydef *keyinfo, uint nod_flag,
|
||||
byte *next_key, byte *org_key, byte *prev_key,
|
||||
const byte *key, struct st_maria_s_param *s_temp);
|
||||
void (*store_key)(struct st_maria_keydef *keyinfo, byte *key_pos,
|
||||
struct st_maria_s_param *s_temp);
|
||||
int (*ck_insert)(struct st_maria_info *inf, uint k_nr, byte *k, uint klen);
|
||||
int (*ck_delete)(struct st_maria_info *inf, uint k_nr, byte *k, uint klen);
|
||||
} MARIA_KEYDEF;
|
||||
|
||||
|
||||
#define MARIA_UNIQUE_HASH_LENGTH 4
|
||||
|
||||
typedef struct st_maria_unique_def /* Segment definition of unique */
|
||||
{
|
||||
uint16 keysegs; /* Number of key-segment */
|
||||
uint8 key; /* Mapped to which key */
|
||||
uint8 null_are_equal;
|
||||
HA_KEYSEG *seg, *end;
|
||||
} MARIA_UNIQUEDEF;
|
||||
|
||||
typedef struct st_maria_decode_tree /* Decode huff-table */
|
||||
{
|
||||
uint16 *table;
|
||||
uint quick_table_bits;
|
||||
byte *intervalls;
|
||||
} MARIA_DECODE_TREE;
|
||||
|
||||
|
||||
struct st_maria_bit_buff;
|
||||
|
||||
/*
|
||||
Note that null markers should always be first in a row !
|
||||
When creating a column, one should only specify:
|
||||
type, length, null_bit and null_pos
|
||||
*/
|
||||
|
||||
typedef struct st_maria_columndef /* column information */
|
||||
{
|
||||
uint64 offset; /* Offset to position in row */
|
||||
enum en_fieldtype type;
|
||||
uint16 length; /* length of field */
|
||||
uint16 fill_length;
|
||||
uint16 null_pos; /* Position for null marker */
|
||||
uint16 empty_pos; /* Position for empty marker */
|
||||
uint8 null_bit; /* If column may be NULL */
|
||||
uint8 empty_bit; /* If column may be empty */
|
||||
|
||||
#ifndef NOT_PACKED_DATABASES
|
||||
void(*unpack)(struct st_maria_columndef *rec,
|
||||
struct st_maria_bit_buff *buff,
|
||||
byte *start, byte *end);
|
||||
enum en_fieldtype base_type;
|
||||
uint space_length_bits, pack_type;
|
||||
MARIA_DECODE_TREE *huff_tree;
|
||||
#endif
|
||||
} MARIA_COLUMNDEF;
|
||||
|
||||
|
||||
extern ulong maria_block_size;
|
||||
extern ulong maria_concurrent_insert;
|
||||
extern my_bool maria_flush, maria_single_user;
|
||||
extern my_bool maria_delay_key_write, maria_delay_rec_write;
|
||||
extern my_off_t maria_max_temp_length;
|
||||
extern ulong maria_bulk_insert_tree_size, maria_data_pointer_size;
|
||||
extern KEY_CACHE maria_key_cache_var, *maria_key_cache;
|
||||
|
||||
|
||||
/* Prototypes for maria-functions */
|
||||
|
||||
extern int maria_init(void);
|
||||
extern void maria_end(void);
|
||||
extern int maria_close(struct st_maria_info *file);
|
||||
extern int maria_delete(struct st_maria_info *file, const byte *buff);
|
||||
extern struct st_maria_info *maria_open(const char *name, int mode,
|
||||
uint wait_if_locked);
|
||||
extern int maria_panic(enum ha_panic_function function);
|
||||
extern int maria_rfirst(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rkey(struct st_maria_info *file, byte *buf, int inx,
|
||||
const byte *key,
|
||||
uint key_len, enum ha_rkey_function search_flag);
|
||||
extern int maria_rlast(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rnext(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rnext_same(struct st_maria_info *info, byte *buf);
|
||||
extern int maria_rprev(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rrnd(struct st_maria_info *file, byte *buf,
|
||||
MARIA_RECORD_POS pos);
|
||||
extern int maria_scan_init(struct st_maria_info *file);
|
||||
extern int maria_scan(struct st_maria_info *file, byte *buf);
|
||||
extern void maria_scan_end(struct st_maria_info *file);
|
||||
extern int maria_rsame(struct st_maria_info *file, byte *record, int inx);
|
||||
extern int maria_rsame_with_pos(struct st_maria_info *file, byte *record,
|
||||
int inx, MARIA_RECORD_POS pos);
|
||||
extern int maria_update(struct st_maria_info *file, const byte *old,
|
||||
byte *new_record);
|
||||
extern int maria_write(struct st_maria_info *file, byte *buff);
|
||||
extern MARIA_RECORD_POS maria_position(struct st_maria_info *file);
|
||||
extern int maria_status(struct st_maria_info *info, MARIA_INFO *x, uint flag);
|
||||
extern int maria_lock_database(struct st_maria_info *file, int lock_type);
|
||||
extern int maria_create(const char *name, enum data_file_type record_type,
|
||||
uint keys, MARIA_KEYDEF *keydef,
|
||||
uint columns, MARIA_COLUMNDEF *columndef,
|
||||
uint uniques, MARIA_UNIQUEDEF *uniquedef,
|
||||
MARIA_CREATE_INFO *create_info, uint flags);
|
||||
extern int maria_delete_table(const char *name);
|
||||
extern int maria_rename(const char *from, const char *to);
|
||||
extern int maria_extra(struct st_maria_info *file,
|
||||
enum ha_extra_function function, void *extra_arg);
|
||||
extern int maria_reset(struct st_maria_info *file);
|
||||
extern ha_rows maria_records_in_range(struct st_maria_info *info, int inx,
|
||||
key_range *min_key, key_range *max_key);
|
||||
extern int maria_is_changed(struct st_maria_info *info);
|
||||
extern int maria_delete_all_rows(struct st_maria_info *info);
|
||||
extern uint maria_get_pointer_length(ulonglong file_length, uint def);
|
||||
|
||||
|
||||
/* this is used to pass to mysql_mariachk_table */
|
||||
|
||||
#define MARIA_CHK_REPAIR 1 /* equivalent to mariachk -r */
|
||||
#define MARIA_CHK_VERIFY 2 /* Verify, run repair if failure */
|
||||
|
||||
typedef uint maria_bit_type;
|
||||
|
||||
typedef struct st_maria_bit_buff
|
||||
{ /* Used for packing of record */
|
||||
maria_bit_type current_byte;
|
||||
uint bits;
|
||||
uchar *pos, *end, *blob_pos, *blob_end;
|
||||
uint error;
|
||||
} MARIA_BIT_BUFF;
|
||||
|
||||
|
||||
typedef struct st_maria_sort_info
|
||||
{
|
||||
#ifdef THREAD
|
||||
/* sync things */
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
MARIA_HA *info;
|
||||
HA_CHECK *param;
|
||||
char *buff;
|
||||
SORT_KEY_BLOCKS *key_block, *key_block_end;
|
||||
SORT_FT_BUF *ft_buf;
|
||||
my_off_t filelength, dupp, buff_length;
|
||||
ha_rows max_records;
|
||||
uint current_key, total_keys;
|
||||
uint got_error, threads_running;
|
||||
myf myf_rw;
|
||||
enum data_file_type new_data_file_type;
|
||||
} MARIA_SORT_INFO;
|
||||
|
||||
typedef struct st_maria_sort_param
|
||||
{
|
||||
pthread_t thr;
|
||||
IO_CACHE read_cache, tempfile, tempfile_for_exceptions;
|
||||
DYNAMIC_ARRAY buffpek;
|
||||
MARIA_BIT_BUFF bit_buff; /* For parallel repair of packrec. */
|
||||
|
||||
MARIA_KEYDEF *keyinfo;
|
||||
MARIA_SORT_INFO *sort_info;
|
||||
HA_KEYSEG *seg;
|
||||
byte **sort_keys;
|
||||
byte *rec_buff;
|
||||
void *wordlist, *wordptr;
|
||||
MEM_ROOT wordroot;
|
||||
char *record;
|
||||
MY_TMPDIR *tmpdir;
|
||||
|
||||
/*
|
||||
The next two are used to collect statistics, see maria_update_key_parts for
|
||||
description.
|
||||
*/
|
||||
ulonglong unique[HA_MAX_KEY_SEG+1];
|
||||
ulonglong notnull[HA_MAX_KEY_SEG+1];
|
||||
|
||||
MARIA_RECORD_POS pos,max_pos,filepos,start_recpos;
|
||||
uint key, key_length,real_key_length,sortbuff_size;
|
||||
uint maxbuffers, keys, find_length, sort_keys_length;
|
||||
my_bool fix_datafile, master;
|
||||
my_bool calc_checksum; /* calculate table checksum */
|
||||
my_size_t rec_buff_size;
|
||||
|
||||
int (*key_cmp)(struct st_maria_sort_param *, const void *, const void *);
|
||||
int (*key_read)(struct st_maria_sort_param *, byte *);
|
||||
int (*key_write)(struct st_maria_sort_param *, const byte *);
|
||||
void (*lock_in_memory)(HA_CHECK *);
|
||||
NEAR int (*write_keys)(struct st_maria_sort_param *, register byte **,
|
||||
uint , struct st_buffpek *, IO_CACHE *);
|
||||
NEAR uint (*read_to_buffer)(IO_CACHE *,struct st_buffpek *, uint);
|
||||
NEAR int (*write_key)(struct st_maria_sort_param *, IO_CACHE *,char *,
|
||||
uint, uint);
|
||||
} MARIA_SORT_PARAM;
|
||||
|
||||
|
||||
/* functions in maria_check */
|
||||
void maria_chk_init(HA_CHECK *param);
|
||||
int maria_chk_status(HA_CHECK *param, MARIA_HA *info);
|
||||
int maria_chk_del(HA_CHECK *param, register MARIA_HA *info, uint test_flag);
|
||||
int maria_chk_size(HA_CHECK *param, MARIA_HA *info);
|
||||
int maria_chk_key(HA_CHECK *param, MARIA_HA *info);
|
||||
int maria_chk_data_link(HA_CHECK *param, MARIA_HA *info, int extend);
|
||||
int maria_repair(HA_CHECK *param, register MARIA_HA *info,
|
||||
my_string name, int rep_quick);
|
||||
int maria_sort_index(HA_CHECK *param, register MARIA_HA *info,
|
||||
my_string name);
|
||||
int maria_repair_by_sort(HA_CHECK *param, register MARIA_HA *info,
|
||||
const char *name, int rep_quick);
|
||||
int maria_repair_parallel(HA_CHECK *param, register MARIA_HA *info,
|
||||
const char *name, int rep_quick);
|
||||
int maria_change_to_newfile(const char *filename, const char *old_ext,
|
||||
const char *new_ext, myf myflags);
|
||||
void maria_lock_memory(HA_CHECK *param);
|
||||
int maria_update_state_info(HA_CHECK *param, MARIA_HA *info, uint update);
|
||||
void maria_update_key_parts(MARIA_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
||||
ulonglong *unique, ulonglong *notnull,
|
||||
ulonglong records);
|
||||
int maria_filecopy(HA_CHECK *param, File to, File from, my_off_t start,
|
||||
my_off_t length, const char *type);
|
||||
int maria_movepoint(MARIA_HA *info, byte *record, my_off_t oldpos,
|
||||
my_off_t newpos, uint prot_key);
|
||||
int maria_write_data_suffix(MARIA_SORT_INFO *sort_info, my_bool fix_datafile);
|
||||
int maria_test_if_almost_full(MARIA_HA *info);
|
||||
int maria_recreate_table(HA_CHECK *param, MARIA_HA **org_info, char *filename);
|
||||
int maria_disable_indexes(MARIA_HA *info);
|
||||
int maria_enable_indexes(MARIA_HA *info);
|
||||
int maria_indexes_are_disabled(MARIA_HA *info);
|
||||
void maria_disable_non_unique_index(MARIA_HA *info, ha_rows rows);
|
||||
my_bool maria_test_if_sort_rep(MARIA_HA *info, ha_rows rows, ulonglong key_map,
|
||||
my_bool force);
|
||||
|
||||
int maria_init_bulk_insert(MARIA_HA *info, ulong cache_size, ha_rows rows);
|
||||
void maria_flush_bulk_insert(MARIA_HA *info, uint inx);
|
||||
void maria_end_bulk_insert(MARIA_HA *info);
|
||||
int maria_assign_to_key_cache(MARIA_HA *info, ulonglong key_map,
|
||||
KEY_CACHE *key_cache);
|
||||
void maria_change_key_cache(KEY_CACHE *old_key_cache,
|
||||
KEY_CACHE *new_key_cache);
|
||||
int maria_preload(MARIA_HA *info, ulonglong key_map, my_bool ignore_leaves);
|
||||
|
||||
/* fulltext functions */
|
||||
FT_INFO *maria_ft_init_search(uint,void *, uint, byte *, uint,
|
||||
CHARSET_INFO *, byte *);
|
||||
|
||||
/* 'Almost-internal' Maria functions */
|
||||
|
||||
void _ma_update_auto_increment_key(HA_CHECK *param, MARIA_HA *info,
|
||||
my_bool repair);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -13,6 +13,40 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
This header defines five atomic operations:
|
||||
|
||||
my_atomic_add#(&var, what)
|
||||
add 'what' to *var, and return the old value of *var
|
||||
|
||||
my_atomic_fas#(&var, what)
|
||||
'Fetch And Store'
|
||||
store 'what' in *var, and return the old value of *var
|
||||
|
||||
my_atomic_cas#(&var, &old, new)
|
||||
'Compare And Swap'
|
||||
if *var is equal to *old, then store 'new' in *var, and return TRUE
|
||||
otherwise store *var in *old, and return FALSE
|
||||
|
||||
my_atomic_load#(&var)
|
||||
return *var
|
||||
|
||||
my_atomic_store#(&var, what)
|
||||
store 'what' in *var
|
||||
|
||||
'#' is substituted by a size suffix - 8, 16, 32, or ptr
|
||||
(e.g. my_atomic_add8, my_atomic_fas32, my_atomic_casptr).
|
||||
|
||||
NOTE This operations are not always atomic, so they always must be
|
||||
enclosed in my_atomic_rwlock_rdlock(lock)/my_atomic_rwlock_rdunlock(lock)
|
||||
or my_atomic_rwlock_wrlock(lock)/my_atomic_rwlock_wrunlock(lock).
|
||||
Hint: if a code block makes intensive use of atomic ops, it make sense
|
||||
to take/release rwlock once for the whole block, not for every statement.
|
||||
|
||||
On architectures where these operations are really atomic, rwlocks will
|
||||
be optimized away.
|
||||
*/
|
||||
|
||||
#ifndef my_atomic_rwlock_init
|
||||
|
||||
#define intptr void *
|
||||
@ -26,70 +60,115 @@
|
||||
#endif
|
||||
|
||||
#ifndef make_atomic_add_body
|
||||
#define make_atomic_add_body(S) \
|
||||
#define make_atomic_add_body(S) \
|
||||
int ## S tmp=*a; \
|
||||
while (!my_atomic_cas ## S(a, &tmp, tmp+v)); \
|
||||
v=tmp;
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/*
|
||||
we want to be able to use my_atomic_xxx functions with
|
||||
both signed and unsigned integers. But gcc will issue a warning
|
||||
"passing arg N of `my_atomic_XXX' as [un]signed due to prototype"
|
||||
if the signedness of the argument doesn't match the prototype, or
|
||||
"pointer targets in passing argument N of my_atomic_XXX differ in signedness"
|
||||
if int* is used where uint* is expected (or vice versa).
|
||||
Let's shut these warnings up
|
||||
*/
|
||||
#define make_transparent_unions(S) \
|
||||
typedef union { \
|
||||
int ## S i; \
|
||||
uint ## S u; \
|
||||
} U_ ## S __attribute__ ((transparent_union)); \
|
||||
typedef union { \
|
||||
int ## S volatile *i; \
|
||||
uint ## S volatile *u; \
|
||||
} Uv_ ## S __attribute__ ((transparent_union));
|
||||
#define uintptr intptr
|
||||
make_transparent_unions(8)
|
||||
make_transparent_unions(16)
|
||||
make_transparent_unions(32)
|
||||
make_transparent_unions(ptr)
|
||||
#undef uintptr
|
||||
#undef make_transparent_unions
|
||||
#define a U_a.i
|
||||
#define cmp U_cmp.i
|
||||
#define v U_v.i
|
||||
#define set U_set.i
|
||||
#else
|
||||
#define U_8 int8
|
||||
#define U_16 int16
|
||||
#define U_32 int32
|
||||
#define U_ptr intptr
|
||||
#define Uv_8 int8
|
||||
#define Uv_16 int16
|
||||
#define Uv_32 int32
|
||||
#define Uv_ptr intptr
|
||||
#define U_a volatile *a
|
||||
#define U_cmp *cmp
|
||||
#define U_v v
|
||||
#define U_set set
|
||||
#endif /* __GCC__ transparent_union magic */
|
||||
|
||||
#ifdef HAVE_INLINE
|
||||
|
||||
#define make_atomic_add(S) \
|
||||
static inline int ## S my_atomic_add ## S( \
|
||||
int ## S volatile *a, int ## S v) \
|
||||
{ \
|
||||
make_atomic_add_body(S); \
|
||||
return v; \
|
||||
#define make_atomic_add(S) \
|
||||
STATIC_INLINE int ## S my_atomic_add ## S( \
|
||||
Uv_ ## S U_a, U_ ## S U_v) \
|
||||
{ \
|
||||
make_atomic_add_body(S); \
|
||||
return v; \
|
||||
}
|
||||
|
||||
#define make_atomic_swap(S) \
|
||||
static inline int ## S my_atomic_swap ## S( \
|
||||
int ## S volatile *a, int ## S v) \
|
||||
{ \
|
||||
make_atomic_swap_body(S); \
|
||||
return v; \
|
||||
#define make_atomic_fas(S) \
|
||||
STATIC_INLINE int ## S my_atomic_fas ## S( \
|
||||
Uv_ ## S U_a, U_ ## S U_v) \
|
||||
{ \
|
||||
make_atomic_fas_body(S); \
|
||||
return v; \
|
||||
}
|
||||
|
||||
#define make_atomic_cas(S) \
|
||||
static inline int my_atomic_cas ## S(int ## S volatile *a, \
|
||||
int ## S *cmp, int ## S set) \
|
||||
{ \
|
||||
int8 ret; \
|
||||
make_atomic_cas_body(S); \
|
||||
return ret; \
|
||||
#define make_atomic_cas(S) \
|
||||
STATIC_INLINE int my_atomic_cas ## S(Uv_ ## S U_a, \
|
||||
Uv_ ## S U_cmp, U_ ## S U_set) \
|
||||
{ \
|
||||
int8 ret; \
|
||||
make_atomic_cas_body(S); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define make_atomic_load(S) \
|
||||
static inline int ## S my_atomic_load ## S(int ## S volatile *a) \
|
||||
{ \
|
||||
int ## S ret; \
|
||||
make_atomic_load_body(S); \
|
||||
return ret; \
|
||||
#define make_atomic_load(S) \
|
||||
STATIC_INLINE int ## S my_atomic_load ## S(Uv_ ## S U_a) \
|
||||
{ \
|
||||
int ## S ret; \
|
||||
make_atomic_load_body(S); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define make_atomic_store(S) \
|
||||
static inline void my_atomic_store ## S( \
|
||||
int ## S volatile *a, int ## S v) \
|
||||
{ \
|
||||
make_atomic_store_body(S); \
|
||||
#define make_atomic_store(S) \
|
||||
STATIC_INLINE void my_atomic_store ## S( \
|
||||
Uv_ ## S U_a, U_ ## S U_v) \
|
||||
{ \
|
||||
make_atomic_store_body(S); \
|
||||
}
|
||||
|
||||
#else /* no inline functions */
|
||||
|
||||
#define make_atomic_add(S) \
|
||||
extern int ## S my_atomic_add ## S(int ## S volatile *a, int ## S v);
|
||||
#define make_atomic_add(S) \
|
||||
extern int ## S my_atomic_add ## S(Uv_ ## S, U_ ## S);
|
||||
|
||||
#define make_atomic_swap(S) \
|
||||
extern int ## S my_atomic_swap ## S(int ## S volatile *a, int ## S v);
|
||||
#define make_atomic_fas(S) \
|
||||
extern int ## S my_atomic_fas ## S(Uv_ ## S, U_ ## S);
|
||||
|
||||
#define make_atomic_cas(S) \
|
||||
extern int my_atomic_cas ## S(int ## S volatile *a, int ## S *cmp, int ## S set);
|
||||
#define make_atomic_cas(S) \
|
||||
extern int my_atomic_cas ## S(Uv_ ## S, Uv_ ## S, U_ ## S);
|
||||
|
||||
#define make_atomic_load(S) \
|
||||
extern int ## S my_atomic_load ## S(int ## S volatile *a);
|
||||
#define make_atomic_load(S) \
|
||||
extern int ## S my_atomic_load ## S(Uv_ ## S);
|
||||
|
||||
#define make_atomic_store(S) \
|
||||
extern void my_atomic_store ## S(int ## S volatile *a, int ## S v);
|
||||
#define make_atomic_store(S) \
|
||||
extern void my_atomic_store ## S(Uv_ ## S, U_ ## S);
|
||||
|
||||
#endif
|
||||
|
||||
@ -112,34 +191,42 @@ make_atomic_store(16)
|
||||
make_atomic_store(32)
|
||||
make_atomic_store(ptr)
|
||||
|
||||
make_atomic_swap( 8)
|
||||
make_atomic_swap(16)
|
||||
make_atomic_swap(32)
|
||||
make_atomic_swap(ptr)
|
||||
|
||||
#undef make_atomic_add
|
||||
#undef make_atomic_cas
|
||||
#undef make_atomic_load
|
||||
#undef make_atomic_store
|
||||
#undef make_atomic_swap
|
||||
#undef make_atomic_add_body
|
||||
#undef make_atomic_cas_body
|
||||
#undef make_atomic_load_body
|
||||
#undef make_atomic_store_body
|
||||
#undef make_atomic_swap_body
|
||||
#undef intptr
|
||||
make_atomic_fas( 8)
|
||||
make_atomic_fas(16)
|
||||
make_atomic_fas(32)
|
||||
make_atomic_fas(ptr)
|
||||
|
||||
#ifdef _atomic_h_cleanup_
|
||||
#include _atomic_h_cleanup_
|
||||
#undef _atomic_h_cleanup_
|
||||
#endif
|
||||
|
||||
#if SIZEOF_CHARP == SIZEOF_INT
|
||||
typedef int intptr;
|
||||
#elif SIZEOF_CHARP == SIZEOF_LONG
|
||||
typedef long intptr;
|
||||
#else
|
||||
#error
|
||||
#undef U_8
|
||||
#undef U_16
|
||||
#undef U_32
|
||||
#undef U_ptr
|
||||
#undef a
|
||||
#undef cmp
|
||||
#undef v
|
||||
#undef set
|
||||
#undef U_a
|
||||
#undef U_cmp
|
||||
#undef U_v
|
||||
#undef U_set
|
||||
#undef make_atomic_add
|
||||
#undef make_atomic_cas
|
||||
#undef make_atomic_load
|
||||
#undef make_atomic_store
|
||||
#undef make_atomic_fas
|
||||
#undef make_atomic_add_body
|
||||
#undef make_atomic_cas_body
|
||||
#undef make_atomic_load_body
|
||||
#undef make_atomic_store_body
|
||||
#undef make_atomic_fas_body
|
||||
#undef intptr
|
||||
|
||||
#ifndef LF_BACKOFF
|
||||
#define LF_BACKOFF (1)
|
||||
#endif
|
||||
|
||||
#define MY_ATOMIC_OK 0
|
||||
|
@ -14,7 +14,6 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* This file includes constants used with all databases */
|
||||
/* Author: Michael Widenius */
|
||||
|
||||
#ifndef _my_base_h
|
||||
#define _my_base_h
|
||||
@ -49,7 +48,7 @@
|
||||
#define HA_OPEN_FROM_SQL_LAYER 64
|
||||
#define HA_OPEN_MMAP 128 /* open memory mapped */
|
||||
|
||||
/* The following is parameter to ha_rkey() how to use key */
|
||||
/* The following is parameter to ha_rkey() how to use key */
|
||||
|
||||
/*
|
||||
We define a complete-field prefix of a key value as a prefix where
|
||||
@ -449,7 +448,7 @@ enum en_fieldtype {
|
||||
};
|
||||
|
||||
enum data_file_type {
|
||||
STATIC_RECORD,DYNAMIC_RECORD,COMPRESSED_RECORD
|
||||
STATIC_RECORD, DYNAMIC_RECORD, COMPRESSED_RECORD, BLOCK_RECORD
|
||||
};
|
||||
|
||||
/* For key ranges */
|
||||
@ -500,4 +499,7 @@ typedef ulong ha_rows;
|
||||
|
||||
#define HA_VARCHAR_PACKLENGTH(field_length) ((field_length) < 256 ? 1 :2)
|
||||
|
||||
/* invalidator function reference for Query Cache */
|
||||
typedef void (* invalidator_by_filename)(const char * filename);
|
||||
|
||||
#endif /* _my_base_h */
|
||||
|
107
include/my_bit.h
Normal file
107
include/my_bit.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
Some useful bit functions
|
||||
*/
|
||||
|
||||
#ifdef HAVE_INLINE
|
||||
|
||||
extern const char _my_bits_nbits[256];
|
||||
extern const uchar _my_bits_reverse_table[256];
|
||||
|
||||
/*
|
||||
Find smallest X in 2^X >= value
|
||||
This can be used to divide a number with value by doing a shift instead
|
||||
*/
|
||||
|
||||
STATIC_INLINE uint my_bit_log2(ulong value)
|
||||
{
|
||||
uint bit;
|
||||
for (bit=0 ; value > 1 ; value>>=1, bit++) ;
|
||||
return bit;
|
||||
}
|
||||
|
||||
STATIC_INLINE uint my_count_bits(ulonglong v)
|
||||
{
|
||||
#if SIZEOF_LONG_LONG > 4
|
||||
/* The following code is a bit faster on 16 bit machines than if we would
|
||||
only shift v */
|
||||
ulong v2=(ulong) (v >> 32);
|
||||
return (uint) (uchar) (_my_bits_nbits[(uchar) v] +
|
||||
_my_bits_nbits[(uchar) (v >> 8)] +
|
||||
_my_bits_nbits[(uchar) (v >> 16)] +
|
||||
_my_bits_nbits[(uchar) (v >> 24)] +
|
||||
_my_bits_nbits[(uchar) (v2)] +
|
||||
_my_bits_nbits[(uchar) (v2 >> 8)] +
|
||||
_my_bits_nbits[(uchar) (v2 >> 16)] +
|
||||
_my_bits_nbits[(uchar) (v2 >> 24)]);
|
||||
#else
|
||||
return (uint) (uchar) (_my_bits_nbits[(uchar) v] +
|
||||
_my_bits_nbits[(uchar) (v >> 8)] +
|
||||
_my_bits_nbits[(uchar) (v >> 16)] +
|
||||
_my_bits_nbits[(uchar) (v >> 24)]);
|
||||
#endif
|
||||
}
|
||||
|
||||
STATIC_INLINE uint my_count_bits_ushort(ushort v)
|
||||
{
|
||||
return _my_bits_nbits[v];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Next highest power of two
|
||||
|
||||
SYNOPSIS
|
||||
my_round_up_to_next_power()
|
||||
v Value to check
|
||||
|
||||
RETURN
|
||||
Next or equal power of 2
|
||||
Note: 0 will return 0
|
||||
|
||||
NOTES
|
||||
Algorithm by Sean Anderson, according to:
|
||||
http://graphics.stanford.edu/~seander/bithacks.html
|
||||
(Orignal code public domain)
|
||||
|
||||
Comments shows how this works with 01100000000000000000000000001011
|
||||
*/
|
||||
|
||||
STATIC_INLINE uint32 my_round_up_to_next_power(uint32 v)
|
||||
{
|
||||
v--; /* 01100000000000000000000000001010 */
|
||||
v|= v >> 1; /* 01110000000000000000000000001111 */
|
||||
v|= v >> 2; /* 01111100000000000000000000001111 */
|
||||
v|= v >> 4; /* 01111111110000000000000000001111 */
|
||||
v|= v >> 8; /* 01111111111111111100000000001111 */
|
||||
v|= v >> 16; /* 01111111111111111111111111111111 */
|
||||
return v+1; /* 10000000000000000000000000000000 */
|
||||
}
|
||||
|
||||
STATIC_INLINE uint32 my_clear_highest_bit(uint32 v)
|
||||
{
|
||||
uint32 w=v >> 1;
|
||||
w|= w >> 1;
|
||||
w|= w >> 2;
|
||||
w|= w >> 4;
|
||||
w|= w >> 8;
|
||||
w|= w >> 16;
|
||||
return v & w;
|
||||
}
|
||||
|
||||
STATIC_INLINE uint32 my_reverse_bits(uint32 key)
|
||||
{
|
||||
return
|
||||
(_my_bits_reverse_table[ key & 255] << 24) |
|
||||
(_my_bits_reverse_table[(key>> 8) & 255] << 16) |
|
||||
(_my_bits_reverse_table[(key>>16) & 255] << 8) |
|
||||
_my_bits_reverse_table[(key>>24) ];
|
||||
}
|
||||
|
||||
#else
|
||||
extern uint my_bit_log2(ulong value);
|
||||
extern uint32 my_round_up_to_next_power(uint32 v);
|
||||
uint32 my_clear_highest_bit(uint32 v);
|
||||
uint32 my_reverse_bits(uint32 key);
|
||||
extern uint my_count_bits(ulonglong v);
|
||||
extern uint my_count_bits_ushort(ushort v);
|
||||
#endif
|
@ -101,7 +101,7 @@ extern FILE *_db_fp_(void);
|
||||
#define DBUG_LONGJMP(a1) longjmp(a1)
|
||||
#define DBUG_DUMP(keyword,a1,a2)
|
||||
#define DBUG_END()
|
||||
#define DBUG_ASSERT(A)
|
||||
#define DBUG_ASSERT(A) do { } while(0)
|
||||
#define DBUG_LOCK_FILE
|
||||
#define DBUG_FILE (stderr)
|
||||
#define DBUG_UNLOCK_FILE
|
||||
|
@ -224,6 +224,8 @@
|
||||
#endif
|
||||
#undef inline_test_2
|
||||
#undef inline_test_1
|
||||
/* helper macro for "instantiating" inline functions */
|
||||
#define STATIC_INLINE static inline
|
||||
|
||||
/*
|
||||
The following macros are used to control inlining a bit more than
|
||||
@ -458,6 +460,14 @@ C_MODE_END
|
||||
*/
|
||||
#include <assert.h>
|
||||
|
||||
/* an assert that works at compile-time. only for constant expression */
|
||||
#define compile_time_assert(X) \
|
||||
do \
|
||||
{ \
|
||||
char compile_time_assert[(X) ? 1 : -1] \
|
||||
__attribute__ ((unused)); \
|
||||
} while(0)
|
||||
|
||||
/* Go around some bugs in different OS and compilers */
|
||||
#if defined (HPUX11) && defined(_LARGEFILE_SOURCE)
|
||||
#define _LARGEFILE64_SOURCE
|
||||
@ -1016,6 +1026,14 @@ typedef unsigned __int64 my_ulonglong;
|
||||
typedef unsigned long long my_ulonglong;
|
||||
#endif
|
||||
|
||||
#if SIZEOF_CHARP == SIZEOF_INT
|
||||
typedef int intptr;
|
||||
#elif SIZEOF_CHARP == SIZEOF_LONG
|
||||
typedef long intptr;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
|
||||
#ifdef USE_RAID
|
||||
/*
|
||||
The following is done with a if to not get problems with pre-processors
|
||||
@ -1479,4 +1497,12 @@ do { doubleget_union _tmp; \
|
||||
#define dlerror() ""
|
||||
#endif
|
||||
|
||||
/*
|
||||
Only Linux is known to need an explicit sync of the directory to make sure a
|
||||
file creation/deletion/renaming in(from,to) this directory durable.
|
||||
*/
|
||||
#ifdef TARGET_OS_LINUX
|
||||
#define NEED_EXPLICIT_SYNC_DIR 1
|
||||
#endif
|
||||
|
||||
#endif /* my_global_h */
|
||||
|
@ -18,10 +18,30 @@
|
||||
#ifndef _my_handler_h
|
||||
#define _my_handler_h
|
||||
|
||||
#include "my_base.h"
|
||||
#include "m_ctype.h"
|
||||
#include "myisampack.h"
|
||||
|
||||
/*
|
||||
There is a hard limit for the maximum number of keys as there are only
|
||||
8 bits in the index file header for the number of keys in a table.
|
||||
This means that 0..255 keys can exist for a table. The idea of
|
||||
HA_MAX_POSSIBLE_KEY is to ensure that one can use myisamchk & tools on
|
||||
a MyISAM table for which one has more keys than MyISAM is normally
|
||||
compiled for. If you don't have this, you will get a core dump when
|
||||
running myisamchk compiled for 128 keys on a table with 255 keys.
|
||||
*/
|
||||
|
||||
#define HA_MAX_POSSIBLE_KEY 255 /* For myisamchk */
|
||||
/*
|
||||
The following defines can be increased if necessary.
|
||||
But beware the dependency of HA_MAX_POSSIBLE_KEY_BUFF and HA_MAX_KEY_LENGTH.
|
||||
*/
|
||||
|
||||
#define HA_MAX_KEY_LENGTH 1000 /* Max length in bytes */
|
||||
#define HA_MAX_KEY_SEG 16 /* Max segments for key */
|
||||
|
||||
#define HA_MAX_POSSIBLE_KEY_BUFF (HA_MAX_KEY_LENGTH + 24+ 6+6)
|
||||
#define HA_MAX_KEY_BUFF (HA_MAX_KEY_LENGTH+HA_MAX_KEY_SEG*6+8+8)
|
||||
|
||||
typedef struct st_HA_KEYSEG /* Key-portion */
|
||||
{
|
||||
CHARSET_INFO *charset;
|
||||
@ -38,33 +58,35 @@ typedef struct st_HA_KEYSEG /* Key-portion */
|
||||
} HA_KEYSEG;
|
||||
|
||||
#define get_key_length(length,key) \
|
||||
{ if ((uchar) *(key) != 255) \
|
||||
length= (uint) (uchar) *((key)++); \
|
||||
{ if (*(uchar*) (key) != 255) \
|
||||
length= (uint) *(uchar*) ((key)++); \
|
||||
else \
|
||||
{ length=mi_uint2korr((key)+1); (key)+=3; } \
|
||||
{ length= mi_uint2korr((key)+1); (key)+=3; } \
|
||||
}
|
||||
|
||||
#define get_key_length_rdonly(length,key) \
|
||||
{ if ((uchar) *(key) != 255) \
|
||||
length= ((uint) (uchar) *((key))); \
|
||||
{ if (*(uchar*) (key) != 255) \
|
||||
length= ((uint) *(uchar*) ((key))); \
|
||||
else \
|
||||
{ length=mi_uint2korr((key)+1); } \
|
||||
{ length= mi_uint2korr((key)+1); } \
|
||||
}
|
||||
|
||||
#define get_key_pack_length(length,length_pack,key) \
|
||||
{ if ((uchar) *(key) != 255) \
|
||||
{ length= (uint) (uchar) *((key)++); length_pack=1; }\
|
||||
{ if (*(uchar*) (key) != 255) \
|
||||
{ length= (uint) *(uchar*) ((key)++); length_pack= 1; }\
|
||||
else \
|
||||
{ length=mi_uint2korr((key)+1); (key)+=3; length_pack=3; } \
|
||||
{ length=mi_uint2korr((key)+1); (key)+= 3; length_pack= 3; } \
|
||||
}
|
||||
|
||||
#define store_key_length_inc(key,length) \
|
||||
{ if ((length) < 255) \
|
||||
{ *(key)++=(length); } \
|
||||
{ *(key)++= (length); } \
|
||||
else \
|
||||
{ *(key)=255; mi_int2store((key)+1,(length)); (key)+=3; } \
|
||||
}
|
||||
|
||||
#define size_to_store_key_length(length) ((length) < 255 ? 1 : 3)
|
||||
|
||||
#define get_rec_bits(bit_ptr, bit_ofs, bit_len) \
|
||||
(((((uint16) (bit_ptr)[1] << 8) | (uint16) (bit_ptr)[0]) >> (bit_ofs)) & \
|
||||
((1 << (bit_len)) - 1))
|
||||
@ -81,7 +103,7 @@ typedef struct st_HA_KEYSEG /* Key-portion */
|
||||
#define clr_rec_bits(bit_ptr, bit_ofs, bit_len) \
|
||||
set_rec_bits(0, bit_ptr, bit_ofs, bit_len)
|
||||
|
||||
extern int mi_compare_text(CHARSET_INFO *, uchar *, uint, uchar *, uint ,
|
||||
extern int ha_compare_text(CHARSET_INFO *, uchar *, uint, uchar *, uint ,
|
||||
my_bool, my_bool);
|
||||
extern int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
||||
register uchar *b, uint key_length, uint nextflag,
|
||||
|
@ -54,6 +54,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_IGNORE_BADFD 32 /* my_sync: ignore 'bad descriptor' errors */
|
||||
#define MY_SYNC_DIR 1024 /* my_create/delete/rename: sync directory */
|
||||
#define MY_RAID 64 /* Support for RAID */
|
||||
#define MY_FULL_IO 512 /* For my_read - loop intil I/O is complete */
|
||||
#define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */
|
||||
@ -215,6 +216,7 @@ extern int (*error_handler_hook)(uint my_err, const char *str,myf MyFlags);
|
||||
extern int (*fatal_error_handler_hook)(uint my_err, const char *str,
|
||||
myf MyFlags);
|
||||
extern uint my_file_limit;
|
||||
extern ulong my_thread_stack_size;
|
||||
|
||||
#ifdef HAVE_LARGE_PAGES
|
||||
extern my_bool my_use_large_pages;
|
||||
@ -628,6 +630,8 @@ extern FILE *my_fdopen(File Filedes,const char *name, int Flags,myf MyFlags);
|
||||
extern int my_fclose(FILE *fd,myf MyFlags);
|
||||
extern int my_chsize(File fd,my_off_t newlength, int filler, myf MyFlags);
|
||||
extern int my_sync(File fd, myf my_flags);
|
||||
extern int my_sync_dir(const char *dir_name, myf my_flags);
|
||||
extern int my_sync_dir_by_file(const char *file_name, myf my_flags);
|
||||
extern int my_error _VARARGS((int nr,myf MyFlags, ...));
|
||||
extern int my_printf_error _VARARGS((uint my_err, const char *format,
|
||||
myf MyFlags, ...))
|
||||
@ -767,6 +771,7 @@ extern my_bool insert_dynamic(DYNAMIC_ARRAY *array,gptr element);
|
||||
extern byte *alloc_dynamic(DYNAMIC_ARRAY *array);
|
||||
extern byte *pop_dynamic(DYNAMIC_ARRAY*);
|
||||
extern my_bool set_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index);
|
||||
extern my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements);
|
||||
extern void get_dynamic(DYNAMIC_ARRAY *array,gptr element,uint array_index);
|
||||
extern void delete_dynamic(DYNAMIC_ARRAY *array);
|
||||
extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index);
|
||||
@ -831,10 +836,6 @@ extern int packfrm(const void *, uint, const void **, uint *);
|
||||
extern int unpackfrm(const void **, uint *, const void *);
|
||||
|
||||
extern ha_checksum my_checksum(ha_checksum crc, const byte *mem, uint count);
|
||||
extern uint my_bit_log2(ulong value);
|
||||
extern uint32 my_round_up_to_next_power(uint32 v);
|
||||
extern uint my_count_bits(ulonglong v);
|
||||
extern uint my_count_bits_ushort(ushort v);
|
||||
extern void my_sleep(ulong m_seconds);
|
||||
extern ulong crc32(ulong crc, const uchar *buf, uint len);
|
||||
extern uint my_set_max_open_files(uint files);
|
||||
@ -850,7 +851,7 @@ extern int my_getncpus();
|
||||
#ifndef MAP_NOSYNC
|
||||
#define MAP_NOSYNC 0
|
||||
#endif
|
||||
#ifndef MAP_NORESERVE
|
||||
#ifndef MAP_NORESERVE
|
||||
#define MAP_NORESERVE 0 /* For irix and AIX */
|
||||
#endif
|
||||
|
||||
|
268
include/myisam.h
268
include/myisam.h
@ -31,33 +31,19 @@ extern "C" {
|
||||
#include "keycache.h"
|
||||
#endif
|
||||
#include "my_handler.h"
|
||||
#include <myisamchk.h>
|
||||
#include <mysql/plugin.h>
|
||||
|
||||
/*
|
||||
There is a hard limit for the maximum number of keys as there are only
|
||||
8 bits in the index file header for the number of keys in a table.
|
||||
This means that 0..255 keys can exist for a table. The idea of
|
||||
MI_MAX_POSSIBLE_KEY is to ensure that one can use myisamchk & tools on
|
||||
a MyISAM table for which one has more keys than MyISAM is normally
|
||||
compiled for. If you don't have this, you will get a core dump when
|
||||
running myisamchk compiled for 128 keys on a table with 255 keys.
|
||||
Limit max keys according to HA_MAX_POSSIBLE_KEY; See myisamchk.h for details
|
||||
*/
|
||||
#define MI_MAX_POSSIBLE_KEY 255 /* For myisam_chk */
|
||||
#if MAX_INDEXES > MI_MAX_POSSIBLE_KEY
|
||||
#define MI_MAX_KEY MI_MAX_POSSIBLE_KEY /* Max allowed keys */
|
||||
|
||||
#if MAX_INDEXES > HA_MAX_POSSIBLE_KEY
|
||||
#define MI_MAX_KEY HA_MAX_POSSIBLE_KEY /* Max allowed keys */
|
||||
#else
|
||||
#define MI_MAX_KEY MAX_INDEXES /* Max allowed keys */
|
||||
#endif
|
||||
|
||||
#define MI_MAX_POSSIBLE_KEY_BUFF (1024+6+6) /* For myisam_chk */
|
||||
/*
|
||||
The following defines can be increased if necessary.
|
||||
But beware the dependency of MI_MAX_POSSIBLE_KEY_BUFF and MI_MAX_KEY_LENGTH.
|
||||
*/
|
||||
#define MI_MAX_KEY_LENGTH 1000 /* Max length in bytes */
|
||||
#define MI_MAX_KEY_SEG 16 /* Max segments for key */
|
||||
|
||||
#define MI_MAX_KEY_BUFF (MI_MAX_KEY_LENGTH+MI_MAX_KEY_SEG*6+8+8)
|
||||
#define MI_MAX_MSG_BUF 1024 /* used in CHECK TABLE, REPAIR TABLE */
|
||||
#define MI_NAME_IEXT ".MYI"
|
||||
#define MI_NAME_DEXT ".MYD"
|
||||
@ -256,9 +242,6 @@ typedef struct st_columndef /* column information */
|
||||
#endif
|
||||
} MI_COLUMNDEF;
|
||||
|
||||
/* invalidator function reference for Query Cache */
|
||||
typedef void (* invalidator_by_filename)(const char * filename);
|
||||
|
||||
extern my_string myisam_log_filename; /* Name of logfile */
|
||||
extern ulong myisam_block_size;
|
||||
extern ulong myisam_concurrent_insert;
|
||||
@ -311,195 +294,117 @@ extern int mi_delete_all_rows(struct st_myisam_info *info);
|
||||
extern ulong _mi_calc_blob_length(uint length , const byte *pos);
|
||||
extern uint mi_get_pointer_length(ulonglong file_length, uint def);
|
||||
|
||||
/* this is used to pass to mysql_myisamchk_table -- by Sasha Pachev */
|
||||
/* this is used to pass to mysql_myisamchk_table */
|
||||
|
||||
#define MYISAMCHK_REPAIR 1 /* equivalent to myisamchk -r */
|
||||
#define MYISAMCHK_VERIFY 2 /* Verify, run repair if failure */
|
||||
|
||||
/*
|
||||
Definitions needed for myisamchk.c
|
||||
typedef uint mi_bit_type;
|
||||
|
||||
Entries marked as "QQ to be removed" are NOT used to
|
||||
pass check/repair options to mi_check.c. They are used
|
||||
internally by myisamchk.c or/and ha_myisam.cc and should NOT
|
||||
be stored together with other flags. They should be removed
|
||||
from the following list to make addition of new flags possible.
|
||||
*/
|
||||
typedef struct st_mi_bit_buff
|
||||
{ /* Used for packing of record */
|
||||
mi_bit_type current_byte;
|
||||
uint bits;
|
||||
uchar *pos, *end, *blob_pos, *blob_end;
|
||||
uint error;
|
||||
} MI_BIT_BUFF;
|
||||
|
||||
#define T_AUTO_INC 1
|
||||
#define T_AUTO_REPAIR 2 /* QQ to be removed */
|
||||
#define T_BACKUP_DATA 4
|
||||
#define T_CALC_CHECKSUM 8
|
||||
#define T_CHECK 16 /* QQ to be removed */
|
||||
#define T_CHECK_ONLY_CHANGED 32 /* QQ to be removed */
|
||||
#define T_CREATE_MISSING_KEYS 64
|
||||
#define T_DESCRIPT 128
|
||||
#define T_DONT_CHECK_CHECKSUM 256
|
||||
#define T_EXTEND 512
|
||||
#define T_FAST (1L << 10) /* QQ to be removed */
|
||||
#define T_FORCE_CREATE (1L << 11) /* QQ to be removed */
|
||||
#define T_FORCE_UNIQUENESS (1L << 12)
|
||||
#define T_INFO (1L << 13)
|
||||
#define T_MEDIUM (1L << 14)
|
||||
#define T_QUICK (1L << 15) /* QQ to be removed */
|
||||
#define T_READONLY (1L << 16) /* QQ to be removed */
|
||||
#define T_REP (1L << 17)
|
||||
#define T_REP_BY_SORT (1L << 18) /* QQ to be removed */
|
||||
#define T_REP_PARALLEL (1L << 19) /* QQ to be removed */
|
||||
#define T_RETRY_WITHOUT_QUICK (1L << 20)
|
||||
#define T_SAFE_REPAIR (1L << 21)
|
||||
#define T_SILENT (1L << 22)
|
||||
#define T_SORT_INDEX (1L << 23) /* QQ to be removed */
|
||||
#define T_SORT_RECORDS (1L << 24) /* QQ to be removed */
|
||||
#define T_STATISTICS (1L << 25)
|
||||
#define T_UNPACK (1L << 26)
|
||||
#define T_UPDATE_STATE (1L << 27)
|
||||
#define T_VERBOSE (1L << 28)
|
||||
#define T_VERY_SILENT (1L << 29)
|
||||
#define T_WAIT_FOREVER (1L << 30)
|
||||
#define T_WRITE_LOOP ((ulong) 1L << 31)
|
||||
|
||||
#define T_REP_ANY (T_REP | T_REP_BY_SORT | T_REP_PARALLEL)
|
||||
|
||||
/*
|
||||
Flags used by myisamchk.c or/and ha_myisam.cc that are NOT passed
|
||||
to mi_check.c follows:
|
||||
*/
|
||||
|
||||
#define TT_USEFRM 1
|
||||
#define TT_FOR_UPGRADE 2
|
||||
|
||||
#define O_NEW_INDEX 1 /* Bits set in out_flag */
|
||||
#define O_NEW_DATA 2
|
||||
#define O_DATA_LOST 4
|
||||
|
||||
/* these struct is used by my_check to tell it what to do */
|
||||
|
||||
typedef struct st_sort_key_blocks /* Used when sorting */
|
||||
typedef struct st_sort_info
|
||||
{
|
||||
uchar *buff,*end_pos;
|
||||
uchar lastkey[MI_MAX_POSSIBLE_KEY_BUFF];
|
||||
uint last_length;
|
||||
int inited;
|
||||
} SORT_KEY_BLOCKS;
|
||||
|
||||
|
||||
/*
|
||||
MyISAM supports several statistics collection methods. Currently statistics
|
||||
collection method is not stored in MyISAM file and has to be specified for
|
||||
each table analyze/repair operation in MI_CHECK::stats_method.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/* Treat NULLs as inequal when collecting statistics (default for 4.1/5.0) */
|
||||
MI_STATS_METHOD_NULLS_NOT_EQUAL,
|
||||
/* Treat NULLs as equal when collecting statistics (like 4.0 did) */
|
||||
MI_STATS_METHOD_NULLS_EQUAL,
|
||||
/* Ignore NULLs - count only tuples without NULLs in the index components */
|
||||
MI_STATS_METHOD_IGNORE_NULLS
|
||||
} enum_mi_stats_method;
|
||||
|
||||
typedef struct st_mi_check_param
|
||||
{
|
||||
ulonglong auto_increment_value;
|
||||
ulonglong max_data_file_length;
|
||||
ulonglong keys_in_use;
|
||||
ulonglong max_record_length;
|
||||
my_off_t search_after_block;
|
||||
my_off_t new_file_pos,key_file_blocks;
|
||||
my_off_t keydata,totaldata,key_blocks,start_check_pos;
|
||||
ha_rows total_records,total_deleted;
|
||||
ha_checksum record_checksum,glob_crc;
|
||||
ulong use_buffers,read_buffer_length,write_buffer_length,
|
||||
sort_buffer_length,sort_key_blocks;
|
||||
uint out_flag,warning_printed,error_printed,verbose;
|
||||
uint opt_sort_key,total_files,max_level;
|
||||
uint testflag, key_cache_block_size;
|
||||
uint8 language;
|
||||
my_bool using_global_keycache, opt_lock_memory, opt_follow_links;
|
||||
my_bool retry_repair, force_sort;
|
||||
char temp_filename[FN_REFLEN],*isam_file_name;
|
||||
MY_TMPDIR *tmpdir;
|
||||
int tmpfile_createflag;
|
||||
#ifdef THREAD
|
||||
/* sync things */
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
MI_INFO *info;
|
||||
HA_CHECK *param;
|
||||
char *buff;
|
||||
SORT_KEY_BLOCKS *key_block, *key_block_end;
|
||||
SORT_FT_BUF *ft_buf;
|
||||
my_off_t filelength, dupp, buff_length;
|
||||
ha_rows max_records;
|
||||
uint current_key, total_keys;
|
||||
uint got_error, threads_running;
|
||||
myf myf_rw;
|
||||
IO_CACHE read_cache;
|
||||
enum data_file_type new_data_file_type;
|
||||
} MI_SORT_INFO;
|
||||
|
||||
typedef struct st_mi_sort_param
|
||||
{
|
||||
pthread_t thr;
|
||||
IO_CACHE read_cache, tempfile, tempfile_for_exceptions;
|
||||
DYNAMIC_ARRAY buffpek;
|
||||
MI_BIT_BUFF bit_buff; /* For parallel repair of packrec. */
|
||||
|
||||
MI_KEYDEF *keyinfo;
|
||||
MI_SORT_INFO *sort_info;
|
||||
HA_KEYSEG *seg;
|
||||
uchar **sort_keys;
|
||||
byte *rec_buff;
|
||||
void *wordlist, *wordptr;
|
||||
MEM_ROOT wordroot;
|
||||
char *record;
|
||||
MY_TMPDIR *tmpdir;
|
||||
|
||||
/*
|
||||
The next two are used to collect statistics, see update_key_parts for
|
||||
description.
|
||||
*/
|
||||
ulonglong unique_count[MI_MAX_KEY_SEG+1];
|
||||
ulonglong notnull_count[MI_MAX_KEY_SEG+1];
|
||||
|
||||
ha_checksum key_crc[MI_MAX_POSSIBLE_KEY];
|
||||
ulong rec_per_key_part[MI_MAX_KEY_SEG*MI_MAX_POSSIBLE_KEY];
|
||||
void *thd;
|
||||
const char *db_name, *table_name;
|
||||
const char *op_name;
|
||||
enum_mi_stats_method stats_method;
|
||||
} MI_CHECK;
|
||||
ulonglong unique[HA_MAX_KEY_SEG+1];
|
||||
ulonglong notnull[HA_MAX_KEY_SEG+1];
|
||||
|
||||
typedef struct st_sort_ft_buf
|
||||
{
|
||||
uchar *buf, *end;
|
||||
int count;
|
||||
uchar lastkey[MI_MAX_KEY_BUFF];
|
||||
} SORT_FT_BUF;
|
||||
my_off_t pos,max_pos,filepos,start_recpos;
|
||||
uint key, key_length,real_key_length,sortbuff_size;
|
||||
uint maxbuffers, keys, find_length, sort_keys_length;
|
||||
my_bool fix_datafile, master;
|
||||
my_bool calc_checksum; /* calculate table checksum */
|
||||
|
||||
int (*key_cmp)(struct st_mi_sort_param *, const void *, const void *);
|
||||
int (*key_read)(struct st_mi_sort_param *,void *);
|
||||
int (*key_write)(struct st_mi_sort_param *, const void *);
|
||||
void (*lock_in_memory)(HA_CHECK *);
|
||||
NEAR int (*write_keys)(struct st_mi_sort_param *, register uchar **,
|
||||
uint , struct st_buffpek *, IO_CACHE *);
|
||||
NEAR uint (*read_to_buffer)(IO_CACHE *,struct st_buffpek *, uint);
|
||||
NEAR int (*write_key)(struct st_mi_sort_param *, IO_CACHE *,char *,
|
||||
uint, uint);
|
||||
} MI_SORT_PARAM;
|
||||
|
||||
typedef struct st_sort_info
|
||||
{
|
||||
my_off_t filelength,dupp,buff_length;
|
||||
ha_rows max_records;
|
||||
uint current_key, total_keys;
|
||||
myf myf_rw;
|
||||
enum data_file_type new_data_file_type;
|
||||
MI_INFO *info;
|
||||
MI_CHECK *param;
|
||||
char *buff;
|
||||
SORT_KEY_BLOCKS *key_block,*key_block_end;
|
||||
SORT_FT_BUF *ft_buf;
|
||||
/* sync things */
|
||||
uint got_error, threads_running;
|
||||
#ifdef THREAD
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
} SORT_INFO;
|
||||
|
||||
/* functions in mi_check */
|
||||
void myisamchk_init(MI_CHECK *param);
|
||||
int chk_status(MI_CHECK *param, MI_INFO *info);
|
||||
int chk_del(MI_CHECK *param, register MI_INFO *info, uint test_flag);
|
||||
int chk_size(MI_CHECK *param, MI_INFO *info);
|
||||
int chk_key(MI_CHECK *param, MI_INFO *info);
|
||||
int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend);
|
||||
int mi_repair(MI_CHECK *param, register MI_INFO *info,
|
||||
void myisamchk_init(HA_CHECK *param);
|
||||
int chk_status(HA_CHECK *param, MI_INFO *info);
|
||||
int chk_del(HA_CHECK *param, register MI_INFO *info, uint test_flag);
|
||||
int chk_size(HA_CHECK *param, MI_INFO *info);
|
||||
int chk_key(HA_CHECK *param, MI_INFO *info);
|
||||
int chk_data_link(HA_CHECK *param, MI_INFO *info,int extend);
|
||||
int mi_repair(HA_CHECK *param, register MI_INFO *info,
|
||||
my_string name, int rep_quick);
|
||||
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, my_string name);
|
||||
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
|
||||
int mi_sort_index(HA_CHECK *param, register MI_INFO *info, my_string name);
|
||||
int mi_repair_by_sort(HA_CHECK *param, register MI_INFO *info,
|
||||
const char * name, int rep_quick);
|
||||
int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
|
||||
int mi_repair_parallel(HA_CHECK *param, register MI_INFO *info,
|
||||
const char * name, int rep_quick);
|
||||
int change_to_newfile(const char * filename, const char * old_ext,
|
||||
const char * new_ext, uint raid_chunks,
|
||||
myf myflags);
|
||||
int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type,
|
||||
int lock_file(HA_CHECK *param, File file, my_off_t start, int lock_type,
|
||||
const char *filetype, const char *filename);
|
||||
void lock_memory(MI_CHECK *param);
|
||||
void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
|
||||
void lock_memory(HA_CHECK *param);
|
||||
void update_auto_increment_key(HA_CHECK *param, MI_INFO *info,
|
||||
my_bool repair);
|
||||
int update_state_info(MI_CHECK *param, MI_INFO *info,uint update);
|
||||
int update_state_info(HA_CHECK *param, MI_INFO *info,uint update);
|
||||
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
||||
ulonglong *unique, ulonglong *notnull,
|
||||
ulonglong records);
|
||||
int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
|
||||
int filecopy(HA_CHECK *param, File to,File from,my_off_t start,
|
||||
my_off_t length, const char *type);
|
||||
int movepoint(MI_INFO *info,byte *record,my_off_t oldpos,
|
||||
my_off_t newpos, uint prot_key);
|
||||
int write_data_suffix(SORT_INFO *sort_info, my_bool fix_datafile);
|
||||
int write_data_suffix(MI_SORT_INFO *sort_info, my_bool fix_datafile);
|
||||
int test_if_almost_full(MI_INFO *info);
|
||||
int recreate_table(MI_CHECK *param, MI_INFO **org_info, char *filename);
|
||||
int recreate_table(HA_CHECK *param, MI_INFO **org_info, char *filename);
|
||||
void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);
|
||||
my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows, ulonglong key_map,
|
||||
my_bool force);
|
||||
@ -513,6 +418,13 @@ void mi_change_key_cache(KEY_CACHE *old_key_cache,
|
||||
KEY_CACHE *new_key_cache);
|
||||
int mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves);
|
||||
|
||||
int write_data_suffix(MI_SORT_INFO *sort_info, my_bool fix_datafile);
|
||||
int flush_pending_blocks(MI_SORT_PARAM *param);
|
||||
int sort_ft_buf_flush(MI_SORT_PARAM *sort_param);
|
||||
int thr_write_keys(MI_SORT_PARAM *sort_param);
|
||||
int sort_write_record(MI_SORT_PARAM *sort_param);
|
||||
int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages, ulong);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
164
include/myisamchk.h
Normal file
164
include/myisamchk.h
Normal file
@ -0,0 +1,164 @@
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; 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 */
|
||||
|
||||
/* Definitions needed for myisamchk/mariachk.c */
|
||||
|
||||
/*
|
||||
Entries marked as "QQ to be removed" are NOT used to
|
||||
pass check/repair options to xxx_check.c. They are used
|
||||
internally by xxxchk.c or/and ha_xxxx.cc and should NOT
|
||||
be stored together with other flags. They should be removed
|
||||
from the following list to make addition of new flags possible.
|
||||
*/
|
||||
|
||||
#ifndef _myisamchk_h
|
||||
#define _myisamchk_h
|
||||
|
||||
#define T_AUTO_INC 1
|
||||
#define T_AUTO_REPAIR 2 /* QQ to be removed */
|
||||
#define T_BACKUP_DATA 4
|
||||
#define T_CALC_CHECKSUM 8
|
||||
#define T_CHECK 16 /* QQ to be removed */
|
||||
#define T_CHECK_ONLY_CHANGED 32 /* QQ to be removed */
|
||||
#define T_CREATE_MISSING_KEYS 64
|
||||
#define T_DESCRIPT 128
|
||||
#define T_DONT_CHECK_CHECKSUM 256
|
||||
#define T_EXTEND 512
|
||||
#define T_FAST (1L << 10) /* QQ to be removed */
|
||||
#define T_FORCE_CREATE (1L << 11) /* QQ to be removed */
|
||||
#define T_FORCE_UNIQUENESS (1L << 12)
|
||||
#define T_INFO (1L << 13)
|
||||
#define T_MEDIUM (1L << 14)
|
||||
#define T_QUICK (1L << 15) /* QQ to be removed */
|
||||
#define T_READONLY (1L << 16) /* QQ to be removed */
|
||||
#define T_REP (1L << 17)
|
||||
#define T_REP_BY_SORT (1L << 18) /* QQ to be removed */
|
||||
#define T_REP_PARALLEL (1L << 19) /* QQ to be removed */
|
||||
#define T_RETRY_WITHOUT_QUICK (1L << 20)
|
||||
#define T_SAFE_REPAIR (1L << 21)
|
||||
#define T_SILENT (1L << 22)
|
||||
#define T_SORT_INDEX (1L << 23) /* QQ to be removed */
|
||||
#define T_SORT_RECORDS (1L << 24) /* QQ to be removed */
|
||||
#define T_STATISTICS (1L << 25)
|
||||
#define T_UNPACK (1L << 26)
|
||||
#define T_UPDATE_STATE (1L << 27)
|
||||
#define T_VERBOSE (1L << 28)
|
||||
#define T_VERY_SILENT (1L << 29)
|
||||
#define T_WAIT_FOREVER (1L << 30)
|
||||
#define T_WRITE_LOOP ((ulong) 1L << 31)
|
||||
|
||||
#define T_REP_ANY (T_REP | T_REP_BY_SORT | T_REP_PARALLEL)
|
||||
|
||||
/*
|
||||
Flags used by xxxxchk.c or/and ha_xxxx.cc that are NOT passed
|
||||
to xxxcheck.c follows:
|
||||
*/
|
||||
|
||||
#define TT_USEFRM 1
|
||||
#define TT_FOR_UPGRADE 2
|
||||
|
||||
#define O_NEW_INDEX 1 /* Bits set in out_flag */
|
||||
#define O_NEW_DATA 2
|
||||
#define O_DATA_LOST 4
|
||||
|
||||
typedef struct st_sort_key_blocks /* Used when sorting */
|
||||
{
|
||||
byte *buff, *end_pos;
|
||||
byte lastkey[HA_MAX_POSSIBLE_KEY_BUFF];
|
||||
uint last_length;
|
||||
int inited;
|
||||
} SORT_KEY_BLOCKS;
|
||||
|
||||
|
||||
/*
|
||||
MARIA/MYISAM supports several statistics collection
|
||||
methods. Currently statistics collection method is not stored in
|
||||
MARIA file and has to be specified for each table analyze/repair
|
||||
operation in MI_CHECK::stats_method.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/* Treat NULLs as inequal when collecting statistics (default for 4.1/5.0) */
|
||||
MI_STATS_METHOD_NULLS_NOT_EQUAL,
|
||||
/* Treat NULLs as equal when collecting statistics (like 4.0 did) */
|
||||
MI_STATS_METHOD_NULLS_EQUAL,
|
||||
/* Ignore NULLs - count only tuples without NULLs in the index components */
|
||||
MI_STATS_METHOD_IGNORE_NULLS
|
||||
} enum_handler_stats_method;
|
||||
|
||||
|
||||
typedef struct st_handler_check_param
|
||||
{
|
||||
char *isam_file_name;
|
||||
MY_TMPDIR *tmpdir;
|
||||
void *thd;
|
||||
const char *db_name, *table_name, *op_name;
|
||||
ulonglong auto_increment_value;
|
||||
ulonglong max_data_file_length;
|
||||
ulonglong keys_in_use;
|
||||
ulonglong max_record_length;
|
||||
/*
|
||||
The next two are used to collect statistics, see update_key_parts for
|
||||
description.
|
||||
*/
|
||||
ulonglong unique_count[HA_MAX_KEY_SEG + 1];
|
||||
ulonglong notnull_count[HA_MAX_KEY_SEG + 1];
|
||||
|
||||
my_off_t search_after_block;
|
||||
my_off_t new_file_pos, key_file_blocks;
|
||||
my_off_t keydata, totaldata, key_blocks, start_check_pos;
|
||||
my_off_t used, empty, splits, del_length, link_used;
|
||||
ha_rows total_records, total_deleted, records,del_blocks;
|
||||
ha_rows full_page_count, tail_count;
|
||||
ha_checksum record_checksum, glob_crc;
|
||||
ha_checksum key_crc[HA_MAX_POSSIBLE_KEY];
|
||||
ha_checksum tmp_key_crc[HA_MAX_POSSIBLE_KEY];
|
||||
ha_checksum tmp_record_checksum;
|
||||
ulong use_buffers, read_buffer_length, write_buffer_length;
|
||||
ulong sort_buffer_length, sort_key_blocks;
|
||||
ulong rec_per_key_part[HA_MAX_KEY_SEG * HA_MAX_POSSIBLE_KEY];
|
||||
uint out_flag, warning_printed, error_printed, verbose;
|
||||
uint opt_sort_key, total_files, max_level;
|
||||
uint testflag, key_cache_block_size;
|
||||
int tmpfile_createflag, err_count;
|
||||
myf myf_rw;
|
||||
uint8 language;
|
||||
my_bool using_global_keycache, opt_lock_memory, opt_follow_links;
|
||||
my_bool retry_repair, force_sort, calc_checksum, static_row_size;
|
||||
char temp_filename[FN_REFLEN];
|
||||
IO_CACHE read_cache;
|
||||
enum_handler_stats_method stats_method;
|
||||
} HA_CHECK;
|
||||
|
||||
|
||||
typedef struct st_sort_ftbuf
|
||||
{
|
||||
byte *buf, *end;
|
||||
int count;
|
||||
byte lastkey[HA_MAX_KEY_BUFF];
|
||||
} SORT_FT_BUF;
|
||||
|
||||
|
||||
typedef struct st_buffpek {
|
||||
my_off_t file_pos; /* Where we are in the sort file */
|
||||
byte *base, *key; /* Key pointers */
|
||||
ha_rows count; /* Number of rows in table */
|
||||
ulong mem_count; /* numbers of keys in memory */
|
||||
ulong max_keys; /* Max keys in buffert */
|
||||
} BUFFPEK;
|
||||
|
||||
#endif /* _myisamchk_h */
|
228
include/pagecache.h
Normal file
228
include/pagecache.h
Normal file
@ -0,0 +1,228 @@
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; 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 */
|
||||
|
||||
/* Page cache variable structures */
|
||||
|
||||
#ifndef _pagecache_h
|
||||
#define _pagecache_h
|
||||
C_MODE_START
|
||||
|
||||
#include "../storage/maria/ma_loghandler_lsn.h"
|
||||
#include <m_string.h>
|
||||
|
||||
/* Type of the page */
|
||||
enum pagecache_page_type
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
/* used only for control page type changing during debugging */
|
||||
PAGECACHE_EMPTY_PAGE,
|
||||
#endif
|
||||
/* the page does not contain LSN */
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
/* the page contain LSN (maria tablespace page) */
|
||||
PAGECACHE_LSN_PAGE
|
||||
};
|
||||
|
||||
/*
|
||||
This enum describe lock status changing. every type of page cache will
|
||||
interpret WRITE/READ lock as it need.
|
||||
*/
|
||||
enum pagecache_page_lock
|
||||
{
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED, /* free -> free */
|
||||
PAGECACHE_LOCK_LEFT_READLOCKED, /* read -> read */
|
||||
PAGECACHE_LOCK_LEFT_WRITELOCKED, /* write -> write */
|
||||
PAGECACHE_LOCK_READ, /* free -> read */
|
||||
PAGECACHE_LOCK_WRITE, /* free -> write */
|
||||
PAGECACHE_LOCK_READ_UNLOCK, /* read -> free */
|
||||
PAGECACHE_LOCK_WRITE_UNLOCK, /* write -> free */
|
||||
PAGECACHE_LOCK_WRITE_TO_READ /* write -> read */
|
||||
};
|
||||
/*
|
||||
This enum describe pin status changing
|
||||
*/
|
||||
enum pagecache_page_pin
|
||||
{
|
||||
PAGECACHE_PIN_LEFT_PINNED, /* pinned -> pinned */
|
||||
PAGECACHE_PIN_LEFT_UNPINNED, /* unpinned -> unpinned */
|
||||
PAGECACHE_PIN, /* unpinned -> pinned */
|
||||
PAGECACHE_UNPIN /* pinned -> unpinned */
|
||||
};
|
||||
/* How to write the page */
|
||||
enum pagecache_write_mode
|
||||
{
|
||||
/* do not write immediately, i.e. it will be dirty page */
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
/* write page to the file and put it to the cache */
|
||||
PAGECACHE_WRITE_NOW,
|
||||
/* page already is in the file. (key cache insert analogue) */
|
||||
PAGECACHE_WRITE_DONE
|
||||
};
|
||||
|
||||
typedef void *PAGECACHE_PAGE_LINK;
|
||||
|
||||
/* file descriptor for Maria */
|
||||
typedef struct st_pagecache_file
|
||||
{
|
||||
int file; /* it is for debugging purposes then it will be uint32 file_no */
|
||||
} PAGECACHE_FILE;
|
||||
|
||||
/* page number for maria */
|
||||
typedef uint32 pgcache_page_no_t;
|
||||
|
||||
/* declare structures that is used by st_pagecache */
|
||||
|
||||
struct st_pagecache_block_link;
|
||||
typedef struct st_pagecache_block_link PAGECACHE_BLOCK_LINK;
|
||||
struct st_pagecache_page;
|
||||
typedef struct st_pagecache_page PAGECACHE_PAGE;
|
||||
struct st_pagecache_hash_link;
|
||||
typedef struct st_pagecache_hash_link PAGECACHE_HASH_LINK;
|
||||
|
||||
#include <wqueue.h>
|
||||
|
||||
typedef my_bool (*pagecache_disk_read_validator)(byte *page, gptr data);
|
||||
|
||||
#define PAGECACHE_CHANGED_BLOCKS_HASH 128 /* must be power of 2 */
|
||||
|
||||
/*
|
||||
The page cache structure
|
||||
It also contains read-only statistics parameters.
|
||||
*/
|
||||
|
||||
typedef struct st_pagecache
|
||||
{
|
||||
my_bool inited;
|
||||
my_bool resize_in_flush; /* true during flush of resize operation */
|
||||
my_bool can_be_used; /* usage of cache for read/write is allowed */
|
||||
uint shift; /* block size = 2 ^ shift */
|
||||
my_size_t mem_size; /* specified size of the cache memory */
|
||||
uint32 block_size; /* size of the page buffer of a cache block */
|
||||
ulong min_warm_blocks; /* min number of warm blocks; */
|
||||
ulong age_threshold; /* age threshold for hot blocks */
|
||||
ulonglong time; /* total number of block link operations */
|
||||
uint hash_entries; /* max number of entries in the hash table */
|
||||
int hash_links; /* max number of hash links */
|
||||
int hash_links_used; /* number of hash links taken from free links pool */
|
||||
int disk_blocks; /* max number of blocks in the cache */
|
||||
ulong blocks_used; /* maximum number of concurrently used blocks */
|
||||
ulong blocks_unused; /* number of currently unused blocks */
|
||||
ulong blocks_changed; /* number of currently dirty blocks */
|
||||
ulong warm_blocks; /* number of blocks in warm sub-chain */
|
||||
ulong cnt_for_resize_op; /* counter to block resize operation */
|
||||
ulong blocks_available; /* number of blocks available in the LRU chain */
|
||||
PAGECACHE_HASH_LINK **hash_root;/* arr. of entries into hash table buckets */
|
||||
PAGECACHE_HASH_LINK *hash_link_root;/* memory for hash table links */
|
||||
PAGECACHE_HASH_LINK *free_hash_list;/* list of free hash links */
|
||||
PAGECACHE_BLOCK_LINK *free_block_list;/* list of free blocks */
|
||||
PAGECACHE_BLOCK_LINK *block_root;/* memory for block links */
|
||||
byte HUGE_PTR *block_mem; /* memory for block buffers */
|
||||
PAGECACHE_BLOCK_LINK *used_last;/* ptr to the last block of the LRU chain */
|
||||
PAGECACHE_BLOCK_LINK *used_ins;/* ptr to the insertion block in LRU chain */
|
||||
pthread_mutex_t cache_lock; /* to lock access to the cache structure */
|
||||
WQUEUE resize_queue; /* threads waiting during resize operation */
|
||||
WQUEUE waiting_for_hash_link;/* waiting for a free hash link */
|
||||
WQUEUE waiting_for_block; /* requests waiting for a free block */
|
||||
/* hash for dirty file bl.*/
|
||||
PAGECACHE_BLOCK_LINK *changed_blocks[PAGECACHE_CHANGED_BLOCKS_HASH];
|
||||
/* hash for other file bl.*/
|
||||
PAGECACHE_BLOCK_LINK *file_blocks[PAGECACHE_CHANGED_BLOCKS_HASH];
|
||||
|
||||
/*
|
||||
The following variables are and variables used to hold parameters for
|
||||
initializing the key cache.
|
||||
*/
|
||||
|
||||
ulonglong param_buff_size; /* size the memory allocated for the cache */
|
||||
ulong param_block_size; /* size of the blocks in the key cache */
|
||||
ulong param_division_limit; /* min. percentage of warm blocks */
|
||||
ulong param_age_threshold; /* determines when hot block is downgraded */
|
||||
|
||||
/* Statistics variables. These are reset in reset_pagecache_counters(). */
|
||||
ulong global_blocks_changed; /* number of currently dirty blocks */
|
||||
ulonglong global_cache_w_requests;/* number of write requests (write hits) */
|
||||
ulonglong global_cache_write; /* number of writes from cache to files */
|
||||
ulonglong global_cache_r_requests;/* number of read requests (read hits) */
|
||||
ulonglong global_cache_read; /* number of reads from files to cache */
|
||||
|
||||
int blocks; /* max number of blocks in the cache */
|
||||
my_bool in_init; /* Set to 1 in MySQL during init/resize */
|
||||
} PAGECACHE;
|
||||
|
||||
extern int init_pagecache(PAGECACHE *pagecache, my_size_t use_mem,
|
||||
uint division_limit, uint age_threshold,
|
||||
uint block_size);
|
||||
extern int resize_pagecache(PAGECACHE *pagecache,
|
||||
my_size_t use_mem, uint division_limit,
|
||||
uint age_threshold);
|
||||
extern void change_pagecache_param(PAGECACHE *pagecache, uint division_limit,
|
||||
uint age_threshold);
|
||||
|
||||
#define pagecache_read(P,F,N,L,B,T,K,I) \
|
||||
pagecache_valid_read(P,F,N,L,B,T,K,I,0,0)
|
||||
|
||||
extern byte *pagecache_valid_read(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
pgcache_page_no_t pageno,
|
||||
uint level,
|
||||
byte *buff,
|
||||
enum pagecache_page_type type,
|
||||
enum pagecache_page_lock lock,
|
||||
PAGECACHE_PAGE_LINK *link,
|
||||
pagecache_disk_read_validator validator,
|
||||
gptr validator_data);
|
||||
extern my_bool pagecache_write(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
pgcache_page_no_t pageno,
|
||||
uint level,
|
||||
byte *buff,
|
||||
enum pagecache_page_type type,
|
||||
enum pagecache_page_lock lock,
|
||||
enum pagecache_page_pin pin,
|
||||
enum pagecache_write_mode write_mode,
|
||||
PAGECACHE_PAGE_LINK *link);
|
||||
extern void pagecache_unlock_page(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
pgcache_page_no_t pageno,
|
||||
enum pagecache_page_lock lock,
|
||||
enum pagecache_page_pin pin,
|
||||
LSN first_REDO_LSN_for_page);
|
||||
extern void pagecache_unlock(PAGECACHE *pagecache,
|
||||
PAGECACHE_PAGE_LINK *link,
|
||||
enum pagecache_page_lock lock,
|
||||
enum pagecache_page_pin pin,
|
||||
LSN first_REDO_LSN_for_page);
|
||||
extern void pagecache_unpin_page(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
pgcache_page_no_t pageno);
|
||||
extern void pagecache_unpin(PAGECACHE *pagecache,
|
||||
PAGECACHE_PAGE_LINK *link);
|
||||
extern int flush_pagecache_blocks(PAGECACHE *keycache,
|
||||
PAGECACHE_FILE *file,
|
||||
enum flush_type type);
|
||||
extern my_bool pagecache_delete_page(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
pgcache_page_no_t pageno,
|
||||
enum pagecache_page_lock lock,
|
||||
my_bool flush);
|
||||
extern void end_pagecache(PAGECACHE *keycache, my_bool cleanup);
|
||||
extern my_bool pagecache_collect_changed_blocks_with_lsn(PAGECACHE *pagecache,
|
||||
LEX_STRING *str,
|
||||
LSN *max_lsn);
|
||||
extern int reset_pagecache_counters(const char *name, PAGECACHE *pagecache);
|
||||
|
||||
C_MODE_END
|
||||
#endif /* _keycache_h */
|
26
include/wqueue.h
Normal file
26
include/wqueue.h
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
#ifndef _wqueue_h
|
||||
#define _wqueue_h
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_pthread.h>
|
||||
|
||||
/* info about requests in a waiting queue */
|
||||
typedef struct st_pagecache_wqueue
|
||||
{
|
||||
struct st_my_thread_var *last_thread; /* circular list of waiting
|
||||
threads */
|
||||
} WQUEUE;
|
||||
|
||||
#ifdef THREAD
|
||||
void wqueue_link_into_queue(WQUEUE *wqueue, struct st_my_thread_var *thread);
|
||||
void wqueue_unlink_from_queue(WQUEUE *wqueue, struct st_my_thread_var *thread);
|
||||
void wqueue_add_to_queue(WQUEUE *wqueue, struct st_my_thread_var *thread);
|
||||
void wqueue_add_and_wait(WQUEUE *wqueue,
|
||||
struct st_my_thread_var *thread,
|
||||
pthread_mutex_t *lock);
|
||||
void wqueue_release_queue(WQUEUE *wqueue);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -52,6 +52,7 @@ ADD_LIBRARY(libmysql SHARED dll.c libmysql.def
|
||||
../mysys/my_open.c ../mysys/my_pread.c ../mysys/my_pthread.c ../mysys/my_read.c
|
||||
../mysys/my_realloc.c ../mysys/my_rename.c ../mysys/my_seek.c
|
||||
../mysys/my_static.c ../strings/my_strtoll10.c ../mysys/my_symlink.c
|
||||
../mysys/my_sync.c
|
||||
../mysys/my_symlink2.c ../mysys/my_thr_init.c ../sql-common/my_time.c
|
||||
../strings/my_vsnprintf.c ../mysys/my_wincond.c ../mysys/my_winthread.c
|
||||
../mysys/my_write.c ../sql/net_serv.cc ../sql-common/pack.c ../sql/password.c
|
||||
|
@ -68,7 +68,7 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo my_realloc.lo \
|
||||
mf_iocache2.lo my_seek.lo my_sleep.lo \
|
||||
my_pread.lo mf_cache.lo md5.lo sha1.lo \
|
||||
my_getopt.lo my_gethostbyname.lo my_port.lo \
|
||||
my_rename.lo my_chsize.lo
|
||||
my_rename.lo my_chsize.lo my_sync.lo
|
||||
sqlobjects = net.lo
|
||||
sql_cmn_objects = pack.lo client.lo my_time.lo
|
||||
|
||||
|
4
mysql-test/include/have_maria.inc
Normal file
4
mysql-test/include/have_maria.inc
Normal file
@ -0,0 +1,4 @@
|
||||
-- require r/have_maria.require
|
||||
disable_query_log;
|
||||
show variables like "have_maria";
|
||||
enable_query_log;
|
@ -86,7 +86,7 @@ slo_val val
|
||||
20 0
|
||||
1 0
|
||||
"Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10"
|
||||
SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
|
||||
SELECT user_host, query_time, db, sql_text FROM mysql.slow_log where sql_text <> "DROP EVENT long_event";
|
||||
user_host query_time db sql_text
|
||||
USER_HOST SLEEPVAL events_test INSERT INTO slow_event_test SELECT @@long_query_time, SLEEP(2)
|
||||
DROP EVENT long_event2;
|
||||
|
2
mysql-test/r/have_maria.require
Normal file
2
mysql-test/r/have_maria.require
Normal file
@ -0,0 +1,2 @@
|
||||
Variable_name Value
|
||||
have_maria YES
|
1788
mysql-test/r/maria.result
Normal file
1788
mysql-test/r/maria.result
Normal file
File diff suppressed because it is too large
Load Diff
3137
mysql-test/r/ps_maria.result
Normal file
3137
mysql-test/r/ps_maria.result
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,8 +35,9 @@ synchronization : Bug#24529 Test 'synchronization' fails on Mac pushb
|
||||
#ndb_binlog_ddl_multi : BUG#18976 2006-04-10 kent CRBR: multiple binlog, second binlog may miss schema log events
|
||||
#ndb_binlog_discover : bug#21806 2006-08-24
|
||||
#ndb_autodiscover3 : bug#21806
|
||||
|
||||
mysql_upgrade : Bug#25074 mysql_upgrade gives inconsisten results
|
||||
plugin : Bug#25659 memory leak via "plugins" test
|
||||
rpl_ndb_dd_advance : Bug#25913 rpl_ndb_dd_advance fails randomly
|
||||
ndb_alter_table : Bug##25774 ndb_alter_table.test fails in DBUG_ASSERT() on Linux x64
|
||||
maria : Until maria is fully functional
|
||||
ps_maria : Until maria is fully functional
|
||||
|
@ -94,7 +94,7 @@ CREATE EVENT long_event2 ON SCHEDULE EVERY 1 MINUTE DO INSERT INTO slow_event_te
|
||||
SELECT * FROM slow_event_test;
|
||||
--echo "Check slow log. Should see 1 row because 4 is over the threshold of 3 for GLOBAL, though under SESSION which is 10"
|
||||
--replace_column 1 USER_HOST 2 SLEEPVAL
|
||||
SELECT user_host, query_time, db, sql_text FROM mysql.slow_log;
|
||||
SELECT user_host, query_time, db, sql_text FROM mysql.slow_log where sql_text <> "DROP EVENT long_event";
|
||||
DROP EVENT long_event2;
|
||||
--echo "Make it quite long"
|
||||
SET SESSION long_query_time=300;
|
||||
|
1082
mysql-test/t/maria.test
Normal file
1082
mysql-test/t/maria.test
Normal file
File diff suppressed because it is too large
Load Diff
46
mysql-test/t/ps_maria.test
Normal file
46
mysql-test/t/ps_maria.test
Normal file
@ -0,0 +1,46 @@
|
||||
###############################################
|
||||
# #
|
||||
# Prepared Statements test on MARIA tables #
|
||||
# #
|
||||
###############################################
|
||||
|
||||
#
|
||||
# NOTE: PLEASE SEE ps_1general.test (bottom)
|
||||
# BEFORE ADDING NEW TEST CASES HERE !!!
|
||||
|
||||
use test;
|
||||
|
||||
-- source include/have_maria.inc
|
||||
|
||||
let $type= 'MARIA' ;
|
||||
-- source include/ps_create.inc
|
||||
-- source include/ps_renew.inc
|
||||
|
||||
-- source include/ps_query.inc
|
||||
|
||||
# parameter in SELECT ... MATCH/AGAINST
|
||||
# case derived from client_test.c: test_bug1500()
|
||||
--disable_warnings
|
||||
drop table if exists t2 ;
|
||||
--enable_warnings
|
||||
eval create table t2 (s varchar(25), fulltext(s))
|
||||
ENGINE = $type ;
|
||||
insert into t2 values ('Gravedigger'), ('Greed'),('Hollow Dogs') ;
|
||||
commit ;
|
||||
|
||||
prepare stmt1 from ' select s from t2 where match (s) against (?) ' ;
|
||||
set @arg00='Dogs' ;
|
||||
execute stmt1 using @arg00 ;
|
||||
prepare stmt1 from ' SELECT s FROM t2
|
||||
where match (s) against (concat(?,''digger'')) ';
|
||||
set @arg00='Grave' ;
|
||||
execute stmt1 using @arg00 ;
|
||||
drop table t2 ;
|
||||
|
||||
-- source include/ps_modify.inc
|
||||
-- source include/ps_modify1.inc
|
||||
-- source include/ps_conv.inc
|
||||
|
||||
drop table t1, t9;
|
||||
|
||||
# End of 4.1 tests
|
@ -30,7 +30,8 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
|
||||
mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \
|
||||
my_malloc.c my_realloc.c my_once.c mulalloc.c \
|
||||
my_alloc.c safemalloc.c my_new.cc \
|
||||
my_vle.c my_atomic.c \
|
||||
my_vle.c my_atomic.c lf_hash.c \
|
||||
lf_dynarray.c lf_alloc-pin.c \
|
||||
my_fopen.c my_fstream.c my_getsystime.c \
|
||||
my_error.c errors.c my_div.c my_messnc.c \
|
||||
mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \
|
||||
@ -52,7 +53,8 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
|
||||
my_gethostbyname.c rijndael.c my_aes.c sha1.c \
|
||||
my_handler.c my_netware.c my_largepage.c \
|
||||
my_memmem.c \
|
||||
my_windac.c my_access.c base64.c my_libwrap.c
|
||||
my_windac.c my_access.c base64.c my_libwrap.c \
|
||||
mf_pagecache.c wqueue.c
|
||||
EXTRA_DIST = thr_alarm.c thr_lock.c my_pthread.c my_thr_init.c \
|
||||
thr_mutex.c thr_rwlock.c \
|
||||
CMakeLists.txt mf_soundex.c \
|
||||
@ -126,5 +128,6 @@ test_base64$(EXEEXT): base64.c $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN ./test_base64.c $(LDADD) $(LIBS)
|
||||
$(RM) -f ./test_base64.c
|
||||
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
%::SCCS/s.%
|
||||
|
@ -60,7 +60,8 @@ my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
|
||||
array->max_element=init_alloc;
|
||||
array->alloc_increment=alloc_increment;
|
||||
array->size_of_element=element_size;
|
||||
if (!(array->buffer=(char*) my_malloc_ci(element_size*init_alloc,MYF(MY_WME))))
|
||||
if (!(array->buffer=(char*) my_malloc_ci(element_size*init_alloc,
|
||||
MYF(MY_WME))))
|
||||
{
|
||||
array->max_element=0;
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -153,7 +154,7 @@ byte *pop_dynamic(DYNAMIC_ARRAY *array)
|
||||
}
|
||||
|
||||
/*
|
||||
Replace elemnent in array with given element and index
|
||||
Replace element in array with given element and index
|
||||
|
||||
SYNOPSIS
|
||||
set_dynamic()
|
||||
@ -174,19 +175,8 @@ my_bool set_dynamic(DYNAMIC_ARRAY *array, gptr element, uint idx)
|
||||
{
|
||||
if (idx >= array->elements)
|
||||
{
|
||||
if (idx >= array->max_element)
|
||||
{
|
||||
uint size;
|
||||
char *new_ptr;
|
||||
size=(idx+array->alloc_increment)/array->alloc_increment;
|
||||
size*= array->alloc_increment;
|
||||
if (!(new_ptr=(char*) my_realloc(array->buffer,size*
|
||||
array->size_of_element,
|
||||
MYF(MY_WME | MY_ALLOW_ZERO_PTR))))
|
||||
return TRUE;
|
||||
array->buffer=new_ptr;
|
||||
array->max_element=size;
|
||||
}
|
||||
if (idx >= array->max_element && allocate_dynamic(array, idx))
|
||||
return TRUE;
|
||||
bzero((gptr) (array->buffer+array->elements*array->size_of_element),
|
||||
(idx - array->elements)*array->size_of_element);
|
||||
array->elements=idx+1;
|
||||
@ -196,6 +186,42 @@ my_bool set_dynamic(DYNAMIC_ARRAY *array, gptr element, uint idx)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Ensure that dynamic array has enough elements
|
||||
|
||||
SYNOPSIS
|
||||
allocate_dynamic()
|
||||
array
|
||||
max_elements Numbers of elements that is needed
|
||||
|
||||
NOTES
|
||||
Any new allocated element are NOT initialized
|
||||
|
||||
RETURN VALUE
|
||||
FALSE Ok
|
||||
TRUE Allocation of new memory failed
|
||||
*/
|
||||
|
||||
my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements)
|
||||
{
|
||||
if (max_elements >= array->max_element)
|
||||
{
|
||||
uint size;
|
||||
char *new_ptr;
|
||||
size= (max_elements + array->alloc_increment)/array->alloc_increment;
|
||||
size*= array->alloc_increment;
|
||||
if (!(new_ptr= (char*) my_realloc(array->buffer,size*
|
||||
array->size_of_element,
|
||||
MYF(MY_WME | MY_ALLOW_ZERO_PTR))))
|
||||
return TRUE;
|
||||
array->buffer= new_ptr;
|
||||
array->max_element= size;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get an element from array by given index
|
||||
|
||||
|
443
mysys/lf_alloc-pin.c
Normal file
443
mysys/lf_alloc-pin.c
Normal file
@ -0,0 +1,443 @@
|
||||
/* QQ: TODO multi-pinbox */
|
||||
/* 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; 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 */
|
||||
|
||||
/*
|
||||
wait-free concurrent allocator based on pinning addresses
|
||||
|
||||
It works as follows: every thread (strictly speaking - every CPU, but
|
||||
it's too difficult to do) has a small array of pointers. They're called
|
||||
"pins". Before using an object its address must be stored in this array
|
||||
(pinned). When an object is no longer necessary its address must be
|
||||
removed from this array (unpinned). When a thread wants to free() an
|
||||
object it scans all pins of all threads to see if somebody has this
|
||||
object pinned. If yes - the object is not freed (but stored in a
|
||||
"purgatory"). To reduce the cost of a single free() pins are not scanned
|
||||
on every free() but only added to (thread-local) purgatory. On every
|
||||
LF_PURGATORY_SIZE free() purgatory is scanned and all unpinned objects
|
||||
are freed.
|
||||
|
||||
Pins are used to solve ABA problem. To use pins one must obey
|
||||
a pinning protocol:
|
||||
1. Let's assume that PTR is a shared pointer to an object. Shared means
|
||||
that any thread may modify it anytime to point to a different object
|
||||
and free the old object. Later the freed object may be potentially
|
||||
allocated by another thread. If we're unlucky that another thread may
|
||||
set PTR to point to this object again. This is ABA problem.
|
||||
2. Create a local pointer LOCAL_PTR.
|
||||
3. Pin the PTR in a loop:
|
||||
do
|
||||
{
|
||||
LOCAL_PTR= PTR;
|
||||
pin(PTR, PIN_NUMBER);
|
||||
} while (LOCAL_PTR != PTR)
|
||||
4. It is guaranteed that after the loop has ended, LOCAL_PTR
|
||||
points to an object (or NULL, if PTR may be NULL), that
|
||||
will never be freed. It is not guaranteed though
|
||||
that LOCAL_PTR == PTR (as PTR can change any time)
|
||||
5. When done working with the object, remove the pin:
|
||||
unpin(PIN_NUMBER)
|
||||
6. When copying pins (as in the list traversing loop:
|
||||
pin(CUR, 1);
|
||||
while ()
|
||||
{
|
||||
do // standard
|
||||
{ // pinning
|
||||
NEXT=CUR->next; // loop
|
||||
pin(NEXT, 0); // see #3
|
||||
} while (NEXT != CUR->next); // above
|
||||
...
|
||||
...
|
||||
CUR=NEXT;
|
||||
pin(CUR, 1); // copy pin[0] to pin[1]
|
||||
}
|
||||
which keeps CUR address constantly pinned), note than pins may be
|
||||
copied only upwards (!!!), that is pin[N] to pin[M], M > N.
|
||||
7. Don't keep the object pinned longer than necessary - the number of
|
||||
pins you have is limited (and small), keeping an object pinned
|
||||
prevents its reuse and cause unnecessary mallocs.
|
||||
|
||||
Implementation details:
|
||||
Pins are given away from a "pinbox". Pinbox is stack-based allocator.
|
||||
It used dynarray for storing pins, new elements are allocated by dynarray
|
||||
as necessary, old are pushed in the stack for reuse. ABA is solved by
|
||||
versioning a pointer - because we use an array, a pointer to pins is 32 bit,
|
||||
upper 32 bits are used for a version.
|
||||
*/
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <lf.h>
|
||||
|
||||
#define LF_PINBOX_MAX_PINS 65536
|
||||
|
||||
static void _lf_pinbox_real_free(LF_PINS *pins);
|
||||
|
||||
/*
|
||||
Initialize a pinbox. Normally called from lf_alloc_init.
|
||||
See the latter for details.
|
||||
*/
|
||||
void lf_pinbox_init(LF_PINBOX *pinbox, uint free_ptr_offset,
|
||||
lf_pinbox_free_func *free_func, void *free_func_arg)
|
||||
{
|
||||
DBUG_ASSERT(sizeof(LF_PINS) == 128);
|
||||
DBUG_ASSERT(free_ptr_offset % sizeof(void *) == 0);
|
||||
lf_dynarray_init(&pinbox->pinstack, sizeof(LF_PINS));
|
||||
pinbox->pinstack_top_ver= 0;
|
||||
pinbox->pins_in_stack= 0;
|
||||
pinbox->free_ptr_offset= free_ptr_offset;
|
||||
pinbox->free_func= free_func;
|
||||
pinbox->free_func_arg= free_func_arg;
|
||||
}
|
||||
|
||||
void lf_pinbox_destroy(LF_PINBOX *pinbox)
|
||||
{
|
||||
lf_dynarray_destroy(&pinbox->pinstack);
|
||||
}
|
||||
|
||||
/*
|
||||
Get pins from a pinbox. Usually called via lf_alloc_get_pins() or
|
||||
lf_hash_get_pins().
|
||||
|
||||
DESCRIPTION
|
||||
get a new LF_PINS structure from a stack of unused pins,
|
||||
or allocate a new one out of dynarray.
|
||||
*/
|
||||
LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox)
|
||||
{
|
||||
uint32 pins, next, top_ver;
|
||||
LF_PINS *el;
|
||||
|
||||
top_ver= pinbox->pinstack_top_ver;
|
||||
do
|
||||
{
|
||||
if (!(pins= top_ver % LF_PINBOX_MAX_PINS))
|
||||
{
|
||||
pins= my_atomic_add32(&pinbox->pins_in_stack, 1)+1;
|
||||
el= (LF_PINS *)_lf_dynarray_lvalue(&pinbox->pinstack, pins);
|
||||
break;
|
||||
}
|
||||
el= (LF_PINS *)_lf_dynarray_value(&pinbox->pinstack, pins);
|
||||
next= el->link;
|
||||
} while (!my_atomic_cas32(&pinbox->pinstack_top_ver, &top_ver,
|
||||
top_ver-pins+next+LF_PINBOX_MAX_PINS));
|
||||
el->link= pins;
|
||||
el->purgatory_count= 0;
|
||||
el->pinbox= pinbox;
|
||||
return el;
|
||||
}
|
||||
|
||||
/*
|
||||
Put pins back to a pinbox. Usually called via lf_alloc_put_pins() or
|
||||
lf_hash_put_pins().
|
||||
|
||||
DESCRIPTION
|
||||
empty the purgatory (XXX deadlock warning below!),
|
||||
push LF_PINS structure to a stack
|
||||
*/
|
||||
void _lf_pinbox_put_pins(LF_PINS *pins)
|
||||
{
|
||||
LF_PINBOX *pinbox= pins->pinbox;
|
||||
uint32 top_ver, nr;
|
||||
nr= pins->link;
|
||||
#ifdef MY_LF_EXTRA_DEBUG
|
||||
{
|
||||
int i;
|
||||
for (i= 0; i < LF_PINBOX_PINS; i++)
|
||||
DBUG_ASSERT(pins->pin[i] == 0);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
XXX this will deadlock if other threads will wait for
|
||||
the caller to do something after _lf_pinbox_put_pins(),
|
||||
and they would have pinned addresses that the caller wants to free.
|
||||
Thus: only free pins when all work is done and nobody can wait for you!!!
|
||||
*/
|
||||
while (pins->purgatory_count)
|
||||
{
|
||||
_lf_pinbox_real_free(pins);
|
||||
if (pins->purgatory_count)
|
||||
{
|
||||
my_atomic_rwlock_wrunlock(&pins->pinbox->pinstack.lock);
|
||||
pthread_yield();
|
||||
my_atomic_rwlock_wrlock(&pins->pinbox->pinstack.lock);
|
||||
}
|
||||
}
|
||||
top_ver= pinbox->pinstack_top_ver;
|
||||
if (nr == pinbox->pins_in_stack)
|
||||
{
|
||||
int32 tmp= nr;
|
||||
if (my_atomic_cas32(&pinbox->pins_in_stack, &tmp, tmp-1))
|
||||
goto ret;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
pins->link= top_ver % LF_PINBOX_MAX_PINS;
|
||||
} while (!my_atomic_cas32(&pinbox->pinstack_top_ver, &top_ver,
|
||||
top_ver-pins->link+nr+LF_PINBOX_MAX_PINS));
|
||||
ret:
|
||||
return;
|
||||
}
|
||||
|
||||
static int ptr_cmp(void **a, void **b)
|
||||
{
|
||||
return *a < *b ? -1 : *a == *b ? 0 : 1;
|
||||
}
|
||||
|
||||
#define add_to_purgatory(PINS, ADDR) \
|
||||
do \
|
||||
{ \
|
||||
*(void **)((char *)(ADDR)+(PINS)->pinbox->free_ptr_offset)= \
|
||||
(PINS)->purgatory; \
|
||||
(PINS)->purgatory= (ADDR); \
|
||||
(PINS)->purgatory_count++; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
Free an object allocated via pinbox allocator
|
||||
|
||||
DESCRIPTION
|
||||
add an object to purgatory. if necessary, call _lf_pinbox_real_free()
|
||||
to actually free something.
|
||||
*/
|
||||
void _lf_pinbox_free(LF_PINS *pins, void *addr)
|
||||
{
|
||||
add_to_purgatory(pins, addr);
|
||||
if (pins->purgatory_count % LF_PURGATORY_SIZE)
|
||||
_lf_pinbox_real_free(pins);
|
||||
}
|
||||
|
||||
struct st_harvester {
|
||||
void **granary;
|
||||
int npins;
|
||||
};
|
||||
|
||||
/*
|
||||
callback for _lf_dynarray_iterate:
|
||||
scan all pins or all threads and accumulate all pins
|
||||
*/
|
||||
static int harvest_pins(LF_PINS *el, struct st_harvester *hv)
|
||||
{
|
||||
int i;
|
||||
LF_PINS *el_end= el+min(hv->npins, LF_DYNARRAY_LEVEL_LENGTH);
|
||||
for (; el < el_end; el++)
|
||||
{
|
||||
for (i= 0; i < LF_PINBOX_PINS; i++)
|
||||
{
|
||||
void *p= el->pin[i];
|
||||
if (p)
|
||||
*hv->granary++= p;
|
||||
}
|
||||
}
|
||||
hv->npins-= LF_DYNARRAY_LEVEL_LENGTH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
callback for _lf_dynarray_iterate:
|
||||
scan all pins or all threads and see if addr is present there
|
||||
*/
|
||||
static int match_pins(LF_PINS *el, void *addr)
|
||||
{
|
||||
int i;
|
||||
LF_PINS *el_end= el+LF_DYNARRAY_LEVEL_LENGTH;
|
||||
for (; el < el_end; el++)
|
||||
for (i= 0; i < LF_PINBOX_PINS; i++)
|
||||
if (el->pin[i] == addr)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Scan the purgatory as free everything that can be freed
|
||||
*/
|
||||
static void _lf_pinbox_real_free(LF_PINS *pins)
|
||||
{
|
||||
int npins;
|
||||
void *list;
|
||||
void **addr;
|
||||
LF_PINBOX *pinbox= pins->pinbox;
|
||||
|
||||
npins= pinbox->pins_in_stack+1;
|
||||
|
||||
#ifdef HAVE_ALLOCA
|
||||
/* create a sorted list of pinned addresses, to speed up searches */
|
||||
if (sizeof(void *)*LF_PINBOX_PINS*npins < my_thread_stack_size)
|
||||
{
|
||||
struct st_harvester hv;
|
||||
addr= (void **) alloca(sizeof(void *)*LF_PINBOX_PINS*npins);
|
||||
hv.granary= addr;
|
||||
hv.npins= npins;
|
||||
/* scan the dynarray and accumulate all pinned addresses */
|
||||
_lf_dynarray_iterate(&pinbox->pinstack,
|
||||
(lf_dynarray_func)harvest_pins, &hv);
|
||||
|
||||
npins= hv.granary-addr;
|
||||
/* and sort them */
|
||||
if (npins)
|
||||
qsort(addr, npins, sizeof(void *), (qsort_cmp)ptr_cmp);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
addr= 0;
|
||||
|
||||
list= pins->purgatory;
|
||||
pins->purgatory= 0;
|
||||
pins->purgatory_count= 0;
|
||||
while (list)
|
||||
{
|
||||
void *cur= list;
|
||||
list= *(void **)((char *)cur+pinbox->free_ptr_offset);
|
||||
if (npins)
|
||||
{
|
||||
if (addr) /* use binary search */
|
||||
{
|
||||
void **a, **b, **c;
|
||||
for (a= addr, b= addr+npins-1, c= a+(b-a)/2; b-a>1; c= a+(b-a)/2)
|
||||
if (cur == *c)
|
||||
a= b= c;
|
||||
else if (cur > *c)
|
||||
a= c;
|
||||
else
|
||||
b= c;
|
||||
if (cur == *a || cur == *b)
|
||||
goto found;
|
||||
}
|
||||
else /* no alloca - no cookie. linear search here */
|
||||
{
|
||||
if (_lf_dynarray_iterate(&pinbox->pinstack,
|
||||
(lf_dynarray_func)match_pins, cur))
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* not pinned - freeing */
|
||||
pinbox->free_func(cur, pinbox->free_func_arg);
|
||||
continue;
|
||||
found:
|
||||
/* pinned - keeping */
|
||||
add_to_purgatory(pins, cur);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
callback for _lf_pinbox_real_free to free an unpinned object -
|
||||
add it back to the allocator stack
|
||||
*/
|
||||
static void alloc_free(struct st_lf_alloc_node *node, LF_ALLOCATOR *allocator)
|
||||
{
|
||||
struct st_lf_alloc_node *tmp;
|
||||
tmp= allocator->top;
|
||||
do
|
||||
{
|
||||
node->next= tmp;
|
||||
} while (!my_atomic_casptr((void **)&allocator->top, (void **)&tmp, node) &&
|
||||
LF_BACKOFF);
|
||||
}
|
||||
|
||||
/* lock-free memory allocator for fixed-size objects */
|
||||
|
||||
LF_REQUIRE_PINS(1);
|
||||
|
||||
/*
|
||||
initialize lock-free allocatod.
|
||||
|
||||
SYNOPSYS
|
||||
allocator -
|
||||
size a size of an object to allocate
|
||||
free_ptr_offset an offset inside the object to a sizeof(void *)
|
||||
memory that is guaranteed to be unused after
|
||||
the object is put in the purgatory. Unused by ANY
|
||||
thread, not only the purgatory owner.
|
||||
*/
|
||||
void lf_alloc_init(LF_ALLOCATOR *allocator, uint size, uint free_ptr_offset)
|
||||
{
|
||||
lf_pinbox_init(&allocator->pinbox, free_ptr_offset,
|
||||
(lf_pinbox_free_func *)alloc_free, allocator);
|
||||
allocator->top= 0;
|
||||
allocator->mallocs= 0;
|
||||
allocator->element_size= size;
|
||||
DBUG_ASSERT(size >= (int)sizeof(void *));
|
||||
DBUG_ASSERT(free_ptr_offset < size);
|
||||
}
|
||||
|
||||
/*
|
||||
destroy the allocator, free everything that's in it
|
||||
*/
|
||||
void lf_alloc_destroy(LF_ALLOCATOR *allocator)
|
||||
{
|
||||
struct st_lf_alloc_node *node= allocator->top;
|
||||
while (node)
|
||||
{
|
||||
struct st_lf_alloc_node *tmp= node->next;
|
||||
my_free((void *)node, MYF(0));
|
||||
node= tmp;
|
||||
}
|
||||
lf_pinbox_destroy(&allocator->pinbox);
|
||||
allocator->top= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate and return an new object.
|
||||
|
||||
DESCRIPTION
|
||||
Pop an unused object from the stack or malloc it is the stack is empty.
|
||||
pin[0] is used, it's removed on return.
|
||||
*/
|
||||
void *_lf_alloc_new(LF_PINS *pins)
|
||||
{
|
||||
LF_ALLOCATOR *allocator= (LF_ALLOCATOR *)(pins->pinbox->free_func_arg);
|
||||
struct st_lf_alloc_node *node;
|
||||
for (;;)
|
||||
{
|
||||
do
|
||||
{
|
||||
node= allocator->top;
|
||||
_lf_pin(pins, 0, node);
|
||||
} while (node != allocator->top && LF_BACKOFF);
|
||||
if (!node)
|
||||
{
|
||||
if (!(node= (void *)my_malloc(allocator->element_size,
|
||||
MYF(MY_WME|MY_ZEROFILL))))
|
||||
break;
|
||||
#ifdef MY_LF_EXTRA_DEBUG
|
||||
my_atomic_add32(&allocator->mallocs, 1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if (my_atomic_casptr((void **)&allocator->top,
|
||||
(void *)&node, *(void **)node))
|
||||
break;
|
||||
}
|
||||
_lf_unpin(pins, 0);
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
count the number of objects in a pool.
|
||||
|
||||
NOTE
|
||||
This is NOT thread-safe !!!
|
||||
*/
|
||||
uint lf_alloc_in_pool(LF_ALLOCATOR *allocator)
|
||||
{
|
||||
uint i;
|
||||
struct st_lf_alloc_node *node;
|
||||
for (node= allocator->top, i= 0; node; node= node->next, i++)
|
||||
/* no op */;
|
||||
return i;
|
||||
}
|
||||
|
204
mysys/lf_dynarray.c
Normal file
204
mysys/lf_dynarray.c
Normal file
@ -0,0 +1,204 @@
|
||||
/* 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; 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 */
|
||||
|
||||
/*
|
||||
Analog of DYNAMIC_ARRAY that never reallocs
|
||||
(so no pointer into the array may ever become invalid).
|
||||
|
||||
Memory is allocated in non-contiguous chunks.
|
||||
This data structure is not space efficient for sparse arrays.
|
||||
|
||||
The number of elements is limited to 4311810304
|
||||
|
||||
Every element is aligned to sizeof(element) boundary
|
||||
(to avoid false sharing if element is big enough).
|
||||
|
||||
LF_DYNARRAY is a recursive structure. On the zero level
|
||||
LF_DYNARRAY::level[0] it's an array of LF_DYNARRAY_LEVEL_LENGTH elements,
|
||||
on the first level it's an array of LF_DYNARRAY_LEVEL_LENGTH pointers
|
||||
to arrays of elements, on the second level it's an array of pointers
|
||||
to arrays of pointers to arrays of elements. And so on.
|
||||
|
||||
Actually, it's wait-free, not lock-free ;-)
|
||||
*/
|
||||
|
||||
#include <my_global.h>
|
||||
#include <strings.h>
|
||||
#include <my_sys.h>
|
||||
#include <lf.h>
|
||||
|
||||
void lf_dynarray_init(LF_DYNARRAY *array, uint element_size)
|
||||
{
|
||||
bzero(array, sizeof(*array));
|
||||
array->size_of_element= element_size;
|
||||
my_atomic_rwlock_init(&array->lock);
|
||||
}
|
||||
|
||||
static void recursive_free(void **alloc, int level)
|
||||
{
|
||||
if (!alloc)
|
||||
return;
|
||||
|
||||
if (level)
|
||||
{
|
||||
int i;
|
||||
for (i= 0; i < LF_DYNARRAY_LEVEL_LENGTH; i++)
|
||||
recursive_free(alloc[i], level-1);
|
||||
my_free((void *)alloc, MYF(0));
|
||||
}
|
||||
else
|
||||
my_free(alloc[-1], MYF(0));
|
||||
}
|
||||
|
||||
void lf_dynarray_destroy(LF_DYNARRAY *array)
|
||||
{
|
||||
int i;
|
||||
for (i= 0; i < LF_DYNARRAY_LEVELS; i++)
|
||||
recursive_free(array->level[i], i);
|
||||
my_atomic_rwlock_destroy(&array->lock);
|
||||
}
|
||||
|
||||
static const ulong dynarray_idxes_in_prev_levels[LF_DYNARRAY_LEVELS]=
|
||||
{
|
||||
0, /* +1 here to to avoid -1's below */
|
||||
LF_DYNARRAY_LEVEL_LENGTH,
|
||||
LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH +
|
||||
LF_DYNARRAY_LEVEL_LENGTH,
|
||||
LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH *
|
||||
LF_DYNARRAY_LEVEL_LENGTH + LF_DYNARRAY_LEVEL_LENGTH *
|
||||
LF_DYNARRAY_LEVEL_LENGTH + LF_DYNARRAY_LEVEL_LENGTH
|
||||
};
|
||||
|
||||
static const ulong dynarray_idxes_in_prev_level[LF_DYNARRAY_LEVELS]=
|
||||
{
|
||||
0, /* +1 here to to avoid -1's below */
|
||||
LF_DYNARRAY_LEVEL_LENGTH,
|
||||
LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH,
|
||||
LF_DYNARRAY_LEVEL_LENGTH * LF_DYNARRAY_LEVEL_LENGTH *
|
||||
LF_DYNARRAY_LEVEL_LENGTH,
|
||||
};
|
||||
|
||||
/*
|
||||
Returns a valid lvalue pointer to the element number 'idx'.
|
||||
Allocates memory if necessary.
|
||||
*/
|
||||
void *_lf_dynarray_lvalue(LF_DYNARRAY *array, uint idx)
|
||||
{
|
||||
void * ptr, * volatile * ptr_ptr= 0;
|
||||
int i;
|
||||
|
||||
for (i= LF_DYNARRAY_LEVELS-1; idx < dynarray_idxes_in_prev_levels[i]; i--)
|
||||
/* no-op */;
|
||||
ptr_ptr= &array->level[i];
|
||||
idx-= dynarray_idxes_in_prev_levels[i];
|
||||
for (; i > 0; i--)
|
||||
{
|
||||
if (!(ptr= *ptr_ptr))
|
||||
{
|
||||
void *alloc= my_malloc(LF_DYNARRAY_LEVEL_LENGTH * sizeof(void *),
|
||||
MYF(MY_WME|MY_ZEROFILL));
|
||||
if (unlikely(!alloc))
|
||||
return(NULL);
|
||||
if (my_atomic_casptr(ptr_ptr, &ptr, alloc))
|
||||
ptr= alloc;
|
||||
else
|
||||
my_free(alloc, MYF(0));
|
||||
}
|
||||
ptr_ptr= ((void **)ptr) + idx / dynarray_idxes_in_prev_level[i];
|
||||
idx%= dynarray_idxes_in_prev_level[i];
|
||||
}
|
||||
if (!(ptr= *ptr_ptr))
|
||||
{
|
||||
void *alloc, *data;
|
||||
alloc= my_malloc(LF_DYNARRAY_LEVEL_LENGTH * array->size_of_element +
|
||||
max(array->size_of_element, sizeof(void *)),
|
||||
MYF(MY_WME|MY_ZEROFILL));
|
||||
if (unlikely(!alloc))
|
||||
return(NULL);
|
||||
/* reserve the space for free() address */
|
||||
data= alloc + sizeof(void *);
|
||||
{ /* alignment */
|
||||
intptr mod= ((intptr)data) % array->size_of_element;
|
||||
if (mod)
|
||||
data+= array->size_of_element - mod;
|
||||
}
|
||||
((void **)data)[-1]= alloc; /* free() will need the original pointer */
|
||||
if (my_atomic_casptr(ptr_ptr, &ptr, data))
|
||||
ptr= data;
|
||||
else
|
||||
my_free(alloc, MYF(0));
|
||||
}
|
||||
return ptr + array->size_of_element * idx;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a pointer to the element number 'idx'
|
||||
or NULL if an element does not exists
|
||||
*/
|
||||
void *_lf_dynarray_value(LF_DYNARRAY *array, uint idx)
|
||||
{
|
||||
void * ptr, * volatile * ptr_ptr= 0;
|
||||
int i;
|
||||
|
||||
for (i= LF_DYNARRAY_LEVELS-1; idx < dynarray_idxes_in_prev_levels[i]; i--)
|
||||
/* no-op */;
|
||||
ptr_ptr= &array->level[i];
|
||||
idx-= dynarray_idxes_in_prev_levels[i];
|
||||
for (; i > 0; i--)
|
||||
{
|
||||
if (!(ptr= *ptr_ptr))
|
||||
return(NULL);
|
||||
ptr_ptr= ((void **)ptr) + idx / dynarray_idxes_in_prev_level[i];
|
||||
idx %= dynarray_idxes_in_prev_level[i];
|
||||
}
|
||||
if (!(ptr= *ptr_ptr))
|
||||
return(NULL);
|
||||
return ptr + array->size_of_element * idx;
|
||||
}
|
||||
|
||||
static int recursive_iterate(LF_DYNARRAY *array, void *ptr, int level,
|
||||
lf_dynarray_func func, void *arg)
|
||||
{
|
||||
int res, i;
|
||||
if (!ptr)
|
||||
return 0;
|
||||
if (!level)
|
||||
return func(ptr, arg);
|
||||
for (i= 0; i < LF_DYNARRAY_LEVEL_LENGTH; i++)
|
||||
if ((res= recursive_iterate(array, ((void **)ptr)[i], level-1, func, arg)))
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Calls func(array, arg) on every array of LF_DYNARRAY_LEVEL_LENGTH elements
|
||||
in lf_dynarray.
|
||||
|
||||
DESCRIPTION
|
||||
lf_dynarray consists of a set of arrays, LF_DYNARRAY_LEVEL_LENGTH elements
|
||||
each. _lf_dynarray_iterate() calls user-supplied function on every array
|
||||
from the set. It is the fastest way to scan the array, faster than
|
||||
for (i=0; i < N; i++) { func(_lf_dynarray_value(dynarray, i)); }
|
||||
*/
|
||||
int _lf_dynarray_iterate(LF_DYNARRAY *array, lf_dynarray_func func, void *arg)
|
||||
{
|
||||
int i, res;
|
||||
for (i= 0; i < LF_DYNARRAY_LEVELS; i++)
|
||||
if ((res= recursive_iterate(array, array->level[i], i, func, arg)))
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
400
mysys/lf_hash.c
Normal file
400
mysys/lf_hash.c
Normal file
@ -0,0 +1,400 @@
|
||||
/* 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; 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 */
|
||||
|
||||
/*
|
||||
extensible hash
|
||||
|
||||
TODO
|
||||
try to get rid of dummy nodes ?
|
||||
for non-unique hash, count only _distinct_ values
|
||||
(but how to do it in lf_hash_delete ?)
|
||||
*/
|
||||
#include <my_global.h>
|
||||
#include <m_string.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_bit.h>
|
||||
#include <lf.h>
|
||||
|
||||
LF_REQUIRE_PINS(3);
|
||||
|
||||
/* An element of the list */
|
||||
typedef struct {
|
||||
intptr volatile link; /* a pointer to the next element in a listand a flag */
|
||||
uint32 hashnr; /* reversed hash number, for sorting */
|
||||
const byte *key;
|
||||
uint keylen;
|
||||
} LF_SLIST;
|
||||
|
||||
/*
|
||||
a structure to pass the context (pointers two the three successive elements
|
||||
in a list) from lfind to linsert/ldelete
|
||||
*/
|
||||
typedef struct {
|
||||
intptr volatile *prev;
|
||||
LF_SLIST *curr, *next;
|
||||
} CURSOR;
|
||||
|
||||
/*
|
||||
the last bit in LF_SLIST::link is a "deleted" flag.
|
||||
the helper macros below convert it to a pure pointer or a pure flag
|
||||
*/
|
||||
#define PTR(V) (LF_SLIST *)((V) & (~(intptr)1))
|
||||
#define DELETED(V) ((V) & 1)
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
Search for hashnr/key/keylen in the list starting from 'head' and
|
||||
position the cursor. The list is ORDER BY hashnr, key
|
||||
|
||||
RETURN
|
||||
0 - not found
|
||||
1 - found
|
||||
|
||||
NOTE
|
||||
cursor is positioned in either case
|
||||
pins[0..2] are used, they are NOT removed on return
|
||||
*/
|
||||
static int lfind(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
|
||||
const byte *key, uint keylen, CURSOR *cursor, LF_PINS *pins)
|
||||
{
|
||||
uint32 cur_hashnr;
|
||||
const byte *cur_key;
|
||||
uint cur_keylen;
|
||||
intptr link;
|
||||
|
||||
retry:
|
||||
cursor->prev= (intptr *)head;
|
||||
do {
|
||||
cursor->curr= PTR(*cursor->prev);
|
||||
_lf_pin(pins, 1, cursor->curr);
|
||||
} while(*cursor->prev != (intptr)cursor->curr && LF_BACKOFF);
|
||||
for (;;)
|
||||
{
|
||||
if (!cursor->curr)
|
||||
return 0;
|
||||
do {
|
||||
/* QQ: XXX or goto retry ? */
|
||||
link= cursor->curr->link;
|
||||
cursor->next= PTR(link);
|
||||
_lf_pin(pins, 0, cursor->next);
|
||||
} while(link != cursor->curr->link && LF_BACKOFF);
|
||||
cur_hashnr= cursor->curr->hashnr;
|
||||
cur_key= cursor->curr->key;
|
||||
cur_keylen= cursor->curr->keylen;
|
||||
if (*cursor->prev != (intptr)cursor->curr)
|
||||
{
|
||||
(void)LF_BACKOFF;
|
||||
goto retry;
|
||||
}
|
||||
if (!DELETED(link))
|
||||
{
|
||||
if (cur_hashnr >= hashnr)
|
||||
{
|
||||
int r= 1;
|
||||
if (cur_hashnr > hashnr ||
|
||||
(r= my_strnncoll(cs, (uchar*) cur_key, cur_keylen, (uchar*) key,
|
||||
keylen)) >= 0)
|
||||
return !r;
|
||||
}
|
||||
cursor->prev= &(cursor->curr->link);
|
||||
_lf_pin(pins, 2, cursor->curr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (my_atomic_casptr((void **)cursor->prev,
|
||||
(void **)&cursor->curr, cursor->next))
|
||||
_lf_alloc_free(pins, cursor->curr);
|
||||
else
|
||||
{
|
||||
(void)LF_BACKOFF;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
cursor->curr= cursor->next;
|
||||
_lf_pin(pins, 1, cursor->curr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
insert a 'node' in the list that starts from 'head' in the correct
|
||||
position (as found by lfind)
|
||||
|
||||
RETURN
|
||||
0 - inserted
|
||||
not 0 - a pointer to a duplicate (not pinned and thus unusable)
|
||||
|
||||
NOTE
|
||||
it uses pins[0..2], on return all pins are removed.
|
||||
*/
|
||||
static LF_SLIST *linsert(LF_SLIST * volatile *head, CHARSET_INFO *cs,
|
||||
LF_SLIST *node, LF_PINS *pins, uint flags)
|
||||
{
|
||||
CURSOR cursor;
|
||||
int res= -1;
|
||||
|
||||
do
|
||||
{
|
||||
if (lfind(head, cs, node->hashnr, node->key, node->keylen,
|
||||
&cursor, pins) &&
|
||||
(flags & LF_HASH_UNIQUE))
|
||||
res= 0; /* duplicate found */
|
||||
else
|
||||
{
|
||||
node->link= (intptr)cursor.curr;
|
||||
assert(node->link != (intptr)node);
|
||||
assert(cursor.prev != &node->link);
|
||||
if (my_atomic_casptr((void **)cursor.prev, (void **)&cursor.curr, node))
|
||||
res= 1; /* inserted ok */
|
||||
}
|
||||
} while (res == -1);
|
||||
_lf_unpin(pins, 0);
|
||||
_lf_unpin(pins, 1);
|
||||
_lf_unpin(pins, 2);
|
||||
return res ? 0 : cursor.curr;
|
||||
}
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
deletes a node as identified by hashnr/keey/keylen from the list
|
||||
that starts from 'head'
|
||||
|
||||
RETURN
|
||||
0 - ok
|
||||
1 - not found
|
||||
|
||||
NOTE
|
||||
it uses pins[0..2], on return all pins are removed.
|
||||
*/
|
||||
static int ldelete(LF_SLIST * volatile *head, CHARSET_INFO *cs, uint32 hashnr,
|
||||
const byte *key, uint keylen, LF_PINS *pins)
|
||||
{
|
||||
CURSOR cursor;
|
||||
int res= -1;
|
||||
|
||||
do
|
||||
{
|
||||
if (!lfind(head, cs, hashnr, key, keylen, &cursor, pins))
|
||||
res= 1;
|
||||
else
|
||||
if (my_atomic_casptr((void **)&(cursor.curr->link),
|
||||
(void **)&cursor.next, 1+(char *)cursor.next))
|
||||
{
|
||||
if (my_atomic_casptr((void **)cursor.prev,
|
||||
(void **)&cursor.curr, cursor.next))
|
||||
_lf_alloc_free(pins, cursor.curr);
|
||||
else
|
||||
lfind(head, cs, hashnr, key, keylen, &cursor, pins);
|
||||
res= 0;
|
||||
}
|
||||
} while (res == -1);
|
||||
_lf_unpin(pins, 0);
|
||||
_lf_unpin(pins, 1);
|
||||
_lf_unpin(pins, 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
searches for a node as identified by hashnr/keey/keylen in the list
|
||||
that starts from 'head'
|
||||
|
||||
RETURN
|
||||
0 - not found
|
||||
node - found
|
||||
|
||||
NOTE
|
||||
it uses pins[0..2], on return the pin[2] keeps the node found
|
||||
all other pins are removed.
|
||||
*/
|
||||
static LF_SLIST *lsearch(LF_SLIST * volatile *head, CHARSET_INFO *cs,
|
||||
uint32 hashnr, const byte *key, uint keylen,
|
||||
LF_PINS *pins)
|
||||
{
|
||||
CURSOR cursor;
|
||||
int res= lfind(head, cs, hashnr, key, keylen, &cursor, pins);
|
||||
if (res) _lf_pin(pins, 2, cursor.curr);
|
||||
_lf_unpin(pins, 0);
|
||||
_lf_unpin(pins, 1);
|
||||
return res ? cursor.curr : 0;
|
||||
}
|
||||
|
||||
static inline const byte* hash_key(const LF_HASH *hash,
|
||||
const byte *record, uint *length)
|
||||
{
|
||||
if (hash->get_key)
|
||||
return (*hash->get_key)(record, length, 0);
|
||||
*length= hash->key_length;
|
||||
return record + hash->key_offset;
|
||||
}
|
||||
|
||||
static inline uint calc_hash(LF_HASH *hash, const byte *key, uint keylen)
|
||||
{
|
||||
ulong nr1= 1, nr2= 4;
|
||||
hash->charset->coll->hash_sort(hash->charset, (uchar*) key, keylen,
|
||||
&nr1, &nr2);
|
||||
return nr1 & INT_MAX32;
|
||||
}
|
||||
|
||||
#define MAX_LOAD 1.0
|
||||
static void initialize_bucket(LF_HASH *, LF_SLIST * volatile*, uint, LF_PINS *);
|
||||
|
||||
/*
|
||||
Initializes lf_hash, the arguments are compatible with hash_init
|
||||
*/
|
||||
void lf_hash_init(LF_HASH *hash, uint element_size, uint flags,
|
||||
uint key_offset, uint key_length, hash_get_key get_key,
|
||||
CHARSET_INFO *charset)
|
||||
{
|
||||
lf_alloc_init(&hash->alloc, sizeof(LF_SLIST)+element_size,
|
||||
offsetof(LF_SLIST, key));
|
||||
lf_dynarray_init(&hash->array, sizeof(LF_SLIST **));
|
||||
hash->size= 1;
|
||||
hash->count= 0;
|
||||
hash->element_size= element_size;
|
||||
hash->flags= flags;
|
||||
hash->charset= charset ? charset : &my_charset_bin;
|
||||
hash->key_offset= key_offset;
|
||||
hash->key_length= key_length;
|
||||
hash->get_key= get_key;
|
||||
DBUG_ASSERT(get_key ? !key_offset && !key_length : key_length);
|
||||
}
|
||||
|
||||
void lf_hash_destroy(LF_HASH *hash)
|
||||
{
|
||||
LF_SLIST *el= *(LF_SLIST **)_lf_dynarray_lvalue(&hash->array, 0);
|
||||
while (el)
|
||||
{
|
||||
intptr next= el->link;
|
||||
if (el->hashnr & 1)
|
||||
lf_alloc_real_free(&hash->alloc, el);
|
||||
else
|
||||
my_free((void *)el, MYF(0));
|
||||
el= (LF_SLIST *)next;
|
||||
}
|
||||
lf_alloc_destroy(&hash->alloc);
|
||||
lf_dynarray_destroy(&hash->array);
|
||||
}
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
inserts a new element to a hash. it will have a _copy_ of
|
||||
data, not a pointer to it.
|
||||
|
||||
RETURN
|
||||
0 - inserted
|
||||
1 - didn't (unique key conflict)
|
||||
|
||||
NOTE
|
||||
see linsert() for pin usage notes
|
||||
*/
|
||||
int lf_hash_insert(LF_HASH *hash, LF_PINS *pins, const void *data)
|
||||
{
|
||||
int csize, bucket, hashnr;
|
||||
LF_SLIST *node, * volatile *el;
|
||||
|
||||
lf_rwlock_by_pins(pins);
|
||||
node= (LF_SLIST *)_lf_alloc_new(pins);
|
||||
memcpy(node+1, data, hash->element_size);
|
||||
node->key= hash_key(hash, (byte *)(node+1), &node->keylen);
|
||||
hashnr= calc_hash(hash, node->key, node->keylen);
|
||||
bucket= hashnr % hash->size;
|
||||
el= _lf_dynarray_lvalue(&hash->array, bucket);
|
||||
if (*el == NULL)
|
||||
initialize_bucket(hash, el, bucket, pins);
|
||||
node->hashnr= my_reverse_bits(hashnr) | 1;
|
||||
if (linsert(el, hash->charset, node, pins, hash->flags))
|
||||
{
|
||||
_lf_alloc_free(pins, node);
|
||||
lf_rwunlock_by_pins(pins);
|
||||
return 1;
|
||||
}
|
||||
csize= hash->size;
|
||||
if ((my_atomic_add32(&hash->count, 1)+1.0) / csize > MAX_LOAD)
|
||||
my_atomic_cas32(&hash->size, &csize, csize*2);
|
||||
lf_rwunlock_by_pins(pins);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
RETURN
|
||||
0 - deleted
|
||||
1 - didn't (not found)
|
||||
NOTE
|
||||
see ldelete() for pin usage notes
|
||||
*/
|
||||
int lf_hash_delete(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
|
||||
{
|
||||
LF_SLIST * volatile *el;
|
||||
uint bucket, hashnr= calc_hash(hash, (byte *)key, keylen);
|
||||
|
||||
bucket= hashnr % hash->size;
|
||||
lf_rwlock_by_pins(pins);
|
||||
el= _lf_dynarray_lvalue(&hash->array, bucket);
|
||||
if (*el == NULL)
|
||||
initialize_bucket(hash, el, bucket, pins);
|
||||
if (ldelete(el, hash->charset, my_reverse_bits(hashnr) | 1,
|
||||
(byte *)key, keylen, pins))
|
||||
{
|
||||
lf_rwunlock_by_pins(pins);
|
||||
return 1;
|
||||
}
|
||||
my_atomic_add32(&hash->count, -1);
|
||||
lf_rwunlock_by_pins(pins);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE
|
||||
see lsearch() for pin usage notes
|
||||
*/
|
||||
void *lf_hash_search(LF_HASH *hash, LF_PINS *pins, const void *key, uint keylen)
|
||||
{
|
||||
LF_SLIST * volatile *el, *found;
|
||||
uint bucket, hashnr= calc_hash(hash, (byte *)key, keylen);
|
||||
|
||||
bucket= hashnr % hash->size;
|
||||
lf_rwlock_by_pins(pins);
|
||||
el= _lf_dynarray_lvalue(&hash->array, bucket);
|
||||
if (*el == NULL)
|
||||
initialize_bucket(hash, el, bucket, pins);
|
||||
found= lsearch(el, hash->charset, my_reverse_bits(hashnr) | 1,
|
||||
(byte *)key, keylen, pins);
|
||||
lf_rwunlock_by_pins(pins);
|
||||
return found ? found+1 : 0;
|
||||
}
|
||||
|
||||
static const char *dummy_key= "";
|
||||
|
||||
static void initialize_bucket(LF_HASH *hash, LF_SLIST * volatile *node,
|
||||
uint bucket, LF_PINS *pins)
|
||||
{
|
||||
uint parent= my_clear_highest_bit(bucket);
|
||||
LF_SLIST *dummy= (LF_SLIST *)my_malloc(sizeof(LF_SLIST), MYF(MY_WME));
|
||||
LF_SLIST **tmp= 0, *cur;
|
||||
LF_SLIST * volatile *el= _lf_dynarray_lvalue(&hash->array, parent);
|
||||
if (*el == NULL && bucket)
|
||||
initialize_bucket(hash, el, parent, pins);
|
||||
dummy->hashnr= my_reverse_bits(bucket);
|
||||
dummy->key= (char*) dummy_key;
|
||||
dummy->keylen= 0;
|
||||
if ((cur= linsert(el, hash->charset, dummy, pins, 0)))
|
||||
{
|
||||
my_free((void *)dummy, MYF(0));
|
||||
dummy= cur;
|
||||
}
|
||||
my_atomic_casptr((void **)node, (void **)&tmp, dummy);
|
||||
}
|
@ -42,6 +42,7 @@
|
||||
#include <keycache.h>
|
||||
#include "my_static.h"
|
||||
#include <m_string.h>
|
||||
#include <my_bit.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
@ -1009,12 +1010,12 @@ static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
|
||||
|
||||
KEYCACHE_THREAD_TRACE("unlink_block");
|
||||
#if defined(KEYCACHE_DEBUG)
|
||||
KEYCACHE_DBUG_ASSERT(keycache->blocks_available != 0);
|
||||
keycache->blocks_available--;
|
||||
KEYCACHE_DBUG_PRINT("unlink_block",
|
||||
("unlinked block %u status=%x #requests=%u #available=%u",
|
||||
BLOCK_NUMBER(block), block->status,
|
||||
block->requests, keycache->blocks_available));
|
||||
KEYCACHE_DBUG_ASSERT(keycache->blocks_available >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1643,9 +1644,9 @@ restart:
|
||||
KEYCACHE_DBUG_ASSERT(page_status != -1);
|
||||
*page_st=page_status;
|
||||
KEYCACHE_DBUG_PRINT("find_key_block",
|
||||
("fd: %d pos: %lu block->status: %u page_status: %u",
|
||||
("fd: %d pos: %lu block->status: %u page_status: %d",
|
||||
file, (ulong) filepos, block->status,
|
||||
(uint) page_status));
|
||||
page_status));
|
||||
|
||||
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
|
||||
DBUG_EXECUTE("check_keycache2",
|
||||
@ -1793,8 +1794,6 @@ byte *key_cache_read(KEY_CACHE *keycache,
|
||||
uint offset= 0;
|
||||
byte *start= buff;
|
||||
DBUG_ENTER("key_cache_read");
|
||||
DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
|
||||
(uint) file, (ulong) filepos, length));
|
||||
|
||||
if (keycache->can_be_used)
|
||||
{
|
||||
@ -1804,6 +1803,11 @@ byte *key_cache_read(KEY_CACHE *keycache,
|
||||
uint status;
|
||||
int page_st;
|
||||
|
||||
DBUG_PRINT("enter", ("fd: %u pos: %lu page: %lu length: %u",
|
||||
(uint) file, (ulong) filepos,
|
||||
(ulong) (filepos / keycache->key_cache_block_size),
|
||||
length));
|
||||
|
||||
offset= (uint) (filepos & (keycache->key_cache_block_size-1));
|
||||
/* Read data in key_cache_block_size increments */
|
||||
do
|
||||
@ -2055,10 +2059,6 @@ int key_cache_write(KEY_CACHE *keycache,
|
||||
reg1 BLOCK_LINK *block;
|
||||
int error=0;
|
||||
DBUG_ENTER("key_cache_write");
|
||||
DBUG_PRINT("enter",
|
||||
("fd: %u pos: %lu length: %u block_length: %u key_block_length: %u",
|
||||
(uint) file, (ulong) filepos, length, block_length,
|
||||
keycache ? keycache->key_cache_block_size : 0));
|
||||
|
||||
if (!dont_write)
|
||||
{
|
||||
@ -2080,6 +2080,12 @@ int key_cache_write(KEY_CACHE *keycache,
|
||||
int page_st;
|
||||
uint offset;
|
||||
|
||||
DBUG_PRINT("enter",
|
||||
("fd: %u pos: %lu page: %lu length: %u block_length: %u",
|
||||
(uint) file, (ulong) filepos,
|
||||
(ulong) (filepos / keycache->key_cache_block_size),
|
||||
length, block_length));
|
||||
|
||||
offset= (uint) (filepos & (keycache->key_cache_block_size-1));
|
||||
do
|
||||
{
|
||||
|
@ -147,7 +147,8 @@ static void safe_hash_free(SAFE_HASH *hash)
|
||||
Return the value stored for a key or default value if no key
|
||||
*/
|
||||
|
||||
static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length)
|
||||
static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length,
|
||||
byte *def)
|
||||
{
|
||||
byte *result;
|
||||
DBUG_ENTER("safe_hash_search");
|
||||
@ -155,7 +156,7 @@ static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length)
|
||||
result= hash_search(&hash->hash, key, length);
|
||||
rw_unlock(&hash->mutex);
|
||||
if (!result)
|
||||
result= hash->default_value;
|
||||
result= def;
|
||||
else
|
||||
result= ((SAFE_HASH_ENTRY*) result)->data;
|
||||
DBUG_PRINT("exit",("data: 0x%lx", (long) result));
|
||||
@ -315,6 +316,7 @@ void multi_keycache_free(void)
|
||||
multi_key_cache_search()
|
||||
key key to find (usually table path)
|
||||
uint length Length of key.
|
||||
def Default value if no key cache
|
||||
|
||||
NOTES
|
||||
This function is coded in such a way that we will return the
|
||||
@ -325,11 +327,13 @@ void multi_keycache_free(void)
|
||||
key cache to use
|
||||
*/
|
||||
|
||||
KEY_CACHE *multi_key_cache_search(byte *key, uint length)
|
||||
KEY_CACHE *multi_key_cache_search(byte *key, uint length,
|
||||
KEY_CACHE *def)
|
||||
{
|
||||
if (!key_cache_hash.hash.records)
|
||||
return dflt_key_cache;
|
||||
return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length);
|
||||
return def;
|
||||
return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length,
|
||||
(void*) def);
|
||||
}
|
||||
|
||||
|
||||
|
4102
mysys/mf_pagecache.c
Executable file
4102
mysys/mf_pagecache.c
Executable file
File diff suppressed because it is too large
Load Diff
@ -17,11 +17,10 @@
|
||||
#include <my_pthread.h>
|
||||
|
||||
#ifndef HAVE_INLINE
|
||||
/*
|
||||
the following will cause all inline functions to be instantiated
|
||||
*/
|
||||
/* the following will cause all inline functions to be instantiated */
|
||||
#define HAVE_INLINE
|
||||
#define static extern
|
||||
#undef STATIC_INLINE
|
||||
#define STATIC_INLINE extern
|
||||
#endif
|
||||
|
||||
#include <my_atomic.h>
|
||||
@ -35,7 +34,7 @@
|
||||
*/
|
||||
int my_atomic_initialize()
|
||||
{
|
||||
DBUG_ASSERT(sizeof(intptr) == sizeof(void *));
|
||||
compile_time_assert(sizeof(intptr) == sizeof(void *));
|
||||
/* currently the only thing worth checking is SMP/UP issue */
|
||||
#ifdef MY_ATOMIC_MODE_DUMMY
|
||||
return my_getncpus() == 1 ? MY_ATOMIC_OK : MY_ATOMIC_NOT_1CPU;
|
||||
|
100
mysys/my_bit.c
100
mysys/my_bit.c
@ -13,23 +13,18 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Some useful bit functions */
|
||||
#include <my_global.h>
|
||||
|
||||
#include "mysys_priv.h"
|
||||
#ifndef HAVE_INLINE
|
||||
/* the following will cause all inline functions to be instantiated */
|
||||
#define HAVE_INLINE
|
||||
#undef STATIC_INLINE
|
||||
#define STATIC_INLINE extern
|
||||
#endif
|
||||
|
||||
/*
|
||||
Find smallest X in 2^X >= value
|
||||
This can be used to divide a number with value by doing a shift instead
|
||||
*/
|
||||
#include <my_bit.h>
|
||||
|
||||
uint my_bit_log2(ulong value)
|
||||
{
|
||||
uint bit;
|
||||
for (bit=0 ; value > 1 ; value>>=1, bit++) ;
|
||||
return bit;
|
||||
}
|
||||
|
||||
static char nbits[256] = {
|
||||
const char _my_bits_nbits[256] = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||
@ -48,60 +43,29 @@ static char nbits[256] = {
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
|
||||
};
|
||||
|
||||
uint my_count_bits(ulonglong v)
|
||||
{
|
||||
#if SIZEOF_LONG_LONG > 4
|
||||
/* The following code is a bit faster on 16 bit machines than if we would
|
||||
only shift v */
|
||||
ulong v2=(ulong) (v >> 32);
|
||||
return (uint) (uchar) (nbits[(uchar) v] +
|
||||
nbits[(uchar) (v >> 8)] +
|
||||
nbits[(uchar) (v >> 16)] +
|
||||
nbits[(uchar) (v >> 24)] +
|
||||
nbits[(uchar) (v2)] +
|
||||
nbits[(uchar) (v2 >> 8)] +
|
||||
nbits[(uchar) (v2 >> 16)] +
|
||||
nbits[(uchar) (v2 >> 24)]);
|
||||
#else
|
||||
return (uint) (uchar) (nbits[(uchar) v] +
|
||||
nbits[(uchar) (v >> 8)] +
|
||||
nbits[(uchar) (v >> 16)] +
|
||||
nbits[(uchar) (v >> 24)]);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint my_count_bits_ushort(ushort v)
|
||||
{
|
||||
return nbits[v];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Next highest power of two
|
||||
|
||||
SYNOPSIS
|
||||
my_round_up_to_next_power()
|
||||
v Value to check
|
||||
|
||||
RETURN
|
||||
Next or equal power of 2
|
||||
Note: 0 will return 0
|
||||
|
||||
NOTES
|
||||
Algorithm by Sean Anderson, according to:
|
||||
http://graphics.stanford.edu/~seander/bithacks.html
|
||||
(Orignal code public domain)
|
||||
|
||||
Comments shows how this works with 01100000000000000000000000001011
|
||||
perl -e 'print map{", 0x".unpack H2,pack B8,unpack b8,chr$_}(0..255)'
|
||||
*/
|
||||
const uchar _my_bits_reverse_table[256]={
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30,
|
||||
0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98,
|
||||
0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64,
|
||||
0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC,
|
||||
0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02,
|
||||
0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2,
|
||||
0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A,
|
||||
0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
|
||||
0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E,
|
||||
0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81,
|
||||
0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71,
|
||||
0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9,
|
||||
0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15,
|
||||
0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD,
|
||||
0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43,
|
||||
0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B,
|
||||
0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97,
|
||||
0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F,
|
||||
0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
};
|
||||
|
||||
uint32 my_round_up_to_next_power(uint32 v)
|
||||
{
|
||||
v--; /* 01100000000000000000000000001010 */
|
||||
v|= v >> 1; /* 01110000000000000000000000001111 */
|
||||
v|= v >> 2; /* 01111100000000000000000000001111 */
|
||||
v|= v >> 4; /* 01111111110000000000000000001111 */
|
||||
v|= v >> 8; /* 01111111111111111100000000001111 */
|
||||
v|= v >> 16; /* 01111111111111111111111111111111 */
|
||||
return v+1; /* 10000000000000000000000000000000 */
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "mysys_priv.h"
|
||||
#include <my_bitmap.h>
|
||||
#include <m_string.h>
|
||||
#include <my_bit.h>
|
||||
|
||||
void create_last_word_mask(MY_BITMAP *map)
|
||||
{
|
||||
|
@ -52,6 +52,13 @@ File my_create(const char *FileName, int CreateFlags, int access_flags,
|
||||
fd = open(FileName, access_flags);
|
||||
#endif
|
||||
|
||||
if ((MyFlags & MY_SYNC_DIR) && (fd >=0) &&
|
||||
my_sync_dir_by_file(FileName, MyFlags))
|
||||
{
|
||||
my_close(fd, MyFlags);
|
||||
fd= -1;
|
||||
}
|
||||
|
||||
DBUG_RETURN(my_register_filename(fd, FileName, FILE_BY_CREATE,
|
||||
EE_CANTCREATEFILE, MyFlags));
|
||||
} /* my_create */
|
||||
|
@ -29,6 +29,9 @@ int my_delete(const char *name, myf MyFlags)
|
||||
my_error(EE_DELETE,MYF(ME_BELL+ME_WAITTANG+(MyFlags & ME_NOINPUT)),
|
||||
name,errno);
|
||||
}
|
||||
else if ((MyFlags & MY_SYNC_DIR) &&
|
||||
my_sync_dir_by_file(name, MyFlags))
|
||||
err= -1;
|
||||
DBUG_RETURN(err);
|
||||
} /* my_delete */
|
||||
|
||||
|
@ -34,10 +34,6 @@ ulonglong my_getsystime()
|
||||
LARGE_INTEGER t_cnt;
|
||||
if (!offset)
|
||||
{
|
||||
/* strictly speaking there should be a mutex to protect
|
||||
initialization section. But my_getsystime() is called from
|
||||
UUID() code, and UUID() calls are serialized with a mutex anyway
|
||||
*/
|
||||
LARGE_INTEGER li;
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
|
@ -16,9 +16,11 @@
|
||||
MA 02111-1307, USA */
|
||||
|
||||
#include <my_global.h>
|
||||
#include "my_handler.h"
|
||||
#include <m_ctype.h>
|
||||
#include <my_base.h>
|
||||
#include <my_handler.h>
|
||||
|
||||
int mi_compare_text(CHARSET_INFO *charset_info, uchar *a, uint a_length,
|
||||
int ha_compare_text(CHARSET_INFO *charset_info, uchar *a, uint a_length,
|
||||
uchar *b, uint b_length, my_bool part_key,
|
||||
my_bool skip_end_space)
|
||||
{
|
||||
@ -174,7 +176,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
||||
next_key_length=key_length-b_length-pack_length;
|
||||
|
||||
if (piks &&
|
||||
(flag=mi_compare_text(keyseg->charset,a,a_length,b,b_length,
|
||||
(flag=ha_compare_text(keyseg->charset,a,a_length,b,b_length,
|
||||
(my_bool) ((nextflag & SEARCH_PREFIX) &&
|
||||
next_key_length <= 0),
|
||||
(my_bool)!(nextflag & SEARCH_PREFIX))))
|
||||
@ -187,7 +189,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
||||
{
|
||||
uint length=(uint) (end-a), a_length=length, b_length=length;
|
||||
if (piks &&
|
||||
(flag= mi_compare_text(keyseg->charset, a, a_length, b, b_length,
|
||||
(flag= ha_compare_text(keyseg->charset, a, a_length, b, b_length,
|
||||
(my_bool) ((nextflag & SEARCH_PREFIX) &&
|
||||
next_key_length <= 0),
|
||||
(my_bool)!(nextflag & SEARCH_PREFIX))))
|
||||
@ -235,7 +237,7 @@ int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
||||
next_key_length=key_length-b_length-pack_length;
|
||||
|
||||
if (piks &&
|
||||
(flag= mi_compare_text(keyseg->charset,a,a_length,b,b_length,
|
||||
(flag= ha_compare_text(keyseg->charset,a,a_length,b,b_length,
|
||||
(my_bool) ((nextflag & SEARCH_PREFIX) &&
|
||||
next_key_length <= 0),
|
||||
(my_bool) ((nextflag & (SEARCH_FIND |
|
||||
@ -482,12 +484,15 @@ end:
|
||||
|
||||
DESCRIPTION
|
||||
Find the first NULL value in index-suffix values tuple.
|
||||
TODO Consider optimizing this fuction or its use so we don't search for
|
||||
NULL values in completely NOT NULL index suffixes.
|
||||
|
||||
TODO
|
||||
Consider optimizing this function or its use so we don't search for
|
||||
NULL values in completely NOT NULL index suffixes.
|
||||
|
||||
RETURN
|
||||
First key part that has NULL as value in values tuple, or the last key part
|
||||
(with keyseg->type==HA_TYPE_END) if values tuple doesn't contain NULLs.
|
||||
First key part that has NULL as value in values tuple, or the last key
|
||||
part (with keyseg->type==HA_TYPE_END) if values tuple doesn't contain
|
||||
NULLs.
|
||||
*/
|
||||
|
||||
HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a)
|
||||
|
@ -43,6 +43,7 @@ static void netware_init();
|
||||
|
||||
my_bool my_init_done= 0;
|
||||
uint mysys_usage_id= 0; /* Incremented for each my_init() */
|
||||
ulong my_thread_stack_size= 65536;
|
||||
|
||||
static ulong atoi_octal(const char *str)
|
||||
{
|
||||
|
@ -161,6 +161,7 @@ File my_register_filename(File fd, const char *FileName, enum file_type
|
||||
}
|
||||
pthread_mutex_unlock(&THR_LOCK_open);
|
||||
(void) my_close(fd, MyFlags);
|
||||
fd= -1;
|
||||
my_errno=ENOMEM;
|
||||
}
|
||||
else
|
||||
|
@ -51,7 +51,7 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
|
||||
if (!error) /* Seek was successful */
|
||||
{
|
||||
if ((readbytes = (uint) read(Filedes, Buffer, Count)) == -1L)
|
||||
my_errno= errno;
|
||||
my_errno= errno ? errno : -1;
|
||||
|
||||
/*
|
||||
We should seek back, even if read failed. If this fails,
|
||||
@ -67,7 +67,7 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
|
||||
#else
|
||||
if ((error= ((readbytes =
|
||||
(uint) pread(Filedes, Buffer, Count, offset)) != Count)))
|
||||
my_errno= errno;
|
||||
my_errno= errno ? errno : -1;
|
||||
#endif
|
||||
if (error || readbytes != Count)
|
||||
{
|
||||
@ -87,8 +87,10 @@ uint my_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset,
|
||||
my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG),
|
||||
my_filename(Filedes),my_errno);
|
||||
else if (MyFlags & (MY_NABP | MY_FNABP))
|
||||
{
|
||||
my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG),
|
||||
my_filename(Filedes),my_errno);
|
||||
}
|
||||
}
|
||||
if ((int) readbytes == -1 || (MyFlags & (MY_FNABP | MY_NABP)))
|
||||
DBUG_RETURN(MY_FILE_ERROR); /* Return with error */
|
||||
@ -158,7 +160,8 @@ uint my_pwrite(int Filedes, const byte *Buffer, uint Count, my_off_t offset,
|
||||
Count-=writenbytes;
|
||||
offset+=writenbytes;
|
||||
}
|
||||
DBUG_PRINT("error",("Write only %d bytes",writenbytes));
|
||||
DBUG_PRINT("error",("Write only %d bytes, error: %d",
|
||||
writenbytes, my_errno));
|
||||
#ifndef NO_BACKGROUND
|
||||
#ifdef THREAD
|
||||
if (my_thread_var->abort)
|
||||
|
@ -16,8 +16,9 @@
|
||||
#include "mysys_priv.h"
|
||||
#include <my_dir.h>
|
||||
#include "mysys_err.h"
|
||||
|
||||
#include "m_string.h"
|
||||
#undef my_rename
|
||||
|
||||
/* On unix rename deletes to file if it exists */
|
||||
|
||||
int my_rename(const char *from, const char *to, myf MyFlags)
|
||||
@ -60,5 +61,18 @@ int my_rename(const char *from, const char *to, myf MyFlags)
|
||||
if (MyFlags & (MY_FAE+MY_WME))
|
||||
my_error(EE_LINK, MYF(ME_BELL+ME_WAITTANG),from,to,my_errno);
|
||||
}
|
||||
else if (MyFlags & MY_SYNC_DIR)
|
||||
{
|
||||
#ifdef NEED_EXPLICIT_SYNC_DIR
|
||||
/* do only the needed amount of syncs: */
|
||||
char dir_from[FN_REFLEN], dir_to[FN_REFLEN];
|
||||
dirname_part(dir_from, from);
|
||||
dirname_part(dir_to, to);
|
||||
if (my_sync_dir(dir_from, MyFlags) ||
|
||||
(strcmp(dir_from, dir_to) &&
|
||||
my_sync_dir(dir_to, MyFlags)))
|
||||
error= -1;
|
||||
#endif
|
||||
}
|
||||
DBUG_RETURN(error);
|
||||
} /* my_rename */
|
||||
|
@ -84,6 +84,8 @@ int my_symlink(const char *content, const char *linkname, myf MyFlags)
|
||||
if (MyFlags & MY_WME)
|
||||
my_error(EE_CANT_SYMLINK, MYF(0), linkname, content, errno);
|
||||
}
|
||||
else if ((MyFlags & MY_SYNC_DIR) && my_sync_dir_by_file(linkname, MyFlags))
|
||||
result= -1;
|
||||
DBUG_RETURN(result);
|
||||
#endif /* HAVE_READLINK */
|
||||
}
|
||||
|
@ -48,6 +48,16 @@ int my_sync(File fd, myf my_flags)
|
||||
|
||||
do
|
||||
{
|
||||
#if defined(F_FULLFSYNC)
|
||||
/*
|
||||
In Mac OS X >= 10.3 this call is safer than fsync() (it forces the
|
||||
disk's cache and guarantees ordered writes).
|
||||
*/
|
||||
if (!(res= fcntl(fd, F_FULLFSYNC, 0)))
|
||||
break; /* ok */
|
||||
/* Some file systems don't support F_FULLFSYNC and fail above: */
|
||||
DBUG_PRINT("info",("fcntl(F_FULLFSYNC) failed, falling back"));
|
||||
#endif
|
||||
#if defined(HAVE_FDATASYNC)
|
||||
res= fdatasync(fd);
|
||||
#elif defined(HAVE_FSYNC)
|
||||
@ -55,6 +65,7 @@ int my_sync(File fd, myf my_flags)
|
||||
#elif defined(__WIN__)
|
||||
res= _commit(fd);
|
||||
#else
|
||||
#error Cannot find a way to sync a file, durability in danger
|
||||
res= 0; /* No sync (strange OS) */
|
||||
#endif
|
||||
} while (res == -1 && errno == EINTR);
|
||||
@ -66,10 +77,78 @@ int my_sync(File fd, myf my_flags)
|
||||
my_errno= -1; /* Unknown error */
|
||||
if ((my_flags & MY_IGNORE_BADFD) &&
|
||||
(er == EBADF || er == EINVAL || er == EROFS))
|
||||
{
|
||||
DBUG_PRINT("info", ("ignoring errno %d", er));
|
||||
res= 0;
|
||||
}
|
||||
else if (my_flags & MY_WME)
|
||||
my_error(EE_SYNC, MYF(ME_BELL+ME_WAITTANG), my_filename(fd), my_errno);
|
||||
}
|
||||
DBUG_RETURN(res);
|
||||
} /* my_sync */
|
||||
|
||||
|
||||
static const char cur_dir_name[]= {FN_CURLIB, 0};
|
||||
/*
|
||||
Force directory information to disk.
|
||||
|
||||
SYNOPSIS
|
||||
my_sync_dir()
|
||||
dir_name the name of the directory
|
||||
my_flags flags (MY_WME etc)
|
||||
|
||||
RETURN
|
||||
0 if ok, !=0 if error
|
||||
*/
|
||||
int my_sync_dir(const char *dir_name, myf my_flags)
|
||||
{
|
||||
#ifdef NEED_EXPLICIT_SYNC_DIR
|
||||
DBUG_ENTER("my_sync_dir");
|
||||
DBUG_PRINT("my",("Dir: '%s' my_flags: %d", dir_name, my_flags));
|
||||
File dir_fd;
|
||||
int res= 0;
|
||||
const char *correct_dir_name;
|
||||
/* Sometimes the path does not contain an explicit directory */
|
||||
correct_dir_name= (dir_name[0] == 0) ? cur_dir_name : dir_name;
|
||||
/*
|
||||
Syncing a dir may give EINVAL on tmpfs on Linux, which is ok.
|
||||
EIO on the other hand is very important. Hence MY_IGNORE_BADFD.
|
||||
*/
|
||||
if ((dir_fd= my_open(correct_dir_name, O_RDONLY, MYF(my_flags))) >= 0)
|
||||
{
|
||||
if (my_sync(dir_fd, MYF(my_flags | MY_IGNORE_BADFD)))
|
||||
res= 2;
|
||||
if (my_close(dir_fd, MYF(my_flags)))
|
||||
res= 3;
|
||||
}
|
||||
else
|
||||
res= 1;
|
||||
DBUG_RETURN(res);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Force directory information to disk.
|
||||
|
||||
SYNOPSIS
|
||||
my_sync_dir_by_file()
|
||||
file_name the name of a file in the directory
|
||||
my_flags flags (MY_WME etc)
|
||||
|
||||
RETURN
|
||||
0 if ok, !=0 if error
|
||||
*/
|
||||
int my_sync_dir_by_file(const char *file_name, myf my_flags)
|
||||
{
|
||||
#ifdef NEED_EXPLICIT_SYNC_DIR
|
||||
char dir_name[FN_REFLEN];
|
||||
dirname_part(dir_name, file_name);
|
||||
return my_sync_dir(dir_name, my_flags);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
167
mysys/wqueue.c
Normal file
167
mysys/wqueue.c
Normal file
@ -0,0 +1,167 @@
|
||||
|
||||
#include <wqueue.h>
|
||||
|
||||
#define STRUCT_PTR(TYPE, MEMBER, a) \
|
||||
(TYPE *) ((char *) (a) - offsetof(TYPE, MEMBER))
|
||||
/*
|
||||
Link a thread into double-linked queue of waiting threads.
|
||||
|
||||
SYNOPSIS
|
||||
wqueue_link_into_queue()
|
||||
wqueue pointer to the queue structure
|
||||
thread pointer to the thread to be added to the queue
|
||||
|
||||
RETURN VALUE
|
||||
none
|
||||
|
||||
NOTES.
|
||||
Queue is represented by a circular list of the thread structures
|
||||
The list is double-linked of the type (**prev,*next), accessed by
|
||||
a pointer to the last element.
|
||||
*/
|
||||
|
||||
void wqueue_link_into_queue(WQUEUE *wqueue, struct st_my_thread_var *thread)
|
||||
{
|
||||
struct st_my_thread_var *last;
|
||||
if (!(last= wqueue->last_thread))
|
||||
{
|
||||
/* Queue is empty */
|
||||
thread->next= thread;
|
||||
thread->prev= &thread->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
thread->prev= last->next->prev;
|
||||
last->next->prev= &thread->next;
|
||||
thread->next= last->next;
|
||||
last->next= thread;
|
||||
}
|
||||
wqueue->last_thread= thread;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add a thread to single-linked queue of waiting threads
|
||||
|
||||
SYNOPSIS
|
||||
wqueue_add_to_queue()
|
||||
wqueue pointer to the queue structure
|
||||
thread pointer to the thread to be added to the queue
|
||||
|
||||
RETURN VALUE
|
||||
none
|
||||
|
||||
NOTES.
|
||||
Queue is represented by a circular list of the thread structures
|
||||
The list is single-linked of the type (*next), accessed by a pointer
|
||||
to the last element.
|
||||
*/
|
||||
|
||||
void wqueue_add_to_queue(WQUEUE *wqueue, struct st_my_thread_var *thread)
|
||||
{
|
||||
struct st_my_thread_var *last;
|
||||
if (!(last= wqueue->last_thread))
|
||||
thread->next= thread;
|
||||
else
|
||||
{
|
||||
thread->next= last->next;
|
||||
last->next= thread;
|
||||
}
|
||||
wqueue->last_thread= thread;
|
||||
}
|
||||
|
||||
/*
|
||||
Unlink a thread from double-linked queue of waiting threads
|
||||
|
||||
SYNOPSIS
|
||||
wqueue_unlink_from_queue()
|
||||
wqueue pointer to the queue structure
|
||||
thread pointer to the thread to be removed from the queue
|
||||
|
||||
RETURN VALUE
|
||||
none
|
||||
|
||||
NOTES.
|
||||
See NOTES for link_into_queue
|
||||
*/
|
||||
|
||||
void wqueue_unlink_from_queue(WQUEUE *wqueue, struct st_my_thread_var *thread)
|
||||
{
|
||||
if (thread->next == thread)
|
||||
/* The queue contains only one member */
|
||||
wqueue->last_thread= NULL;
|
||||
else
|
||||
{
|
||||
thread->next->prev= thread->prev;
|
||||
*thread->prev= thread->next;
|
||||
if (wqueue->last_thread == thread)
|
||||
wqueue->last_thread= STRUCT_PTR(struct st_my_thread_var, next,
|
||||
thread->prev);
|
||||
}
|
||||
thread->next= NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Remove all threads from queue signaling them to proceed
|
||||
|
||||
SYNOPSIS
|
||||
wqueue_realease_queue()
|
||||
wqueue pointer to the queue structure
|
||||
thread pointer to the thread to be added to the queue
|
||||
|
||||
RETURN VALUE
|
||||
none
|
||||
|
||||
NOTES.
|
||||
See notes for add_to_queue
|
||||
When removed from the queue each thread is signaled via condition
|
||||
variable thread->suspend.
|
||||
*/
|
||||
|
||||
void wqueue_release_queue(WQUEUE *wqueue)
|
||||
{
|
||||
struct st_my_thread_var *last= wqueue->last_thread;
|
||||
struct st_my_thread_var *next= last->next;
|
||||
struct st_my_thread_var *thread;
|
||||
do
|
||||
{
|
||||
thread= next;
|
||||
pthread_cond_signal(&thread->suspend);
|
||||
next= thread->next;
|
||||
thread->next= NULL;
|
||||
}
|
||||
while (thread != last);
|
||||
wqueue->last_thread= NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add thread and wait
|
||||
|
||||
SYNOPSYS
|
||||
wqueue_add_and_wait()
|
||||
wqueue queue to add to
|
||||
thread thread which is waiting
|
||||
lock mutex need for the operation
|
||||
*/
|
||||
|
||||
void wqueue_add_and_wait(WQUEUE *wqueue,
|
||||
struct st_my_thread_var *thread, pthread_mutex_t *lock)
|
||||
{
|
||||
DBUG_ENTER("wqueue_add_and_wait");
|
||||
DBUG_PRINT("enter", ("thread ox%lxcond 0x%lx, mutex 0x%lx",
|
||||
(ulong) thread, (ulong) &thread->suspend, (ulong) lock));
|
||||
wqueue_add_to_queue(wqueue, thread);
|
||||
do
|
||||
{
|
||||
DBUG_PRINT("info", ("wait... cond 0x%lx, mutex 0x%lx",
|
||||
(ulong) &thread->suspend, (ulong) lock));
|
||||
pthread_cond_wait(&thread->suspend, lock);
|
||||
DBUG_PRINT("info", ("wait done cond 0x%lx, mutex 0x%lx, next 0x%lx",
|
||||
(ulong) &thread->suspend, (ulong) lock,
|
||||
(ulong) thread->next));
|
||||
}
|
||||
while (thread->next);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
@ -81,7 +81,7 @@ pthread_handler_t mysql_heartbeat(void *p)
|
||||
1 failure (cannot happen)
|
||||
*/
|
||||
|
||||
static int daemon_example_plugin_init(void *p)
|
||||
static int daemon_example_plugin_init(void *p __attribute__ ((unused)))
|
||||
{
|
||||
|
||||
DBUG_ENTER("daemon_example_plugin_init");
|
||||
@ -147,7 +147,7 @@ static int daemon_example_plugin_init(void *p)
|
||||
|
||||
*/
|
||||
|
||||
static int daemon_example_plugin_deinit(void *p)
|
||||
static int daemon_example_plugin_deinit(void *p __attribute__ ((unused)))
|
||||
{
|
||||
DBUG_ENTER("daemon_example_plugin_deinit");
|
||||
char buffer[HEART_STRING_BUFFER];
|
||||
|
@ -1064,7 +1064,7 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
|
||||
|
||||
void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
|
||||
{
|
||||
uchar *reuse_end= reuse->base + reuse->max_keys * key_length;
|
||||
byte *reuse_end= reuse->base + reuse->max_keys * key_length;
|
||||
for (uint i= 0; i < queue->elements; ++i)
|
||||
{
|
||||
BUFFPEK *bp= (BUFFPEK *) queue_element(queue, i);
|
||||
@ -1135,7 +1135,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
||||
offset= rec_length-res_length;
|
||||
maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1));
|
||||
to_start_filepos= my_b_tell(to_file);
|
||||
strpos= (uchar*) sort_buffer;
|
||||
strpos= sort_buffer;
|
||||
org_max_rows=max_rows= param->max_rows;
|
||||
|
||||
/* The following will fire if there is not enough space in sort_buffer */
|
||||
@ -1147,10 +1147,10 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
||||
DBUG_RETURN(1); /* purecov: inspected */
|
||||
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
|
||||
{
|
||||
buffpek->base= strpos;
|
||||
buffpek->base= (byte*) strpos;
|
||||
buffpek->max_keys= maxcount;
|
||||
strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek,
|
||||
rec_length));
|
||||
rec_length));
|
||||
if (error == -1)
|
||||
goto err; /* purecov: inspected */
|
||||
buffpek->max_keys= buffpek->mem_count; // If less data in buffers than expected
|
||||
@ -1239,7 +1239,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
||||
}
|
||||
}
|
||||
buffpek= (BUFFPEK*) queue_top(&queue);
|
||||
buffpek->base= sort_buffer;
|
||||
buffpek->base= (byte*) sort_buffer;
|
||||
buffpek->max_keys= param->keys;
|
||||
|
||||
/*
|
||||
@ -1274,7 +1274,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
||||
else
|
||||
{
|
||||
register uchar *end;
|
||||
strpos= buffpek->key+offset;
|
||||
strpos= (uchar*) buffpek->key+offset;
|
||||
for (end= strpos+buffpek->mem_count*rec_length ;
|
||||
strpos != end ;
|
||||
strpos+= rec_length)
|
||||
|
@ -481,8 +481,8 @@ int main(int argc,char **argv)
|
||||
printf("\nstatic unsigned int symbols_max_len=%d;\n\n", max_len2);
|
||||
|
||||
printf("\
|
||||
static inline SYMBOL *get_hash_symbol(const char *s,\n\
|
||||
unsigned int len,bool function)\n\
|
||||
static SYMBOL *get_hash_symbol(const char *s,\n\
|
||||
unsigned int len,bool function)\n\
|
||||
{\n\
|
||||
register uchar *hash_map;\n\
|
||||
register const char *cur_str= s;\n\
|
||||
|
@ -20,6 +20,7 @@
|
||||
#pragma interface /* gcc class implementation */
|
||||
#endif
|
||||
|
||||
#include <my_handler.h>
|
||||
#include <ft_global.h>
|
||||
#include <keycache.h>
|
||||
|
||||
@ -259,6 +260,7 @@ enum legacy_db_type
|
||||
DB_TYPE_TABLE_FUNCTION,
|
||||
DB_TYPE_MEMCACHE,
|
||||
DB_TYPE_FALCON,
|
||||
DB_TYPE_MARIA,
|
||||
DB_TYPE_FIRST_DYNAMIC=42,
|
||||
DB_TYPE_DEFAULT=127 // Must be last
|
||||
};
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <hash.h>
|
||||
#include <time.h>
|
||||
#include <ft_global.h>
|
||||
#include <my_bit.h>
|
||||
|
||||
#include "sp_head.h"
|
||||
#include "sp_rcontext.h"
|
||||
|
@ -1326,7 +1326,6 @@ public:
|
||||
|
||||
|
||||
/* for fulltext search */
|
||||
#include <ft_global.h>
|
||||
|
||||
class Item_func_match :public Item_real_func
|
||||
{
|
||||
|
@ -2397,6 +2397,11 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
|
||||
my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
|
||||
0, MYF(MY_WME | MY_WAIT_IF_FULL)))
|
||||
{
|
||||
/*
|
||||
TODO: all operations creating/deleting the index file or a log, should
|
||||
call my_sync_dir() or my_sync_dir_by_file() to be durable.
|
||||
TODO: file creation should be done with my_create() not my_open().
|
||||
*/
|
||||
if (index_file_nr >= 0)
|
||||
my_close(index_file_nr,MYF(0));
|
||||
return TRUE;
|
||||
|
@ -1592,7 +1592,7 @@ extern ulong max_connections,max_connect_errors, connect_timeout;
|
||||
extern ulong slave_net_timeout, slave_trans_retries;
|
||||
extern uint max_user_connections;
|
||||
extern ulong what_to_log,flush_time;
|
||||
extern ulong query_buff_size, thread_stack;
|
||||
extern ulong query_buff_size;
|
||||
extern ulong max_prepared_stmt_count, prepared_stmt_count;
|
||||
extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
|
||||
extern ulong max_binlog_size, max_relay_log_size;
|
||||
@ -1694,7 +1694,7 @@ extern SHOW_COMP_OPTION have_innodb;
|
||||
extern SHOW_COMP_OPTION have_csv_db;
|
||||
extern SHOW_COMP_OPTION have_ndbcluster;
|
||||
extern SHOW_COMP_OPTION have_partition_db;
|
||||
|
||||
extern SHOW_COMP_OPTION have_maria_db;
|
||||
extern handlerton *partition_hton;
|
||||
extern handlerton *myisam_hton;
|
||||
extern handlerton *heap_hton;
|
||||
|
113
sql/mysqld.cc
113
sql/mysqld.cc
@ -16,6 +16,7 @@
|
||||
#include "mysql_priv.h"
|
||||
#include <m_ctype.h>
|
||||
#include <my_dir.h>
|
||||
#include <my_bit.h>
|
||||
#include "slave.h"
|
||||
#include "sql_repl.h"
|
||||
#include "rpl_filter.h"
|
||||
@ -26,6 +27,9 @@
|
||||
#include "events.h"
|
||||
|
||||
#include "../storage/myisam/ha_myisam.h"
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
#include "../storage/maria/ha_maria.h"
|
||||
#endif
|
||||
|
||||
#include "rpl_injector.h"
|
||||
|
||||
@ -469,7 +473,7 @@ uint volatile thread_count, thread_running;
|
||||
ulonglong thd_startup_options;
|
||||
ulong back_log, connect_timeout, concurrency, server_id;
|
||||
ulong table_cache_size, table_def_size;
|
||||
ulong thread_stack, what_to_log;
|
||||
ulong what_to_log;
|
||||
ulong query_buff_size, slow_launch_time, slave_open_temp_tables;
|
||||
ulong open_files_limit, max_binlog_size, max_relay_log_size;
|
||||
ulong slave_net_timeout, slave_trans_retries;
|
||||
@ -531,6 +535,7 @@ char *mysqld_unix_port, *opt_mysql_tmpdir;
|
||||
const char **errmesg; /* Error messages */
|
||||
const char *myisam_recover_options_str="OFF";
|
||||
const char *myisam_stats_method_str="nulls_unequal";
|
||||
const char *maria_stats_method_str="nulls_unequal";
|
||||
|
||||
/* name of reference on left espression in rewritten IN subquery */
|
||||
const char *in_left_expr_name= "<left expr>";
|
||||
@ -2224,7 +2229,7 @@ the thread stack. Please read http://www.mysql.com/doc/en/Linux.html\n\n",
|
||||
{
|
||||
fprintf(stderr,"thd: 0x%lx\n",(long) thd);
|
||||
print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0,
|
||||
thread_stack);
|
||||
my_thread_stack_size);
|
||||
}
|
||||
if (thd)
|
||||
{
|
||||
@ -2368,9 +2373,9 @@ static void start_signal_handler(void)
|
||||
Peculiar things with ia64 platforms - it seems we only have half the
|
||||
stack size in reality, so we have to double it here
|
||||
*/
|
||||
pthread_attr_setstacksize(&thr_attr,thread_stack*2);
|
||||
pthread_attr_setstacksize(&thr_attr,my_thread_stack_size*2);
|
||||
#else
|
||||
pthread_attr_setstacksize(&thr_attr,thread_stack);
|
||||
pthread_attr_setstacksize(&thr_attr,my_thread_stack_size);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -3616,9 +3621,9 @@ int main(int argc, char **argv)
|
||||
Peculiar things with ia64 platforms - it seems we only have half the
|
||||
stack size in reality, so we have to double it here
|
||||
*/
|
||||
pthread_attr_setstacksize(&connection_attrib,thread_stack*2);
|
||||
pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size*2);
|
||||
#else
|
||||
pthread_attr_setstacksize(&connection_attrib,thread_stack);
|
||||
pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size);
|
||||
#endif
|
||||
#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE
|
||||
{
|
||||
@ -3629,15 +3634,15 @@ int main(int argc, char **argv)
|
||||
stack_size/= 2;
|
||||
#endif
|
||||
/* We must check if stack_size = 0 as Solaris 2.9 can return 0 here */
|
||||
if (stack_size && stack_size < thread_stack)
|
||||
if (stack_size && stack_size < my_thread_stack_size)
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_warning("Asked for %lu thread stack, but got %ld",
|
||||
thread_stack, (long) stack_size);
|
||||
my_thread_stack_size, (long) stack_size);
|
||||
#if defined(__ia64__) || defined(__ia64)
|
||||
thread_stack= stack_size*2;
|
||||
my_thread_stack_size= stack_size*2;
|
||||
#else
|
||||
thread_stack= stack_size;
|
||||
my_thread_stack_size= stack_size;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -4886,10 +4891,17 @@ enum options_mysqld
|
||||
OPT_MAX_LENGTH_FOR_SORT_DATA,
|
||||
OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE,
|
||||
OPT_MAX_ERROR_COUNT, OPT_MULTI_RANGE_COUNT, OPT_MYISAM_DATA_POINTER_SIZE,
|
||||
|
||||
OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE,
|
||||
OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE,
|
||||
OPT_MYISAM_USE_MMAP,
|
||||
OPT_MYISAM_USE_MMAP, OPT_MYISAM_REPAIR_THREADS,
|
||||
OPT_MYISAM_STATS_METHOD,
|
||||
|
||||
OPT_MARIA_BLOCK_SIZE,
|
||||
OPT_MARIA_MAX_SORT_FILE_SIZE, OPT_MARIA_SORT_BUFFER_SIZE,
|
||||
OPT_MARIA_USE_MMAP, OPT_MARIA_REPAIR_THREADS,
|
||||
OPT_MARIA_STATS_METHOD,
|
||||
|
||||
OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT,
|
||||
OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT,
|
||||
OPT_OPEN_FILES_LIMIT,
|
||||
@ -4903,7 +4915,7 @@ enum options_mysqld
|
||||
OPT_SORT_BUFFER, OPT_TABLE_OPEN_CACHE, OPT_TABLE_DEF_CACHE,
|
||||
OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE,
|
||||
OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK,
|
||||
OPT_WAIT_TIMEOUT, OPT_MYISAM_REPAIR_THREADS,
|
||||
OPT_WAIT_TIMEOUT,
|
||||
OPT_INNODB_MIRRORED_LOG_GROUPS,
|
||||
OPT_INNODB_LOG_FILES_IN_GROUP,
|
||||
OPT_INNODB_LOG_FILE_SIZE,
|
||||
@ -6042,6 +6054,49 @@ log and this option does nothing anymore.",
|
||||
0
|
||||
#endif
|
||||
, 0, 2, 0, 1, 0},
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
{"maria_block_size", OPT_MARIA_BLOCK_SIZE,
|
||||
"Block size to be used for MARIA index pages.",
|
||||
(gptr*) &maria_block_size,
|
||||
(gptr*) &maria_block_size, 0, GET_ULONG, REQUIRED_ARG,
|
||||
MARIA_KEY_BLOCK_LENGTH, MARIA_MIN_KEY_BLOCK_LENGTH,
|
||||
MARIA_MAX_KEY_BLOCK_LENGTH,
|
||||
0, MARIA_MIN_KEY_BLOCK_LENGTH, 0},
|
||||
{"maria_key_buffer_size", OPT_KEY_BUFFER_SIZE,
|
||||
"The size of the buffer used for index blocks for Maria tables. Increase "
|
||||
"this to get better index handling (for all reads and multiple writes) to "
|
||||
"as much as you can afford; 64M on a 256M machine that mainly runs MySQL "
|
||||
"is quite common.",
|
||||
(gptr*) &maria_key_cache_var.param_buff_size, (gptr*) 0,
|
||||
0, (GET_ULL | GET_ASK_ADDR),
|
||||
REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, ~(ulong) 0, MALLOC_OVERHEAD,
|
||||
IO_SIZE, 0},
|
||||
{"maria_max_sort_file_size", OPT_MARIA_MAX_SORT_FILE_SIZE,
|
||||
"Don't use the fast sort index method to created index if the temporary "
|
||||
"file would get bigger than this.",
|
||||
(gptr*) &global_system_variables.maria_max_sort_file_size,
|
||||
(gptr*) &max_system_variables.maria_max_sort_file_size, 0,
|
||||
GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, (ulonglong) MAX_FILE_SIZE,
|
||||
0, 1024*1024, 0},
|
||||
{"maria_repair_threads", OPT_MARIA_REPAIR_THREADS,
|
||||
"Number of threads to use when repairing maria tables. The value of 1 "
|
||||
"disables parallel repair.",
|
||||
(gptr*) &global_system_variables.maria_repair_threads,
|
||||
(gptr*) &max_system_variables.maria_repair_threads, 0,
|
||||
GET_ULONG, REQUIRED_ARG, 1, 1, ~0L, 0, 1, 0},
|
||||
{"maria_sort_buffer_size", OPT_MARIA_SORT_BUFFER_SIZE,
|
||||
"The buffer that is allocated when sorting the index when doing a REPAIR "
|
||||
"or when creating indexes with CREATE INDEX or ALTER TABLE.",
|
||||
(gptr*) &global_system_variables.maria_sort_buff_size,
|
||||
(gptr*) &max_system_variables.maria_sort_buff_size, 0,
|
||||
GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0},
|
||||
{"maria_stats_method", OPT_MARIA_STATS_METHOD,
|
||||
"Specifies how maria index statistics collection code should threat NULLs. "
|
||||
"Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
|
||||
"\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
|
||||
(gptr*) &maria_stats_method_str, (gptr*) &maria_stats_method_str, 0,
|
||||
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
#endif
|
||||
{"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
|
||||
"Max packetlength to send/receive from to server.",
|
||||
(gptr*) &global_system_variables.max_allowed_packet,
|
||||
@ -6145,12 +6200,6 @@ The minimum value for this variable is 4096.",
|
||||
(gptr*) &myisam_data_pointer_size,
|
||||
(gptr*) &myisam_data_pointer_size, 0, GET_ULONG, REQUIRED_ARG,
|
||||
6, 2, 7, 0, 1, 0},
|
||||
{"myisam_max_extra_sort_file_size", OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE,
|
||||
"Deprecated option",
|
||||
(gptr*) &global_system_variables.myisam_max_extra_sort_file_size,
|
||||
(gptr*) &max_system_variables.myisam_max_extra_sort_file_size,
|
||||
0, GET_ULL, REQUIRED_ARG, (ulonglong) MI_MAX_TEMP_LENGTH,
|
||||
0, (ulonglong) MAX_FILE_SIZE, 0, 1, 0},
|
||||
{"myisam_max_sort_file_size", OPT_MYISAM_MAX_SORT_FILE_SIZE,
|
||||
"Don't use the fast sort index method to created index if the temporary file would get bigger than this.",
|
||||
(gptr*) &global_system_variables.myisam_max_sort_file_size,
|
||||
@ -6363,8 +6412,8 @@ The minimum value for this variable is 4096.",
|
||||
REQUIRED_ARG, 20, 1, 16384, 0, 1, 0},
|
||||
#endif
|
||||
{"thread_stack", OPT_THREAD_STACK,
|
||||
"The stack size for each thread.", (gptr*) &thread_stack,
|
||||
(gptr*) &thread_stack, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK,
|
||||
"The stack size for each thread.", (gptr*) &my_thread_stack_size,
|
||||
(gptr*) &my_thread_stack_size, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK,
|
||||
1024L*128L, ~0L, 0, 1024, 0},
|
||||
{ "time_format", OPT_TIME_FORMAT,
|
||||
"The TIME format (for future).",
|
||||
@ -7109,15 +7158,24 @@ static void mysql_init_variables(void)
|
||||
global_query_id= thread_id= 1L;
|
||||
strmov(server_version, MYSQL_SERVER_VERSION);
|
||||
myisam_recover_options_str= sql_mode_str= "OFF";
|
||||
myisam_stats_method_str= "nulls_unequal";
|
||||
myisam_stats_method_str= maria_stats_method_str= "nulls_unequal";
|
||||
my_bind_addr = htonl(INADDR_ANY);
|
||||
threads.empty();
|
||||
thread_cache.empty();
|
||||
key_caches.empty();
|
||||
if (!(dflt_key_cache= get_or_create_key_cache(default_key_cache_base.str,
|
||||
default_key_cache_base.length)))
|
||||
default_key_cache_base.length)))
|
||||
exit(1);
|
||||
multi_keycache_init(); /* set key_cache_hash.default_value = dflt_key_cache */
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
if (!(maria_key_cache= get_or_create_key_cache(maria_key_cache_base.str,
|
||||
maria_key_cache_base.length)))
|
||||
exit(1);
|
||||
maria_key_cache->param_buff_size= maria_key_cache_var.param_buff_size;
|
||||
maria_key_cache->param_block_size= maria_block_size;
|
||||
#endif
|
||||
|
||||
/* set key_cache_hash.default_value = dflt_key_cache */
|
||||
multi_keycache_init();
|
||||
|
||||
/* Set directory paths */
|
||||
strmake(language, LANGUAGE, sizeof(language)-1);
|
||||
@ -7159,6 +7217,7 @@ static void mysql_init_variables(void)
|
||||
when collecting index statistics for MyISAM tables.
|
||||
*/
|
||||
global_system_variables.myisam_stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
|
||||
global_system_variables.maria_stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
|
||||
|
||||
/* Variables that depends on compile options */
|
||||
#ifndef DBUG_OFF
|
||||
@ -7176,6 +7235,11 @@ static void mysql_init_variables(void)
|
||||
#else
|
||||
have_csv_db= SHOW_OPTION_NO;
|
||||
#endif
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
have_maria_db= SHOW_OPTION_YES;
|
||||
#else
|
||||
have_maria_db= SHOW_OPTION_NO;
|
||||
#endif
|
||||
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
|
||||
have_ndbcluster= SHOW_OPTION_DISABLED;
|
||||
#else
|
||||
@ -7773,7 +7837,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
||||
int method;
|
||||
LINT_INIT(method_conv);
|
||||
|
||||
myisam_stats_method_str= argument;
|
||||
if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
|
||||
{
|
||||
fprintf(stderr, "Invalid value of myisam_stats_method: %s.\n", argument);
|
||||
@ -8267,11 +8330,13 @@ void refresh_status(THD *thd)
|
||||
#undef have_innodb
|
||||
#undef have_ndbcluster
|
||||
#undef have_csv_db
|
||||
#undef have_maria_db
|
||||
|
||||
SHOW_COMP_OPTION have_innodb= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_ndbcluster= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_csv_db= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_partition_db= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_maria_db= SHOW_OPTION_NO;
|
||||
|
||||
#ifndef WITH_INNOBASE_STORAGE_ENGINE
|
||||
uint innobase_flush_log_at_trx_commit;
|
||||
|
@ -55,6 +55,9 @@
|
||||
#include <thr_alarm.h>
|
||||
#include <myisam.h>
|
||||
#include <my_dir.h>
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
#include <maria.h>
|
||||
#endif
|
||||
|
||||
#include "events.h"
|
||||
|
||||
@ -141,6 +144,7 @@ static void fix_max_join_size(THD *thd, enum_var_type type);
|
||||
static void fix_query_cache_size(THD *thd, enum_var_type type);
|
||||
static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type);
|
||||
static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type);
|
||||
static void fix_maria_max_sort_file_size(THD *thd, enum_var_type type);
|
||||
static void fix_max_binlog_size(THD *thd, enum_var_type type);
|
||||
static void fix_max_relay_log_size(THD *thd, enum_var_type type);
|
||||
static void fix_max_connections(THD *thd, enum_var_type type);
|
||||
@ -343,6 +347,14 @@ sys_var_thd_enum sys_myisam_stats_method("myisam_stats_method",
|
||||
&myisam_stats_method_typelib,
|
||||
NULL);
|
||||
|
||||
sys_var_thd_ulonglong sys_maria_max_sort_file_size("maria_max_sort_file_size", &SV::maria_max_sort_file_size, fix_maria_max_sort_file_size, 1);
|
||||
sys_var_thd_ulong sys_maria_repair_threads("maria_repair_threads", &SV::maria_repair_threads);
|
||||
sys_var_thd_ulong sys_maria_sort_buffer_size("maria_sort_buffer_size", &SV::maria_sort_buff_size);
|
||||
sys_var_thd_enum sys_maria_stats_method("maria_stats_method",
|
||||
&SV::maria_stats_method,
|
||||
&myisam_stats_method_typelib,
|
||||
NULL);
|
||||
|
||||
sys_var_thd_ulong sys_net_buffer_length("net_buffer_length",
|
||||
&SV::net_buffer_length);
|
||||
sys_var_thd_ulong sys_net_read_timeout("net_read_timeout",
|
||||
@ -667,6 +679,7 @@ sys_var_have_variable sys_have_csv_db("have_csv", &have_csv_db);
|
||||
sys_var_have_variable sys_have_dlopen("have_dynamic_loading", &have_dlopen);
|
||||
sys_var_have_variable sys_have_geometry("have_geometry", &have_geometry);
|
||||
sys_var_have_variable sys_have_innodb("have_innodb", &have_innodb);
|
||||
sys_var_have_variable sys_have_maria_db("have_maria", &have_maria_db);
|
||||
sys_var_have_variable sys_have_ndbcluster("have_ndbcluster", &have_ndbcluster);
|
||||
sys_var_have_variable sys_have_openssl("have_openssl", &have_openssl);
|
||||
sys_var_have_variable sys_have_partition_db("have_partitioning",
|
||||
@ -792,6 +805,7 @@ SHOW_VAR init_vars[]= {
|
||||
{sys_have_dlopen.name, (char*) &have_dlopen, SHOW_HAVE},
|
||||
{sys_have_geometry.name, (char*) &have_geometry, SHOW_HAVE},
|
||||
{sys_have_innodb.name, (char*) &have_innodb, SHOW_HAVE},
|
||||
{sys_have_maria_db.name, (char*) &have_maria_db, SHOW_HAVE},
|
||||
{sys_have_ndbcluster.name, (char*) &have_ndbcluster, SHOW_HAVE},
|
||||
{sys_have_openssl.name, (char*) &have_openssl, SHOW_HAVE},
|
||||
{sys_have_partition_db.name,(char*) &have_partition_db, SHOW_HAVE},
|
||||
@ -871,6 +885,15 @@ SHOW_VAR init_vars[]= {
|
||||
{sys_low_priority_updates.name, (char*) &sys_low_priority_updates, SHOW_SYS},
|
||||
{"lower_case_file_system", (char*) &lower_case_file_system, SHOW_MY_BOOL},
|
||||
{"lower_case_table_names", (char*) &lower_case_table_names, SHOW_INT},
|
||||
|
||||
{sys_maria_max_sort_file_size.name, (char*) &sys_maria_max_sort_file_size,
|
||||
SHOW_SYS},
|
||||
{sys_maria_repair_threads.name, (char*) &sys_maria_repair_threads,
|
||||
SHOW_SYS},
|
||||
{sys_maria_sort_buffer_size.name, (char*) &sys_maria_sort_buffer_size,
|
||||
SHOW_SYS},
|
||||
{sys_maria_stats_method.name, (char*) &sys_maria_stats_method, SHOW_SYS},
|
||||
|
||||
{sys_max_allowed_packet.name,(char*) &sys_max_allowed_packet, SHOW_SYS},
|
||||
{sys_max_binlog_cache_size.name,(char*) &sys_max_binlog_cache_size, SHOW_SYS},
|
||||
{sys_max_binlog_size.name, (char*) &sys_max_binlog_size, SHOW_SYS},
|
||||
@ -1019,10 +1042,10 @@ SHOW_VAR init_vars[]= {
|
||||
#if HAVE_POOL_OF_THREADS == 1
|
||||
{sys_thread_pool_size.name, (char*) &sys_thread_pool_size, SHOW_SYS},
|
||||
#endif
|
||||
{"thread_stack", (char*) &thread_stack, SHOW_LONG},
|
||||
{"thread_stack", (char*) &my_thread_stack_size, SHOW_LONG},
|
||||
{sys_time_format.name, (char*) &sys_time_format, SHOW_SYS},
|
||||
{"time_zone", (char*) &sys_time_zone, SHOW_SYS},
|
||||
{sys_timed_mutexes.name, (char*) &sys_timed_mutexes, SHOW_SYS},
|
||||
{sys_timed_mutexes.name, (char*) &sys_timed_mutexes, SHOW_SYS},
|
||||
{sys_tmp_table_size.name, (char*) &sys_tmp_table_size, SHOW_SYS},
|
||||
{sys_tmpdir.name, (char*) &sys_tmpdir, SHOW_SYS},
|
||||
{sys_trans_alloc_block_size.name, (char*) &sys_trans_alloc_block_size,
|
||||
@ -1165,6 +1188,16 @@ fix_myisam_max_sort_file_size(THD *thd, enum_var_type type)
|
||||
(my_off_t) global_system_variables.myisam_max_sort_file_size;
|
||||
}
|
||||
|
||||
static void
|
||||
fix_maria_max_sort_file_size(THD *thd, enum_var_type type)
|
||||
{
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
maria_max_temp_length=
|
||||
(my_off_t) global_system_variables.myisam_max_sort_file_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Set the OPTION_BIG_SELECTS flag if max_join_size == HA_POS_ERROR
|
||||
*/
|
||||
@ -2364,6 +2397,7 @@ void sys_var_collation_server::set_default(THD *thd, enum_var_type type)
|
||||
|
||||
|
||||
LEX_STRING default_key_cache_base= {(char *) "default", 7 };
|
||||
LEX_STRING maria_key_cache_base= {(char *) "maria", 5 };
|
||||
|
||||
static KEY_CACHE zero_key_cache;
|
||||
|
||||
@ -2373,7 +2407,7 @@ KEY_CACHE *get_key_cache(LEX_STRING *cache_name)
|
||||
if (!cache_name || ! cache_name->length)
|
||||
cache_name= &default_key_cache_base;
|
||||
return ((KEY_CACHE*) find_named(&key_caches,
|
||||
cache_name->str, cache_name->length, 0));
|
||||
cache_name->str, cache_name->length, 0));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1120,6 +1120,7 @@ public:
|
||||
extern sys_var_thd_bool sys_old_alter_table;
|
||||
extern sys_var_thd_bool sys_old_passwords;
|
||||
extern LEX_STRING default_key_cache_base;
|
||||
extern LEX_STRING maria_key_cache_base;
|
||||
|
||||
/* For sql_yacc */
|
||||
struct sys_var_with_base
|
||||
|
@ -180,7 +180,7 @@ class Time_zone;
|
||||
|
||||
struct system_variables
|
||||
{
|
||||
ulonglong myisam_max_extra_sort_file_size;
|
||||
ulonglong maria_max_sort_file_size;
|
||||
ulonglong myisam_max_sort_file_size;
|
||||
ulonglong max_heap_table_size;
|
||||
ulonglong tmp_table_size;
|
||||
@ -196,6 +196,9 @@ struct system_variables
|
||||
ulong max_sort_length;
|
||||
ulong max_tmp_tables;
|
||||
ulong max_insert_delayed_threads;
|
||||
ulong maria_repair_threads;
|
||||
ulong maria_sort_buff_size;
|
||||
ulong maria_stats_method;
|
||||
ulong multi_range_count;
|
||||
ulong myisam_repair_threads;
|
||||
ulong myisam_sort_buff_size;
|
||||
|
@ -4868,10 +4868,10 @@ bool check_stack_overrun(THD *thd, long margin,
|
||||
long stack_used;
|
||||
DBUG_ASSERT(thd == current_thd);
|
||||
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
|
||||
(long) (thread_stack - margin))
|
||||
(long) (my_thread_stack_size - margin))
|
||||
{
|
||||
sprintf(errbuff[0],ER(ER_STACK_OVERRUN_NEED_MORE),
|
||||
stack_used,thread_stack,margin);
|
||||
stack_used,my_thread_stack_size,margin);
|
||||
my_message(ER_STACK_OVERRUN_NEED_MORE,errbuff[0],MYF(0));
|
||||
thd->fatal_error();
|
||||
return 1;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "sql_cursor.h"
|
||||
|
||||
#include <m_ctype.h>
|
||||
#include <my_bit.h>
|
||||
#include <hash.h>
|
||||
#include <ft_global.h>
|
||||
|
||||
|
@ -34,7 +34,9 @@
|
||||
the callback function 'unpack_addon_fields'.
|
||||
*/
|
||||
|
||||
typedef struct st_sort_addon_field { /* Sort addon packed field */
|
||||
typedef struct st_sort_addon_field
|
||||
{
|
||||
/* Sort addon packed field */
|
||||
Field *field; /* Original field */
|
||||
uint offset; /* Offset from the last sorted field */
|
||||
uint null_offset; /* Offset to to null bit from the last sorted field */
|
||||
@ -42,13 +44,6 @@ typedef struct st_sort_addon_field { /* Sort addon packed field */
|
||||
uint8 null_bit; /* Null bit mask for the field */
|
||||
} SORT_ADDON_FIELD;
|
||||
|
||||
typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */
|
||||
my_off_t file_pos; /* Where we are in the sort file */
|
||||
uchar *base,*key; /* key pointers */
|
||||
ha_rows count; /* Number of rows in table */
|
||||
ulong mem_count; /* numbers of keys in memory */
|
||||
ulong max_keys; /* Max keys in buffert */
|
||||
} BUFFPEK;
|
||||
|
||||
typedef struct st_sort_param {
|
||||
uint rec_length; /* Length of sorted records */
|
||||
|
@ -459,7 +459,7 @@ void mysql_print_status()
|
||||
VOID(my_getwd(current_dir, sizeof(current_dir),MYF(0)));
|
||||
printf("Current dir: %s\n", current_dir);
|
||||
printf("Running threads: %d Stack size: %ld\n", thread_count,
|
||||
(long) thread_stack);
|
||||
(long) my_thread_stack_size);
|
||||
thr_print_locks(); // Write some debug info
|
||||
#ifndef DBUG_OFF
|
||||
print_cached_tables();
|
||||
@ -534,7 +534,7 @@ Estimated memory (with thread stack): %ld\n",
|
||||
(int) info.uordblks,
|
||||
(int) info.fordblks,
|
||||
(int) info.keepcost,
|
||||
(long) (thread_count * thread_stack + info.hblkhd + info.arena));
|
||||
(long) (thread_count * my_thread_stack_size + info.hblkhd + info.arena));
|
||||
#endif
|
||||
|
||||
Events::get_instance()->dump_internal_status();
|
||||
|
@ -449,7 +449,7 @@ static bool merge_walk(uchar *merge_buffer, ulong merge_buffer_size,
|
||||
*/
|
||||
for (top= begin; top != end; ++top)
|
||||
{
|
||||
top->base= merge_buffer + (top - begin) * piece_size;
|
||||
top->base= (byte*) (merge_buffer + (top - begin) * piece_size);
|
||||
top->max_keys= max_key_count_per_piece;
|
||||
bytes_read= read_to_buffer(file, top, key_length);
|
||||
if (bytes_read == (uint) (-1))
|
||||
|
@ -284,8 +284,10 @@ bool mysql_create_frm(THD *thd, const char *file_name,
|
||||
my_free((gptr) keybuff, MYF(0));
|
||||
|
||||
if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
|
||||
my_sync(file, MYF(MY_WME)))
|
||||
goto err2;
|
||||
(my_sync(file, MYF(MY_WME)) ||
|
||||
my_sync_dir_by_file(file_name, MYF(MY_WME))))
|
||||
goto err2;
|
||||
|
||||
if (my_close(file,MYF(MY_WME)))
|
||||
goto err3;
|
||||
|
||||
|
@ -19,7 +19,12 @@ AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
# These are built from source in the Docs directory
|
||||
EXTRA_DIST =
|
||||
SUBDIRS = @mysql_se_dirs@
|
||||
# Until we remove fulltext-related references from Maria to MyISAM
|
||||
# MyISAM must be built before Maria, which is not the case by default
|
||||
# because of alphabetical order
|
||||
# So we put myisam first; this is very ugly regarding plugins' logic
|
||||
# but it works, and we'll remove it soon.
|
||||
SUBDIRS = myisam @mysql_se_dirs@
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
%::SCCS/s.%
|
||||
|
1
storage/maria/CMakeLists.txt
Normal file
1
storage/maria/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
# empty for the moment; will fill it when we build under Windows
|
162
storage/maria/Makefile.am
Normal file
162
storage/maria/Makefile.am
Normal file
@ -0,0 +1,162 @@
|
||||
# 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
|
||||
|
||||
MYSQLDATAdir = $(localstatedir)
|
||||
MYSQLSHAREdir = $(pkgdatadir)
|
||||
MYSQLBASEdir= $(prefix)
|
||||
MYSQLLIBdir= $(pkglibdir)
|
||||
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \
|
||||
-I$(top_srcdir)/regex \
|
||||
-I$(top_srcdir)/sql \
|
||||
-I$(srcdir)
|
||||
WRAPLIBS=
|
||||
|
||||
LDADD =
|
||||
|
||||
DEFS = @DEFS@
|
||||
|
||||
# "." is needed first because tests in unittest need libmaria
|
||||
SUBDIRS = . unittest
|
||||
|
||||
EXTRA_DIST = ma_test_all.sh ma_test_all.res ma_ft_stem.c CMakeLists.txt plug.in
|
||||
pkgdata_DATA = ma_test_all ma_test_all.res
|
||||
pkglib_LIBRARIES = libmaria.a
|
||||
bin_PROGRAMS = maria_chk maria_pack maria_ftdump
|
||||
maria_chk_DEPENDENCIES= $(LIBRARIES)
|
||||
# Only reason to link with libmyisam.a here is that it's where some fulltext
|
||||
# pieces are (but soon we'll remove fulltext dependencies from Maria).
|
||||
# For now, it imposes that storage/myisam be built before storage/maria.
|
||||
maria_chk_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
maria_pack_DEPENDENCIES=$(LIBRARIES)
|
||||
maria_pack_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
noinst_PROGRAMS = ma_test1 ma_test2 ma_test3 ma_rt_test ma_sp_test
|
||||
noinst_HEADERS = maria_def.h ma_rt_index.h ma_rt_key.h ma_rt_mbr.h \
|
||||
ma_sp_defs.h ma_fulltext.h ma_ftdefs.h ma_ft_test1.h \
|
||||
ma_ft_eval.h trnman.h lockman.h tablockman.h \
|
||||
ma_control_file.h ha_maria.h ma_blockrec.h
|
||||
ma_test1_DEPENDENCIES= $(LIBRARIES)
|
||||
ma_test1_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
ma_test2_DEPENDENCIES= $(LIBRARIES)
|
||||
ma_test2_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
ma_test3_DEPENDENCIES= $(LIBRARIES)
|
||||
ma_test3_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
#ma_ft_test1_DEPENDENCIES= $(LIBRARIES)
|
||||
#ma_ft_eval_DEPENDENCIES= $(LIBRARIES)
|
||||
maria_ftdump_DEPENDENCIES= $(LIBRARIES)
|
||||
maria_ftdump_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
ma_rt_test_DEPENDENCIES= $(LIBRARIES)
|
||||
ma_rt_test_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
ma_sp_test_DEPENDENCIES= $(LIBRARIES)
|
||||
ma_sp_test_LDADD= @CLIENT_EXTRA_LDFLAGS@ libmaria.a \
|
||||
$(top_builddir)/storage/myisam/libmyisam.a \
|
||||
$(top_builddir)/mysys/libmysys.a \
|
||||
$(top_builddir)/dbug/libdbug.a \
|
||||
$(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
|
||||
libmaria_a_SOURCES = ma_init.c ma_open.c ma_extra.c ma_info.c ma_rkey.c \
|
||||
ma_rnext.c ma_rnext_same.c \
|
||||
ma_search.c ma_page.c ma_key.c ma_locking.c \
|
||||
ma_rrnd.c ma_scan.c ma_cache.c \
|
||||
ma_statrec.c ma_packrec.c ma_dynrec.c \
|
||||
ma_blockrec.c ma_bitmap.c \
|
||||
ma_update.c ma_write.c ma_unique.c \
|
||||
ma_delete.c \
|
||||
ma_rprev.c ma_rfirst.c ma_rlast.c ma_rsame.c \
|
||||
ma_rsamepos.c ma_panic.c ma_close.c ma_create.c\
|
||||
ma_range.c ma_dbug.c ma_checksum.c \
|
||||
ma_changed.c ma_static.c ma_delete_all.c \
|
||||
ma_delete_table.c ma_rename.c ma_check.c \
|
||||
ma_keycache.c ma_preload.c ma_ft_parser.c \
|
||||
ma_ft_update.c ma_ft_boolean_search.c \
|
||||
ma_ft_nlq_search.c ft_maria.c ma_sort.c \
|
||||
ha_maria.cc trnman.c lockman.c tablockman.c \
|
||||
ma_rt_index.c ma_rt_key.c ma_rt_mbr.c ma_rt_split.c \
|
||||
ma_sp_key.c ma_control_file.c ma_loghandler.c
|
||||
CLEANFILES = test?.MA? FT?.MA? isam.log ma_test_all ma_rt_test.MA? sp_test.MA?
|
||||
|
||||
SUFFIXES = .sh
|
||||
|
||||
.sh:
|
||||
@RM@ -f $@ $@-t
|
||||
@SED@ \
|
||||
-e 's!@''bindir''@!$(bindir)!g' \
|
||||
-e 's!@''scriptdir''@!$(bindir)!g' \
|
||||
-e 's!@''prefix''@!$(prefix)!g' \
|
||||
-e 's!@''datadir''@!$(datadir)!g' \
|
||||
-e 's!@''localstatedir''@!$(localstatedir)!g' \
|
||||
-e 's!@''libexecdir''@!$(libexecdir)!g' \
|
||||
-e 's!@''CC''@!@CC@!'\
|
||||
-e 's!@''CXX''@!@CXX@!'\
|
||||
-e 's!@''GXX''@!@GXX@!'\
|
||||
-e 's!@''PERL''@!@PERL@!' \
|
||||
-e 's!@''CFLAGS''@!@SAVE_CFLAGS@!'\
|
||||
-e 's!@''CXXFLAGS''@!@SAVE_CXXFLAGS@!'\
|
||||
-e 's!@''LDFLAGS''@!@SAVE_LDFLAGS@!'\
|
||||
-e 's!@''VERSION''@!@VERSION@!' \
|
||||
-e 's!@''MYSQL_SERVER_SUFFIX''@!@MYSQL_SERVER_SUFFIX@!' \
|
||||
-e 's!@''COMPILATION_COMMENT''@!@COMPILATION_COMMENT@!' \
|
||||
-e 's!@''MACHINE_TYPE''@!@MACHINE_TYPE@!' \
|
||||
-e 's!@''HOSTNAME''@!@HOSTNAME@!' \
|
||||
-e 's!@''SYSTEM_TYPE''@!@SYSTEM_TYPE@!' \
|
||||
-e 's!@''CHECK_PID''@!@CHECK_PID@!' \
|
||||
-e 's!@''FIND_PROC''@!@FIND_PROC@!' \
|
||||
-e 's!@''MYSQLD_DEFAULT_SWITCHES''@!@MYSQLD_DEFAULT_SWITCHES@!' \
|
||||
-e 's!@''MYSQL_UNIX_ADDR''@!@MYSQL_UNIX_ADDR@!' \
|
||||
-e 's!@''TARGET_LINUX''@!@TARGET_LINUX@!' \
|
||||
-e "s!@""CONF_COMMAND""@!@CONF_COMMAND@!" \
|
||||
-e 's!@''MYSQLD_USER''@!@MYSQLD_USER@!' \
|
||||
-e 's!@''sysconfdir''@!@sysconfdir@!' \
|
||||
-e 's!@''SHORT_MYSQL_INTRO''@!@SHORT_MYSQL_INTRO@!' \
|
||||
-e 's!@''SHARED_LIB_VERSION''@!@SHARED_LIB_VERSION@!' \
|
||||
-e 's!@''MYSQL_BASE_VERSION''@!@MYSQL_BASE_VERSION@!' \
|
||||
-e 's!@''MYSQL_NO_DASH_VERSION''@!@MYSQL_NO_DASH_VERSION@!' \
|
||||
-e 's!@''MYSQL_TCP_PORT''@!@MYSQL_TCP_PORT@!' \
|
||||
-e 's!@''PERL_DBI_VERSION''@!@PERL_DBI_VERSION@!' \
|
||||
-e 's!@''PERL_DBD_VERSION''@!@PERL_DBD_VERSION@!' \
|
||||
-e 's!@''PERL_DATA_DUMPER''@!@PERL_DATA_DUMPER@!' \
|
||||
$< > $@-t
|
||||
@CHMOD@ +x $@-t
|
||||
@MV@ $@-t $@
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
%::SCCS/s.%
|
49
storage/maria/ft_maria.c
Normal file
49
storage/maria/ft_maria.c
Normal file
@ -0,0 +1,49 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
/*
|
||||
This function is for interface functions between fulltext and maria
|
||||
*/
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
|
||||
FT_INFO *maria_ft_init_search(uint flags, void *info, uint keynr,
|
||||
byte *query, uint query_len, CHARSET_INFO *cs,
|
||||
byte *record)
|
||||
{
|
||||
FT_INFO *res;
|
||||
if (flags & FT_BOOL)
|
||||
res= maria_ft_init_boolean_search((MARIA_HA *) info, keynr, query,
|
||||
query_len, cs);
|
||||
else
|
||||
res= maria_ft_init_nlq_search((MARIA_HA *) info, keynr, query, query_len,
|
||||
flags, record);
|
||||
return res;
|
||||
}
|
||||
|
||||
const struct _ft_vft _ma_ft_vft_nlq = {
|
||||
maria_ft_nlq_read_next, maria_ft_nlq_find_relevance,
|
||||
maria_ft_nlq_close_search, maria_ft_nlq_get_relevance,
|
||||
maria_ft_nlq_reinit_search
|
||||
};
|
||||
const struct _ft_vft _ma_ft_vft_boolean = {
|
||||
maria_ft_boolean_read_next, maria_ft_boolean_find_relevance,
|
||||
maria_ft_boolean_close_search, maria_ft_boolean_get_relevance,
|
||||
maria_ft_boolean_reinit_search
|
||||
};
|
||||
|
1896
storage/maria/ha_maria.cc
Normal file
1896
storage/maria/ha_maria.cc
Normal file
File diff suppressed because it is too large
Load Diff
145
storage/maria/ha_maria.h
Normal file
145
storage/maria/ha_maria.h
Normal file
@ -0,0 +1,145 @@
|
||||
/* Copyright (C) 2006,2004 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 */
|
||||
|
||||
|
||||
#ifdef USE_PRAGMA_INTERFACE
|
||||
#pragma interface /* gcc class implementation */
|
||||
#endif
|
||||
|
||||
/* class for the the maria handler */
|
||||
|
||||
#include <maria.h>
|
||||
|
||||
#define HA_RECOVER_NONE 0 /* No automatic recover */
|
||||
#define HA_RECOVER_DEFAULT 1 /* Automatic recover active */
|
||||
#define HA_RECOVER_BACKUP 2 /* Make a backupfile on recover */
|
||||
#define HA_RECOVER_FORCE 4 /* Recover even if we loose rows */
|
||||
#define HA_RECOVER_QUICK 8 /* Don't check rows in data file */
|
||||
|
||||
extern ulong maria_sort_buffer_size;
|
||||
extern TYPELIB maria_recover_typelib;
|
||||
extern ulong maria_recover_options;
|
||||
|
||||
class ha_maria :public handler
|
||||
{
|
||||
MARIA_HA *file;
|
||||
ulonglong int_table_flags;
|
||||
char *data_file_name, *index_file_name;
|
||||
bool can_enable_indexes;
|
||||
int repair(THD * thd, HA_CHECK ¶m, bool optimize);
|
||||
|
||||
public:
|
||||
ha_maria(handlerton *hton, TABLE_SHARE * table_arg);
|
||||
~ha_maria() {}
|
||||
handler *clone(MEM_ROOT *mem_root);
|
||||
const char *table_type() const
|
||||
{ return "MARIA"; }
|
||||
const char *index_type(uint key_number);
|
||||
const char **bas_ext() const;
|
||||
ulonglong table_flags() const
|
||||
{ return int_table_flags; }
|
||||
ulong index_flags(uint inx, uint part, bool all_parts) const
|
||||
{
|
||||
return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ?
|
||||
0 : HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE |
|
||||
HA_READ_ORDER | HA_KEYREAD_ONLY);
|
||||
}
|
||||
uint max_supported_keys() const
|
||||
{ return MARIA_MAX_KEY; }
|
||||
uint max_supported_key_length() const
|
||||
{ return HA_MAX_KEY_LENGTH; }
|
||||
uint max_supported_key_part_length() const
|
||||
{ return HA_MAX_KEY_LENGTH; }
|
||||
uint checksum() const;
|
||||
|
||||
virtual bool check_if_locking_is_allowed(uint sql_command,
|
||||
ulong type, TABLE * table,
|
||||
uint count,
|
||||
bool called_by_logger_thread);
|
||||
int open(const char *name, int mode, uint test_if_locked);
|
||||
int close(void);
|
||||
int write_row(byte * buf);
|
||||
int update_row(const byte * old_data, byte * new_data);
|
||||
int delete_row(const byte * buf);
|
||||
int index_read(byte * buf, const byte * key,
|
||||
uint key_len, enum ha_rkey_function find_flag);
|
||||
int index_read_idx(byte * buf, uint idx, const byte * key,
|
||||
uint key_len, enum ha_rkey_function find_flag);
|
||||
int index_read_last(byte * buf, const byte * key, uint key_len);
|
||||
int index_next(byte * buf);
|
||||
int index_prev(byte * buf);
|
||||
int index_first(byte * buf);
|
||||
int index_last(byte * buf);
|
||||
int index_next_same(byte * buf, const byte * key, uint keylen);
|
||||
int ft_init()
|
||||
{
|
||||
if (!ft_handler)
|
||||
return 1;
|
||||
ft_handler->please->reinit_search(ft_handler);
|
||||
return 0;
|
||||
}
|
||||
FT_INFO *ft_init_ext(uint flags, uint inx, String * key)
|
||||
{
|
||||
return maria_ft_init_search(flags, file, inx,
|
||||
(byte *) key->ptr(), key->length(),
|
||||
key->charset(), table->record[0]);
|
||||
}
|
||||
int ft_read(byte * buf);
|
||||
int rnd_init(bool scan);
|
||||
int rnd_next(byte * buf);
|
||||
int rnd_pos(byte * buf, byte * pos);
|
||||
int restart_rnd_next(byte * buf, byte * pos);
|
||||
void position(const byte * record);
|
||||
int info(uint);
|
||||
int extra(enum ha_extra_function operation);
|
||||
int extra_opt(enum ha_extra_function operation, ulong cache_size);
|
||||
int reset(void);
|
||||
int external_lock(THD * thd, int lock_type);
|
||||
int delete_all_rows(void);
|
||||
int disable_indexes(uint mode);
|
||||
int enable_indexes(uint mode);
|
||||
int indexes_are_disabled(void);
|
||||
void start_bulk_insert(ha_rows rows);
|
||||
int end_bulk_insert();
|
||||
ha_rows records_in_range(uint inx, key_range * min_key, key_range * max_key);
|
||||
void update_create_info(HA_CREATE_INFO * create_info);
|
||||
int create(const char *name, TABLE * form, HA_CREATE_INFO * create_info);
|
||||
THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to,
|
||||
enum thr_lock_type lock_type);
|
||||
virtual void get_auto_increment(ulonglong offset, ulonglong increment,
|
||||
ulonglong nb_desired_values,
|
||||
ulonglong *first_value,
|
||||
ulonglong *nb_reserved_values);
|
||||
int rename_table(const char *from, const char *to);
|
||||
int delete_table(const char *name);
|
||||
int check(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int analyze(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int repair(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
bool check_and_repair(THD * thd);
|
||||
bool is_crashed() const;
|
||||
bool auto_repair() const
|
||||
{ return maria_recover_options != 0; }
|
||||
int optimize(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int restore(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int backup(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int assign_to_keycache(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int preload_keys(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
bool check_if_incompatible_data(HA_CREATE_INFO * info, uint table_changes);
|
||||
#ifdef HAVE_REPLICATION
|
||||
int dump(THD * thd, int fd);
|
||||
int net_read_dump(NET * net);
|
||||
#endif
|
||||
};
|
787
storage/maria/lockman.c
Normal file
787
storage/maria/lockman.c
Normal file
@ -0,0 +1,787 @@
|
||||
/* QQ: TODO - allocate everything from dynarrays !!! (benchmark) */
|
||||
/* QQ: TODO instant duration locks */
|
||||
/* QQ: #warning automatically place S instead of LS if possible */
|
||||
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; 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 */
|
||||
|
||||
/*
|
||||
Generic Lock Manager
|
||||
|
||||
Lock manager handles locks on "resources", a resource must be uniquely
|
||||
identified by a 64-bit number. Lock manager itself does not imply
|
||||
anything about the nature of a resource - it can be a row, a table, a
|
||||
database, or just anything.
|
||||
|
||||
Locks belong to "lock owners". A Lock owner is uniquely identified by a
|
||||
16-bit number. A function loid2lo must be provided by the application
|
||||
that takes such a number as an argument and returns a LOCK_OWNER
|
||||
structure.
|
||||
|
||||
Lock levels are completely defined by three tables. Lock compatibility
|
||||
matrix specifies which locks can be held at the same time on a resource.
|
||||
Lock combining matrix specifies what lock level has the same behaviour as
|
||||
a pair of two locks of given levels. getlock_result matrix simplifies
|
||||
intention locking and lock escalation for an application, basically it
|
||||
defines which locks are intention locks and which locks are "loose"
|
||||
locks. It is only used to provide better diagnostics for the
|
||||
application, lock manager itself does not differentiate between normal,
|
||||
intention, and loose locks.
|
||||
|
||||
Internally lock manager is based on a lock-free hash, see lf_hash.c for
|
||||
details. All locks are stored in a hash, with a resource id as a search
|
||||
key, so all locks for the same resource will be considered collisions and
|
||||
will be put in a one (lock-free) linked list. The main lock-handling
|
||||
logic is in the inner loop that searches for a lock in such a linked
|
||||
list - lockfind().
|
||||
|
||||
This works as follows. Locks generally are added to the end of the list
|
||||
(with one exception, see below). When scanning the list it is always
|
||||
possible to determine what locks are granted (active) and what locks are
|
||||
waiting - first lock is obviously active, the second is active if it's
|
||||
compatible with the first, and so on, a lock is active if it's compatible
|
||||
with all previous locks and all locks before it are also active.
|
||||
To calculate the "compatible with all previous locks" all locks are
|
||||
accumulated in prev_lock variable using lock_combining_matrix.
|
||||
|
||||
Lock upgrades: when a thread that has a lock on a given resource,
|
||||
requests a new lock on the same resource and the old lock is not enough
|
||||
to satisfy new lock requirements (which is defined by
|
||||
lock_combining_matrix[old_lock][new_lock] != old_lock), a new lock is
|
||||
placed in the list. Depending on other locks it is immediately active or
|
||||
it will wait for other locks. Here's an exception to "locks are added
|
||||
to the end" rule - upgraded locks are added after the last active lock
|
||||
but before all waiting locks. Old lock (the one we upgraded from) is
|
||||
not removed from the list, indeed it may be needed if the new lock was
|
||||
in a savepoint that gets rolled back. So old lock is marked as "ignored"
|
||||
(IGNORE_ME flag). New lock gets an UPGRADED flag.
|
||||
|
||||
Loose locks add an important exception to the above. Loose locks do not
|
||||
always commute with other locks. In the list IX-LS both locks are active,
|
||||
while in the LS-IX list only the first lock is active. This creates a
|
||||
problem in lock upgrades. If the list was IX-LS and the owner of the
|
||||
first lock wants to place LS lock (which can be immediately granted), the
|
||||
IX lock is upgraded to LSIX and the list becomes IX-LS-LSIX, which,
|
||||
according to the lock compatibility matrix means that the last lock is
|
||||
waiting - of course it all happened because IX and LS were swapped and
|
||||
they don't commute. To work around this there's ACTIVE flag which is set
|
||||
in every lock that never waited (was placed active), and this flag
|
||||
overrides "compatible with all previous locks" rule.
|
||||
|
||||
When a lock is placed to the end of the list it's either compatible with
|
||||
all locks and all locks are active - new lock becomes active at once, or
|
||||
it conflicts with some of the locks, in this case in the 'blocker'
|
||||
variable a conflicting lock is returned and the calling thread waits on a
|
||||
pthread condition in the LOCK_OWNER structure of the owner of the
|
||||
conflicting lock. Or a new lock is compatible with all locks, but some
|
||||
existing locks are not compatible with each other (example: request IS,
|
||||
when the list is S-IX) - that is not all locks are active. In this case a
|
||||
first waiting lock is returned in the 'blocker' variable, lockman_getlock()
|
||||
notices that a "blocker" does not conflict with the requested lock, and
|
||||
"dereferences" it, to find the lock that it's waiting on. The calling
|
||||
thread than begins to wait on the same lock.
|
||||
|
||||
To better support table-row relations where one needs to lock the table
|
||||
with an intention lock before locking the row, extended diagnostics is
|
||||
provided. When an intention lock (presumably on a table) is granted,
|
||||
lockman_getlock() returns one of GOT_THE_LOCK (no need to lock the row,
|
||||
perhaps the thread already has a normal lock on this table),
|
||||
GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE (need to lock the row, as usual),
|
||||
GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE (only need to check
|
||||
whether it's possible to lock the row, but no need to lock it - perhaps
|
||||
the thread has a loose lock on this table). This is defined by
|
||||
getlock_result[] table.
|
||||
*/
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_bit.h>
|
||||
#include <lf.h>
|
||||
#include "lockman.h"
|
||||
|
||||
/*
|
||||
Lock compatibility matrix.
|
||||
|
||||
It's asymmetric. Read it as "Somebody has the lock <value in the row
|
||||
label>, can I set the lock <value in the column label> ?"
|
||||
|
||||
') Though you can take LS lock while somebody has S lock, it makes no
|
||||
sense - it's simpler to take S lock too.
|
||||
|
||||
1 - compatible
|
||||
0 - incompatible
|
||||
-1 - "impossible", so that we can assert the impossibility.
|
||||
*/
|
||||
static int lock_compatibility_matrix[10][10]=
|
||||
{ /* N S X IS IX SIX LS LX SLX LSIX */
|
||||
{ -1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* N */
|
||||
{ -1, 1, 0, 1, 0, 0, 1, 0, 0, 0 }, /* S */
|
||||
{ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* X */
|
||||
{ -1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, /* IS */
|
||||
{ -1, 0, 0, 1, 1, 0, 1, 1, 0, 1 }, /* IX */
|
||||
{ -1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, /* SIX */
|
||||
{ -1, 1, 0, 1, 0, 0, 1, 0, 0, 0 }, /* LS */
|
||||
{ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* LX */
|
||||
{ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* SLX */
|
||||
{ -1, 0, 0, 1, 0, 0, 1, 0, 0, 0 } /* LSIX */
|
||||
};
|
||||
|
||||
/*
|
||||
Lock combining matrix.
|
||||
|
||||
It's symmetric. Read it as "what lock level L is identical to the
|
||||
set of two locks A and B"
|
||||
|
||||
One should never get N from it, we assert the impossibility
|
||||
*/
|
||||
static enum lock_type lock_combining_matrix[10][10]=
|
||||
{/* N S X IS IX SIX LS LX SLX LSIX */
|
||||
{ N, S, X, IS, IX, SIX, S, SLX, SLX, SIX}, /* N */
|
||||
{ S, S, X, S, SIX, SIX, S, SLX, SLX, SIX}, /* S */
|
||||
{ X, X, X, X, X, X, X, X, X, X}, /* X */
|
||||
{ IS, S, X, IS, IX, SIX, LS, LX, SLX, LSIX}, /* IS */
|
||||
{ IX, SIX, X, IX, IX, SIX, LSIX, LX, SLX, LSIX}, /* IX */
|
||||
{ SIX, SIX, X, SIX, SIX, SIX, SIX, SLX, SLX, SIX}, /* SIX */
|
||||
{ LS, S, X, LS, LSIX, SIX, LS, LX, SLX, LSIX}, /* LS */
|
||||
{ LX, SLX, X, LX, LX, SLX, LX, LX, SLX, LX}, /* LX */
|
||||
{ SLX, SLX, X, SLX, SLX, SLX, SLX, SLX, SLX, SLX}, /* SLX */
|
||||
{ LSIX, SIX, X, LSIX, LSIX, SIX, LSIX, LX, SLX, LSIX} /* LSIX */
|
||||
};
|
||||
|
||||
#define REPEAT_ONCE_MORE 0
|
||||
#define OK_TO_PLACE_THE_LOCK 1
|
||||
#define OK_TO_PLACE_THE_REQUEST 2
|
||||
#define ALREADY_HAVE_THE_LOCK 4
|
||||
#define ALREADY_HAVE_THE_REQUEST 8
|
||||
#define PLACE_NEW_DISABLE_OLD 16
|
||||
#define REQUEST_NEW_DISABLE_OLD 32
|
||||
#define RESOURCE_WAS_UNLOCKED 64
|
||||
|
||||
#define NEED_TO_WAIT (OK_TO_PLACE_THE_REQUEST | ALREADY_HAVE_THE_REQUEST |\
|
||||
REQUEST_NEW_DISABLE_OLD)
|
||||
#define ALREADY_HAVE (ALREADY_HAVE_THE_LOCK | ALREADY_HAVE_THE_REQUEST)
|
||||
#define LOCK_UPGRADE (PLACE_NEW_DISABLE_OLD | REQUEST_NEW_DISABLE_OLD)
|
||||
|
||||
|
||||
/*
|
||||
the return codes for lockman_getlock
|
||||
|
||||
It's asymmetric. Read it as "I have the lock <value in the row label>,
|
||||
what value should be returned for <value in the column label> ?"
|
||||
|
||||
0 means impossible combination (assert!)
|
||||
|
||||
Defines below help to preserve the table structure.
|
||||
I/L/A values are self explanatory
|
||||
x means the combination is possible (assert should not crash)
|
||||
but it cannot happen in row locks, only in table locks (S,X),
|
||||
or lock escalations (LS,LX)
|
||||
*/
|
||||
#define I GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE
|
||||
#define L GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE
|
||||
#define A GOT_THE_LOCK
|
||||
#define x GOT_THE_LOCK
|
||||
static enum lockman_getlock_result getlock_result[10][10]=
|
||||
{/* N S X IS IX SIX LS LX SLX LSIX */
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* N */
|
||||
{ 0, x, 0, A, 0, 0, x, 0, 0, 0}, /* S */
|
||||
{ 0, x, x, A, A, 0, x, x, 0, 0}, /* X */
|
||||
{ 0, 0, 0, I, 0, 0, 0, 0, 0, 0}, /* IS */
|
||||
{ 0, 0, 0, I, I, 0, 0, 0, 0, 0}, /* IX */
|
||||
{ 0, x, 0, A, I, 0, x, 0, 0, 0}, /* SIX */
|
||||
{ 0, 0, 0, L, 0, 0, x, 0, 0, 0}, /* LS */
|
||||
{ 0, 0, 0, L, L, 0, x, x, 0, 0}, /* LX */
|
||||
{ 0, x, 0, A, L, 0, x, x, 0, 0}, /* SLX */
|
||||
{ 0, 0, 0, L, I, 0, x, 0, 0, 0} /* LSIX */
|
||||
};
|
||||
#undef I
|
||||
#undef L
|
||||
#undef A
|
||||
#undef x
|
||||
|
||||
LF_REQUIRE_PINS(4);
|
||||
|
||||
typedef struct lockman_lock {
|
||||
uint64 resource;
|
||||
struct lockman_lock *lonext;
|
||||
intptr volatile link;
|
||||
uint32 hashnr;
|
||||
/* QQ: TODO - remove hashnr from LOCK */
|
||||
uint16 loid;
|
||||
uchar lock; /* sizeof(uchar) <= sizeof(enum) */
|
||||
uchar flags;
|
||||
} LOCK;
|
||||
|
||||
#define IGNORE_ME 1
|
||||
#define UPGRADED 2
|
||||
#define ACTIVE 4
|
||||
|
||||
typedef struct {
|
||||
intptr volatile *prev;
|
||||
LOCK *curr, *next;
|
||||
LOCK *blocker, *upgrade_from;
|
||||
} CURSOR;
|
||||
|
||||
#define PTR(V) (LOCK *)((V) & (~(intptr)1))
|
||||
#define DELETED(V) ((V) & 1)
|
||||
|
||||
/*
|
||||
NOTE
|
||||
cursor is positioned in either case
|
||||
pins[0..3] are used, they are NOT removed on return
|
||||
*/
|
||||
static int lockfind(LOCK * volatile *head, LOCK *node,
|
||||
CURSOR *cursor, LF_PINS *pins)
|
||||
{
|
||||
uint32 hashnr, cur_hashnr;
|
||||
uint64 resource, cur_resource;
|
||||
intptr link;
|
||||
my_bool cur_active, compatible, upgrading, prev_active;
|
||||
enum lock_type lock, prev_lock, cur_lock;
|
||||
uint16 loid, cur_loid;
|
||||
int cur_flags, flags;
|
||||
|
||||
hashnr= node->hashnr;
|
||||
resource= node->resource;
|
||||
lock= node->lock;
|
||||
loid= node->loid;
|
||||
flags= node->flags;
|
||||
|
||||
retry:
|
||||
cursor->prev= (intptr *)head;
|
||||
prev_lock= N;
|
||||
cur_active= TRUE;
|
||||
compatible= TRUE;
|
||||
upgrading= FALSE;
|
||||
cursor->blocker= cursor->upgrade_from= 0;
|
||||
_lf_unpin(pins, 3);
|
||||
do {
|
||||
cursor->curr= PTR(*cursor->prev);
|
||||
_lf_pin(pins, 1, cursor->curr);
|
||||
} while(*cursor->prev != (intptr)cursor->curr && LF_BACKOFF);
|
||||
for (;;)
|
||||
{
|
||||
if (!cursor->curr)
|
||||
break;
|
||||
do {
|
||||
link= cursor->curr->link;
|
||||
cursor->next= PTR(link);
|
||||
_lf_pin(pins, 0, cursor->next);
|
||||
} while(link != cursor->curr->link && LF_BACKOFF);
|
||||
cur_hashnr= cursor->curr->hashnr;
|
||||
cur_resource= cursor->curr->resource;
|
||||
cur_lock= cursor->curr->lock;
|
||||
cur_loid= cursor->curr->loid;
|
||||
cur_flags= cursor->curr->flags;
|
||||
if (*cursor->prev != (intptr)cursor->curr)
|
||||
{
|
||||
(void)LF_BACKOFF;
|
||||
goto retry;
|
||||
}
|
||||
if (!DELETED(link))
|
||||
{
|
||||
if (cur_hashnr > hashnr ||
|
||||
(cur_hashnr == hashnr && cur_resource >= resource))
|
||||
{
|
||||
if (cur_hashnr > hashnr || cur_resource > resource)
|
||||
break;
|
||||
/* ok, we have a lock for this resource */
|
||||
DBUG_ASSERT(lock_compatibility_matrix[prev_lock][cur_lock] >= 0);
|
||||
DBUG_ASSERT(lock_compatibility_matrix[cur_lock][lock] >= 0);
|
||||
if ((cur_flags & IGNORE_ME) && ! (flags & IGNORE_ME))
|
||||
{
|
||||
DBUG_ASSERT(cur_active);
|
||||
if (cur_loid == loid)
|
||||
cursor->upgrade_from= cursor->curr;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_active= cur_active;
|
||||
if (cur_flags & ACTIVE)
|
||||
DBUG_ASSERT(prev_active == TRUE);
|
||||
else
|
||||
cur_active&= lock_compatibility_matrix[prev_lock][cur_lock];
|
||||
if (upgrading && !cur_active /*&& !(cur_flags & UPGRADED)*/)
|
||||
break;
|
||||
if (prev_active && !cur_active)
|
||||
{
|
||||
cursor->blocker= cursor->curr;
|
||||
_lf_pin(pins, 3, cursor->curr);
|
||||
}
|
||||
if (cur_loid == loid)
|
||||
{
|
||||
/* we already have a lock on this resource */
|
||||
DBUG_ASSERT(lock_combining_matrix[cur_lock][lock] != N);
|
||||
DBUG_ASSERT(!upgrading || (flags & IGNORE_ME));
|
||||
if (lock_combining_matrix[cur_lock][lock] == cur_lock)
|
||||
{
|
||||
/* new lock is compatible */
|
||||
if (cur_active)
|
||||
{
|
||||
cursor->blocker= cursor->curr; /* loose-locks! */
|
||||
_lf_unpin(pins, 3); /* loose-locks! */
|
||||
return ALREADY_HAVE_THE_LOCK;
|
||||
}
|
||||
else
|
||||
return ALREADY_HAVE_THE_REQUEST;
|
||||
}
|
||||
/* not compatible, upgrading */
|
||||
upgrading= TRUE;
|
||||
cursor->upgrade_from= cursor->curr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!lock_compatibility_matrix[cur_lock][lock])
|
||||
{
|
||||
compatible= FALSE;
|
||||
cursor->blocker= cursor->curr;
|
||||
_lf_pin(pins, 3, cursor->curr);
|
||||
}
|
||||
}
|
||||
prev_lock= lock_combining_matrix[prev_lock][cur_lock];
|
||||
DBUG_ASSERT(prev_lock != N);
|
||||
}
|
||||
}
|
||||
cursor->prev= &(cursor->curr->link);
|
||||
_lf_pin(pins, 2, cursor->curr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (my_atomic_casptr((void **)cursor->prev,
|
||||
(void **)&cursor->curr, cursor->next))
|
||||
_lf_alloc_free(pins, cursor->curr);
|
||||
else
|
||||
{
|
||||
(void)LF_BACKOFF;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
cursor->curr= cursor->next;
|
||||
_lf_pin(pins, 1, cursor->curr);
|
||||
}
|
||||
/*
|
||||
either the end of lock list - no more locks for this resource,
|
||||
or upgrading and the end of active lock list
|
||||
*/
|
||||
if (upgrading)
|
||||
{
|
||||
if (compatible /*&& prev_active*/)
|
||||
return PLACE_NEW_DISABLE_OLD;
|
||||
else
|
||||
return REQUEST_NEW_DISABLE_OLD;
|
||||
}
|
||||
if (cur_active && compatible)
|
||||
{
|
||||
/*
|
||||
either no locks for this resource or all are compatible.
|
||||
ok to place the lock in any case.
|
||||
*/
|
||||
return prev_lock == N ? RESOURCE_WAS_UNLOCKED
|
||||
: OK_TO_PLACE_THE_LOCK;
|
||||
}
|
||||
/* we have a lock conflict. ok to place a lock request. And wait */
|
||||
return OK_TO_PLACE_THE_REQUEST;
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE
|
||||
it uses pins[0..3], on return pins 0..2 are removed, pin 3 (blocker) stays
|
||||
*/
|
||||
static int lockinsert(LOCK * volatile *head, LOCK *node, LF_PINS *pins,
|
||||
LOCK **blocker)
|
||||
{
|
||||
CURSOR cursor;
|
||||
int res;
|
||||
|
||||
do
|
||||
{
|
||||
res= lockfind(head, node, &cursor, pins);
|
||||
DBUG_ASSERT(res != ALREADY_HAVE_THE_REQUEST);
|
||||
if (!(res & ALREADY_HAVE))
|
||||
{
|
||||
if (res & LOCK_UPGRADE)
|
||||
{
|
||||
node->flags|= UPGRADED;
|
||||
node->lock= lock_combining_matrix[cursor.upgrade_from->lock][node->lock];
|
||||
}
|
||||
if (!(res & NEED_TO_WAIT))
|
||||
node->flags|= ACTIVE;
|
||||
node->link= (intptr)cursor.curr;
|
||||
DBUG_ASSERT(node->link != (intptr)node);
|
||||
DBUG_ASSERT(cursor.prev != &node->link);
|
||||
if (!my_atomic_casptr((void **)cursor.prev, (void **)&cursor.curr, node))
|
||||
{
|
||||
res= REPEAT_ONCE_MORE;
|
||||
node->flags&= ~ACTIVE;
|
||||
}
|
||||
if (res & LOCK_UPGRADE)
|
||||
cursor.upgrade_from->flags|= IGNORE_ME;
|
||||
/*
|
||||
QQ: is this OK ? if a reader has already read upgrade_from,
|
||||
it may find it conflicting with node :(
|
||||
- see the last test from test_lockman_simple()
|
||||
*/
|
||||
}
|
||||
|
||||
} while (res == REPEAT_ONCE_MORE);
|
||||
_lf_unpin(pins, 0);
|
||||
_lf_unpin(pins, 1);
|
||||
_lf_unpin(pins, 2);
|
||||
/*
|
||||
note that blocker is not necessarily pinned here (when it's == curr).
|
||||
this is ok as in such a case it's either a dummy node for
|
||||
initialize_bucket() and dummy nodes don't need pinning,
|
||||
or it's a lock of the same transaction for lockman_getlock,
|
||||
and it cannot be removed by another thread
|
||||
*/
|
||||
*blocker= cursor.blocker;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE
|
||||
it uses pins[0..3], on return pins 0..2 are removed, pin 3 (blocker) stays
|
||||
*/
|
||||
static int lockpeek(LOCK * volatile *head, LOCK *node, LF_PINS *pins,
|
||||
LOCK **blocker)
|
||||
{
|
||||
CURSOR cursor;
|
||||
int res;
|
||||
|
||||
res= lockfind(head, node, &cursor, pins);
|
||||
|
||||
_lf_unpin(pins, 0);
|
||||
_lf_unpin(pins, 1);
|
||||
_lf_unpin(pins, 2);
|
||||
if (blocker)
|
||||
*blocker= cursor.blocker;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
NOTE
|
||||
it uses pins[0..3], on return all pins are removed.
|
||||
|
||||
One _must_ have the lock (or request) to call this
|
||||
*/
|
||||
static int lockdelete(LOCK * volatile *head, LOCK *node, LF_PINS *pins)
|
||||
{
|
||||
CURSOR cursor;
|
||||
int res;
|
||||
|
||||
do
|
||||
{
|
||||
res= lockfind(head, node, &cursor, pins);
|
||||
DBUG_ASSERT(res & ALREADY_HAVE);
|
||||
|
||||
if (cursor.upgrade_from)
|
||||
cursor.upgrade_from->flags&= ~IGNORE_ME;
|
||||
|
||||
/*
|
||||
XXX this does not work with savepoints, as old lock is left ignored.
|
||||
It cannot be unignored, as would basically mean moving the lock back
|
||||
in the lock chain (from upgraded). And the latter is not allowed -
|
||||
because it breaks list scanning. So old ignored lock must be deleted,
|
||||
new - same - lock must be installed right after the lock we're deleting,
|
||||
then we can delete. Good news is - this is only required when rolling
|
||||
back a savepoint.
|
||||
*/
|
||||
if (my_atomic_casptr((void **)&(cursor.curr->link),
|
||||
(void **)&cursor.next, 1+(char *)cursor.next))
|
||||
{
|
||||
if (my_atomic_casptr((void **)cursor.prev,
|
||||
(void **)&cursor.curr, cursor.next))
|
||||
_lf_alloc_free(pins, cursor.curr);
|
||||
else
|
||||
lockfind(head, node, &cursor, pins);
|
||||
}
|
||||
else
|
||||
{
|
||||
res= REPEAT_ONCE_MORE;
|
||||
if (cursor.upgrade_from)
|
||||
cursor.upgrade_from->flags|= IGNORE_ME;
|
||||
}
|
||||
} while (res == REPEAT_ONCE_MORE);
|
||||
_lf_unpin(pins, 0);
|
||||
_lf_unpin(pins, 1);
|
||||
_lf_unpin(pins, 2);
|
||||
_lf_unpin(pins, 3);
|
||||
return res;
|
||||
}
|
||||
|
||||
void lockman_init(LOCKMAN *lm, loid_to_lo_func *func, uint timeout)
|
||||
{
|
||||
lf_alloc_init(&lm->alloc, sizeof(LOCK), offsetof(LOCK, lonext));
|
||||
lf_dynarray_init(&lm->array, sizeof(LOCK **));
|
||||
lm->size= 1;
|
||||
lm->count= 0;
|
||||
lm->loid_to_lo= func;
|
||||
lm->lock_timeout= timeout;
|
||||
}
|
||||
|
||||
void lockman_destroy(LOCKMAN *lm)
|
||||
{
|
||||
LOCK *el= *(LOCK **)_lf_dynarray_lvalue(&lm->array, 0);
|
||||
while (el)
|
||||
{
|
||||
intptr next= el->link;
|
||||
if (el->hashnr & 1)
|
||||
lf_alloc_real_free(&lm->alloc, el);
|
||||
else
|
||||
my_free((void *)el, MYF(0));
|
||||
el= (LOCK *)next;
|
||||
}
|
||||
lf_alloc_destroy(&lm->alloc);
|
||||
lf_dynarray_destroy(&lm->array);
|
||||
}
|
||||
|
||||
/* TODO: optimize it */
|
||||
#define MAX_LOAD 1
|
||||
|
||||
static void initialize_bucket(LOCKMAN *lm, LOCK * volatile *node,
|
||||
uint bucket, LF_PINS *pins)
|
||||
{
|
||||
int res;
|
||||
uint parent= my_clear_highest_bit(bucket);
|
||||
LOCK *dummy= (LOCK *)my_malloc(sizeof(LOCK), MYF(MY_WME));
|
||||
LOCK **tmp= 0, *cur;
|
||||
LOCK * volatile *el= _lf_dynarray_lvalue(&lm->array, parent);
|
||||
|
||||
if (*el == NULL && bucket)
|
||||
initialize_bucket(lm, el, parent, pins);
|
||||
dummy->hashnr= my_reverse_bits(bucket);
|
||||
dummy->loid= 0;
|
||||
dummy->lock= X; /* doesn't matter, in fact */
|
||||
dummy->resource= 0;
|
||||
dummy->flags= 0;
|
||||
res= lockinsert(el, dummy, pins, &cur);
|
||||
DBUG_ASSERT(res & (ALREADY_HAVE_THE_LOCK | RESOURCE_WAS_UNLOCKED));
|
||||
if (res & ALREADY_HAVE_THE_LOCK)
|
||||
{
|
||||
my_free((void *)dummy, MYF(0));
|
||||
dummy= cur;
|
||||
}
|
||||
my_atomic_casptr((void **)node, (void **)&tmp, dummy);
|
||||
}
|
||||
|
||||
static inline uint calc_hash(uint64 resource)
|
||||
{
|
||||
const uchar *pos= (uchar *)&resource;
|
||||
ulong nr1= 1, nr2= 4, i;
|
||||
for (i= 0; i < sizeof(resource) ; i++, pos++)
|
||||
{
|
||||
nr1^= (ulong) ((((uint) nr1 & 63)+nr2) * ((uint)*pos)) + (nr1 << 8);
|
||||
nr2+= 3;
|
||||
}
|
||||
return nr1 & INT_MAX32;
|
||||
}
|
||||
|
||||
/*
|
||||
RETURN
|
||||
see enum lockman_getlock_result
|
||||
NOTE
|
||||
uses pins[0..3], they're removed on return
|
||||
*/
|
||||
enum lockman_getlock_result lockman_getlock(LOCKMAN *lm, LOCK_OWNER *lo,
|
||||
uint64 resource,
|
||||
enum lock_type lock)
|
||||
{
|
||||
int res;
|
||||
uint csize, bucket, hashnr;
|
||||
LOCK *node, * volatile *el, *blocker;
|
||||
LF_PINS *pins= lo->pins;
|
||||
enum lock_type old_lock;
|
||||
|
||||
DBUG_ASSERT(lo->loid);
|
||||
lf_rwlock_by_pins(pins);
|
||||
node= (LOCK *)_lf_alloc_new(pins);
|
||||
node->flags= 0;
|
||||
node->lock= lock;
|
||||
node->loid= lo->loid;
|
||||
node->resource= resource;
|
||||
hashnr= calc_hash(resource);
|
||||
bucket= hashnr % lm->size;
|
||||
el= _lf_dynarray_lvalue(&lm->array, bucket);
|
||||
if (*el == NULL)
|
||||
initialize_bucket(lm, el, bucket, pins);
|
||||
node->hashnr= my_reverse_bits(hashnr) | 1;
|
||||
res= lockinsert(el, node, pins, &blocker);
|
||||
if (res & ALREADY_HAVE)
|
||||
{
|
||||
int r;
|
||||
old_lock= blocker->lock;
|
||||
_lf_alloc_free(pins, node);
|
||||
lf_rwunlock_by_pins(pins);
|
||||
r= getlock_result[old_lock][lock];
|
||||
DBUG_ASSERT(r);
|
||||
return r;
|
||||
}
|
||||
/* a new value was added to the hash */
|
||||
csize= lm->size;
|
||||
if ((my_atomic_add32(&lm->count, 1)+1.0) / csize > MAX_LOAD)
|
||||
my_atomic_cas32(&lm->size, &csize, csize*2);
|
||||
node->lonext= lo->all_locks;
|
||||
lo->all_locks= node;
|
||||
for ( ; res & NEED_TO_WAIT; res= lockpeek(el, node, pins, &blocker))
|
||||
{
|
||||
LOCK_OWNER *wait_for_lo;
|
||||
ulonglong deadline;
|
||||
struct timespec timeout;
|
||||
|
||||
_lf_assert_pin(pins, 3); /* blocker must be pinned here */
|
||||
wait_for_lo= lm->loid_to_lo(blocker->loid);
|
||||
|
||||
/*
|
||||
now, this is tricky. blocker is not necessarily a LOCK
|
||||
we're waiting for. If it's compatible with what we want,
|
||||
then we're waiting for a lock that blocker is waiting for
|
||||
(see two places where blocker is set in lockfind)
|
||||
In the latter case, let's "dereference" it
|
||||
*/
|
||||
if (lock_compatibility_matrix[blocker->lock][lock])
|
||||
{
|
||||
blocker= wait_for_lo->all_locks;
|
||||
_lf_pin(pins, 3, blocker);
|
||||
if (blocker != wait_for_lo->all_locks)
|
||||
continue;
|
||||
wait_for_lo= wait_for_lo->waiting_for;
|
||||
}
|
||||
|
||||
/*
|
||||
note that the blocker transaction may have ended by now,
|
||||
its LOCK_OWNER and short id were reused, so 'wait_for_lo' may point
|
||||
to an unrelated - albeit valid - LOCK_OWNER
|
||||
*/
|
||||
if (!wait_for_lo)
|
||||
continue;
|
||||
|
||||
lo->waiting_for= wait_for_lo;
|
||||
lf_rwunlock_by_pins(pins);
|
||||
|
||||
/*
|
||||
We lock a mutex - it may belong to a wrong LOCK_OWNER, but it must
|
||||
belong to _some_ LOCK_OWNER. It means, we can never free() a LOCK_OWNER,
|
||||
if there're other active LOCK_OWNERs.
|
||||
*/
|
||||
/* QQ: race condition here */
|
||||
pthread_mutex_lock(wait_for_lo->mutex);
|
||||
if (DELETED(blocker->link))
|
||||
{
|
||||
/*
|
||||
blocker transaction was ended, or a savepoint that owned
|
||||
the lock was rolled back. Either way - the lock was removed
|
||||
*/
|
||||
pthread_mutex_unlock(wait_for_lo->mutex);
|
||||
lf_rwlock_by_pins(pins);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* yuck. waiting */
|
||||
deadline= my_getsystime() + lm->lock_timeout * 10000;
|
||||
timeout.tv_sec= deadline/10000000;
|
||||
timeout.tv_nsec= (deadline % 10000000) * 100;
|
||||
do
|
||||
{
|
||||
pthread_cond_timedwait(wait_for_lo->cond, wait_for_lo->mutex, &timeout);
|
||||
} while (!DELETED(blocker->link) && my_getsystime() < deadline);
|
||||
pthread_mutex_unlock(wait_for_lo->mutex);
|
||||
lf_rwlock_by_pins(pins);
|
||||
if (!DELETED(blocker->link))
|
||||
{
|
||||
/*
|
||||
timeout.
|
||||
note that we _don't_ release the lock request here.
|
||||
Instead we're relying on the caller to abort the transaction,
|
||||
and release all locks at once - see lockman_release_locks()
|
||||
*/
|
||||
_lf_unpin(pins, 3);
|
||||
lf_rwunlock_by_pins(pins);
|
||||
return DIDNT_GET_THE_LOCK;
|
||||
}
|
||||
}
|
||||
lo->waiting_for= 0;
|
||||
_lf_assert_unpin(pins, 3); /* unpin should not be needed */
|
||||
lf_rwunlock_by_pins(pins);
|
||||
return getlock_result[lock][lock];
|
||||
}
|
||||
|
||||
/*
|
||||
RETURN
|
||||
0 - deleted
|
||||
1 - didn't (not found)
|
||||
NOTE
|
||||
see lockdelete() for pin usage notes
|
||||
*/
|
||||
int lockman_release_locks(LOCKMAN *lm, LOCK_OWNER *lo)
|
||||
{
|
||||
LOCK * volatile *el, *node, *next;
|
||||
uint bucket;
|
||||
LF_PINS *pins= lo->pins;
|
||||
|
||||
pthread_mutex_lock(lo->mutex);
|
||||
lf_rwlock_by_pins(pins);
|
||||
for (node= lo->all_locks; node; node= next)
|
||||
{
|
||||
next= node->lonext;
|
||||
bucket= calc_hash(node->resource) % lm->size;
|
||||
el= _lf_dynarray_lvalue(&lm->array, bucket);
|
||||
if (*el == NULL)
|
||||
initialize_bucket(lm, el, bucket, pins);
|
||||
lockdelete(el, node, pins);
|
||||
my_atomic_add32(&lm->count, -1);
|
||||
}
|
||||
lf_rwunlock_by_pins(pins);
|
||||
lo->all_locks= 0;
|
||||
/* now signal all waiters */
|
||||
pthread_cond_broadcast(lo->cond);
|
||||
pthread_mutex_unlock(lo->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MY_LF_EXTRA_DEBUG
|
||||
static const char *lock2str[]=
|
||||
{ "N", "S", "X", "IS", "IX", "SIX", "LS", "LX", "SLX", "LSIX" };
|
||||
/*
|
||||
NOTE
|
||||
the function below is NOT thread-safe !!!
|
||||
*/
|
||||
void print_lockhash(LOCKMAN *lm)
|
||||
{
|
||||
LOCK *el= *(LOCK **)_lf_dynarray_lvalue(&lm->array, 0);
|
||||
printf("hash: size %u count %u\n", lm->size, lm->count);
|
||||
while (el)
|
||||
{
|
||||
intptr next= el->link;
|
||||
if (el->hashnr & 1)
|
||||
{
|
||||
printf("0x%08lx { resource %lu, loid %u, lock %s",
|
||||
(long) el->hashnr, (ulong) el->resource, el->loid,
|
||||
lock2str[el->lock]);
|
||||
if (el->flags & IGNORE_ME) printf(" IGNORE_ME");
|
||||
if (el->flags & UPGRADED) printf(" UPGRADED");
|
||||
if (el->flags & ACTIVE) printf(" ACTIVE");
|
||||
if (DELETED(next)) printf(" ***DELETED***");
|
||||
printf("}\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
/*printf("0x%08x { dummy }\n", el->hashnr);*/
|
||||
DBUG_ASSERT(el->resource == 0 && el->loid == 0 && el->lock == X);
|
||||
}
|
||||
el= PTR(next);
|
||||
}
|
||||
}
|
||||
#endif
|
77
storage/maria/lockman.h
Normal file
77
storage/maria/lockman.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; 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 */
|
||||
|
||||
#ifndef _lockman_h
|
||||
#define _lockman_h
|
||||
|
||||
/*
|
||||
Lock levels:
|
||||
^^^^^^^^^^^
|
||||
|
||||
N - "no lock", not a lock, used sometimes internally to simplify the code
|
||||
S - Shared
|
||||
X - eXclusive
|
||||
IS - Intention Shared
|
||||
IX - Intention eXclusive
|
||||
SIX - Shared + Intention eXclusive
|
||||
LS - Loose Shared
|
||||
LX - Loose eXclusive
|
||||
SLX - Shared + Loose eXclusive
|
||||
LSIX - Loose Shared + Intention eXclusive
|
||||
*/
|
||||
enum lock_type { N, S, X, IS, IX, SIX, LS, LX, SLX, LSIX, LOCK_TYPE_LAST };
|
||||
|
||||
struct lockman_lock;
|
||||
|
||||
typedef struct st_lock_owner LOCK_OWNER;
|
||||
struct st_lock_owner {
|
||||
LF_PINS *pins; /* must be allocated from lockman's pinbox */
|
||||
struct lockman_lock *all_locks; /* a LIFO */
|
||||
LOCK_OWNER *waiting_for;
|
||||
pthread_cond_t *cond; /* transactions waiting for this, wait on 'cond' */
|
||||
pthread_mutex_t *mutex; /* mutex is required to use 'cond' */
|
||||
uint16 loid;
|
||||
};
|
||||
|
||||
typedef LOCK_OWNER *loid_to_lo_func(uint16);
|
||||
typedef struct {
|
||||
LF_DYNARRAY array; /* hash itself */
|
||||
LF_ALLOCATOR alloc; /* allocator for elements */
|
||||
int32 volatile size; /* size of array */
|
||||
int32 volatile count; /* number of elements in the hash */
|
||||
uint lock_timeout;
|
||||
loid_to_lo_func *loid_to_lo;
|
||||
} LOCKMAN;
|
||||
#define DIDNT_GET_THE_LOCK 0
|
||||
enum lockman_getlock_result {
|
||||
NO_MEMORY_FOR_LOCK=1, DEADLOCK, LOCK_TIMEOUT,
|
||||
GOT_THE_LOCK,
|
||||
GOT_THE_LOCK_NEED_TO_LOCK_A_SUBRESOURCE,
|
||||
GOT_THE_LOCK_NEED_TO_INSTANT_LOCK_A_SUBRESOURCE
|
||||
};
|
||||
|
||||
void lockman_init(LOCKMAN *, loid_to_lo_func *, uint);
|
||||
void lockman_destroy(LOCKMAN *);
|
||||
enum lockman_getlock_result lockman_getlock(LOCKMAN *lm, LOCK_OWNER *lo,
|
||||
uint64 resource,
|
||||
enum lock_type lock);
|
||||
int lockman_release_locks(LOCKMAN *, LOCK_OWNER *);
|
||||
|
||||
#ifdef EXTRA_DEBUG
|
||||
void print_lockhash(LOCKMAN *lm);
|
||||
#endif
|
||||
|
||||
#endif
|
1704
storage/maria/ma_bitmap.c
Normal file
1704
storage/maria/ma_bitmap.c
Normal file
File diff suppressed because it is too large
Load Diff
2743
storage/maria/ma_blockrec.c
Normal file
2743
storage/maria/ma_blockrec.c
Normal file
File diff suppressed because it is too large
Load Diff
160
storage/maria/ma_blockrec.h
Normal file
160
storage/maria/ma_blockrec.h
Normal file
@ -0,0 +1,160 @@
|
||||
/* Copyright (C) 2007 Michael Widenius
|
||||
|
||||
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 */
|
||||
|
||||
/*
|
||||
Storage of records in block
|
||||
*/
|
||||
|
||||
#define LSN_SIZE 7
|
||||
#define DIRCOUNT_SIZE 1 /* Stores number of rows on page */
|
||||
#define EMPTY_SPACE_SIZE 2 /* Stores empty space on page */
|
||||
#define PAGE_TYPE_SIZE 1
|
||||
#define PAGE_SUFFIX_SIZE 0 /* Bytes for page suffix */
|
||||
#define PAGE_HEADER_SIZE (LSN_SIZE + DIRCOUNT_SIZE + EMPTY_SPACE_SIZE +\
|
||||
PAGE_TYPE_SIZE)
|
||||
#define PAGE_OVERHEAD_SIZE (PAGE_HEADER_SIZE + DIR_ENTRY_SIZE + \
|
||||
PAGE_SUFFIX_SIZE)
|
||||
#define BLOCK_RECORD_POINTER_SIZE 6
|
||||
|
||||
#define FULL_PAGE_SIZE(block_size) ((block_size) - LSN_SIZE - PAGE_TYPE_SIZE)
|
||||
|
||||
#define ROW_EXTENT_PAGE_SIZE 5
|
||||
#define ROW_EXTENT_COUNT_SIZE 2
|
||||
#define ROW_EXTENT_SIZE (ROW_EXTENT_PAGE_SIZE + ROW_EXTENT_COUNT_SIZE)
|
||||
#define TAIL_BIT 0x8000 /* Bit in page_count to signify tail */
|
||||
#define ELEMENTS_RESERVED_FOR_MAIN_PART 4
|
||||
#define EXTRA_LENGTH_FIELDS 3
|
||||
|
||||
#define FLAG_SIZE 1
|
||||
#define TRANSID_SIZE 6
|
||||
#define VERPTR_SIZE 7
|
||||
#define DIR_ENTRY_SIZE 4
|
||||
#define FIELD_OFFSET_SIZE 2
|
||||
|
||||
/* Minimum header size needed for a new row */
|
||||
#define BASE_ROW_HEADER_SIZE FLAG_SIZE
|
||||
#define TRANS_ROW_EXTRA_HEADER_SIZE TRANSID_SIZE
|
||||
|
||||
#define PAGE_TYPE_MASK 127
|
||||
enum en_page_type { UNALLOCATED_PAGE, HEAD_PAGE, TAIL_PAGE, BLOB_PAGE, MAX_PAGE_TYPE };
|
||||
|
||||
#define PAGE_TYPE_OFFSET LSN_SIZE
|
||||
#define DIR_ENTRY_OFFSET LSN_SIZE+PAGE_TYPE_SIZE
|
||||
#define EMPTY_SPACE_OFFSET (DIR_ENTRY_OFFSET + DIRCOUNT_SIZE)
|
||||
|
||||
#define PAGE_CAN_BE_COMPACTED 128 /* Bit in PAGE_TYPE */
|
||||
|
||||
/* Bits used for flag byte (one byte, first in record) */
|
||||
#define ROW_FLAG_TRANSID 1
|
||||
#define ROW_FLAG_VER_PTR 2
|
||||
#define ROW_FLAG_DELETE_TRANSID 4
|
||||
#define ROW_FLAG_NULLS_EXTENDED 8
|
||||
#define ROW_FLAG_EXTENTS 128
|
||||
#define ROW_FLAG_ALL (1+2+4+8+128)
|
||||
|
||||
/* Variables that affects how data pages are utilized */
|
||||
#define MIN_TAIL_SIZE 32
|
||||
|
||||
/* Fixed part of Max possible header size; See table in ma_blockrec.c */
|
||||
#define MAX_FIXED_HEADER_SIZE (FLAG_SIZE + 3 + ROW_EXTENT_SIZE + 3)
|
||||
#define TRANS_MAX_FIXED_HEADER_SIZE (MAX_FIXED_HEADER_SIZE + \
|
||||
FLAG_SIZE + TRANSID_SIZE + VERPTR_SIZE + \
|
||||
TRANSID_SIZE)
|
||||
|
||||
/* We use 1 byte in record header to store number of directory entries */
|
||||
#define MAX_ROWS_PER_PAGE 255
|
||||
|
||||
/* Bits for MARIA_BITMAP_BLOCKS->used */
|
||||
#define BLOCKUSED_USED 1
|
||||
#define BLOCKUSED_USE_ORG_BITMAP 2
|
||||
#define BLOCKUSED_TAIL 4
|
||||
|
||||
/* defines that affects allocation (density) of data */
|
||||
|
||||
/* If we fill up a block to 75 %, don't create a new tail page for it */
|
||||
#define MAX_TAIL_SIZE(block_size) ((block_size) *3 / 4)
|
||||
|
||||
/* Functions to convert MARIA_RECORD_POS to/from page:offset */
|
||||
|
||||
static inline MARIA_RECORD_POS ma_recordpos(ulonglong page, uint offset)
|
||||
{
|
||||
return (MARIA_RECORD_POS) ((page << 8) | offset);
|
||||
}
|
||||
|
||||
static inline my_off_t ma_recordpos_to_page(MARIA_RECORD_POS record_pos)
|
||||
{
|
||||
return record_pos >> 8;
|
||||
}
|
||||
|
||||
static inline my_off_t ma_recordpos_to_offset(MARIA_RECORD_POS record_pos)
|
||||
{
|
||||
return record_pos & 255;
|
||||
}
|
||||
|
||||
/* ma_blockrec.c */
|
||||
void _ma_init_block_record_data(void);
|
||||
my_bool _ma_once_init_block_row(MARIA_SHARE *share, File dfile);
|
||||
my_bool _ma_once_end_block_row(MARIA_SHARE *share);
|
||||
my_bool _ma_init_block_row(MARIA_HA *info);
|
||||
void _ma_end_block_row(MARIA_HA *info);
|
||||
|
||||
my_bool _ma_update_block_record(MARIA_HA *info, MARIA_RECORD_POS pos,
|
||||
const byte *record);
|
||||
my_bool _ma_delete_block_record(MARIA_HA *info);
|
||||
int _ma_read_block_record(MARIA_HA *info, byte *record,
|
||||
MARIA_RECORD_POS record_pos);
|
||||
int _ma_read_block_record2(MARIA_HA *info, byte *record,
|
||||
byte *data, byte *end_of_data);
|
||||
int _ma_scan_block_record(MARIA_HA *info, byte *record,
|
||||
MARIA_RECORD_POS, my_bool);
|
||||
my_bool _ma_cmp_block_unique(MARIA_HA *info, MARIA_UNIQUEDEF *def,
|
||||
const byte *record, MARIA_RECORD_POS pos);
|
||||
my_bool _ma_scan_init_block_record(MARIA_HA *info);
|
||||
void _ma_scan_end_block_record(MARIA_HA *info);
|
||||
|
||||
MARIA_RECORD_POS _ma_write_init_block_record(MARIA_HA *info,
|
||||
const byte *record);
|
||||
my_bool _ma_write_block_record(MARIA_HA *info, const byte *record);
|
||||
my_bool _ma_write_abort_block_record(MARIA_HA *info);
|
||||
my_bool _ma_compare_block_record(register MARIA_HA *info,
|
||||
register const byte *record);
|
||||
|
||||
/* ma_bitmap.c */
|
||||
my_bool _ma_bitmap_init(MARIA_SHARE *share, File file);
|
||||
my_bool _ma_bitmap_end(MARIA_SHARE *share);
|
||||
my_bool _ma_flush_bitmap(MARIA_SHARE *share);
|
||||
my_bool _ma_read_bitmap_page(MARIA_SHARE *share, MARIA_FILE_BITMAP *bitmap,
|
||||
ulonglong page);
|
||||
my_bool _ma_bitmap_find_place(MARIA_HA *info, MARIA_ROW *row,
|
||||
MARIA_BITMAP_BLOCKS *result_blocks);
|
||||
my_bool _ma_bitmap_release_unused(MARIA_HA *info, MARIA_BITMAP_BLOCKS *blocks);
|
||||
my_bool _ma_bitmap_free_full_pages(MARIA_HA *info, const byte *extents,
|
||||
uint count);
|
||||
my_bool _ma_bitmap_set(MARIA_HA *info, ulonglong pos, my_bool head,
|
||||
uint empty_space);
|
||||
my_bool _ma_reset_full_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
|
||||
ulonglong page, uint page_count);
|
||||
uint _ma_free_size_to_head_pattern(MARIA_FILE_BITMAP *bitmap, uint size);
|
||||
my_bool _ma_bitmap_find_new_place(MARIA_HA *info, MARIA_ROW *new_row,
|
||||
ulonglong page, uint free_size,
|
||||
MARIA_BITMAP_BLOCKS *result_blocks);
|
||||
my_bool _ma_check_bitmap_data(MARIA_HA *info,
|
||||
enum en_page_type page_type, ulonglong page,
|
||||
uint empty_space, uint *bitmap_pattern);
|
||||
my_bool _ma_check_if_right_bitmap_type(MARIA_HA *info,
|
||||
enum en_page_type page_type,
|
||||
ulonglong page,
|
||||
uint *bitmap_pattern);
|
108
storage/maria/ma_cache.c
Normal file
108
storage/maria/ma_cache.c
Normal file
@ -0,0 +1,108 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/*
|
||||
Functions for read record cacheing with maria
|
||||
Used for reading dynamic/compressed records from datafile.
|
||||
|
||||
Can fetch data directly from file (outside cache),
|
||||
if reading a small chunk straight before the cached part (with possible
|
||||
overlap).
|
||||
|
||||
Can be explicitly asked not to use cache (by not setting READING_NEXT in
|
||||
flag) - useful for occasional out-of-cache reads, when the next read is
|
||||
expected to hit the cache again.
|
||||
|
||||
Allows "partial read" errors in the record header (when READING_HEADER flag
|
||||
is set) - unread part is bzero'ed
|
||||
|
||||
Note: out-of-cache reads are enabled for shared IO_CACHE's too,
|
||||
as these reads will be cached by OS cache (and my_pread is always atomic)
|
||||
*/
|
||||
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
int _ma_read_cache(IO_CACHE *info, byte *buff, my_off_t pos, uint length,
|
||||
int flag)
|
||||
{
|
||||
uint read_length,in_buff_length;
|
||||
my_off_t offset;
|
||||
char *in_buff_pos;
|
||||
DBUG_ENTER("_ma_read_cache");
|
||||
|
||||
if (pos < info->pos_in_file)
|
||||
{
|
||||
read_length=length;
|
||||
if ((my_off_t) read_length > (my_off_t) (info->pos_in_file-pos))
|
||||
read_length=(uint) (info->pos_in_file-pos);
|
||||
info->seek_not_done=1;
|
||||
if (my_pread(info->file,buff,read_length,pos,MYF(MY_NABP)))
|
||||
DBUG_RETURN(1);
|
||||
if (!(length-=read_length))
|
||||
DBUG_RETURN(0);
|
||||
pos+=read_length;
|
||||
buff+=read_length;
|
||||
}
|
||||
if (pos >= info->pos_in_file &&
|
||||
(offset= (my_off_t) (pos - info->pos_in_file)) <
|
||||
(my_off_t) (info->read_end - info->request_pos))
|
||||
{
|
||||
in_buff_pos=info->request_pos+(uint) offset;
|
||||
in_buff_length= min(length,(uint) (info->read_end-in_buff_pos));
|
||||
memcpy(buff,info->request_pos+(uint) offset,(size_t) in_buff_length);
|
||||
if (!(length-=in_buff_length))
|
||||
DBUG_RETURN(0);
|
||||
pos+=in_buff_length;
|
||||
buff+=in_buff_length;
|
||||
}
|
||||
else
|
||||
in_buff_length=0;
|
||||
if (flag & READING_NEXT)
|
||||
{
|
||||
if (pos != (info->pos_in_file +
|
||||
(uint) (info->read_end - info->request_pos)))
|
||||
{
|
||||
info->pos_in_file=pos; /* Force start here */
|
||||
info->read_pos=info->read_end=info->request_pos; /* Everything used */
|
||||
info->seek_not_done=1;
|
||||
}
|
||||
else
|
||||
info->read_pos=info->read_end; /* All block used */
|
||||
if (!(*info->read_function)(info,buff,length))
|
||||
DBUG_RETURN(0);
|
||||
read_length=info->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->seek_not_done=1;
|
||||
if ((read_length=my_pread(info->file,buff,length,pos,MYF(0))) == length)
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
if (!(flag & READING_HEADER) || (int) read_length == -1 ||
|
||||
read_length+in_buff_length < 3)
|
||||
{
|
||||
DBUG_PRINT("error",
|
||||
("Error %d reading next-multi-part block (Got %d bytes)",
|
||||
my_errno, (int) read_length));
|
||||
if (!my_errno || my_errno == -1)
|
||||
my_errno=HA_ERR_WRONG_IN_RECORD;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
bzero(buff+read_length,MARIA_BLOCK_INFO_HEADER_LENGTH - in_buff_length -
|
||||
read_length);
|
||||
DBUG_RETURN(0);
|
||||
} /* _ma_read_cache */
|
34
storage/maria/ma_changed.c
Normal file
34
storage/maria/ma_changed.c
Normal file
@ -0,0 +1,34 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/* Check if somebody has changed table since last check. */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Return 0 if table isn't changed */
|
||||
|
||||
int maria_is_changed(MARIA_HA *info)
|
||||
{
|
||||
int result;
|
||||
DBUG_ENTER("maria_is_changed");
|
||||
if (fast_ma_readinfo(info))
|
||||
DBUG_RETURN(-1);
|
||||
VOID(_ma_writeinfo(info,0));
|
||||
result=(int) info->data_changed;
|
||||
info->data_changed=0;
|
||||
DBUG_PRINT("exit",("result: %d",result));
|
||||
DBUG_RETURN(result);
|
||||
}
|
5096
storage/maria/ma_check.c
Normal file
5096
storage/maria/ma_check.c
Normal file
File diff suppressed because it is too large
Load Diff
429
storage/maria/ma_checkpoint.c
Normal file
429
storage/maria/ma_checkpoint.c
Normal file
@ -0,0 +1,429 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/*
|
||||
WL#3071 Maria checkpoint
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/* Here is the implementation of this module */
|
||||
|
||||
/*
|
||||
Summary:
|
||||
- there are asynchronous checkpoints (a writer to the log notices that it's
|
||||
been a long time since we last checkpoint-ed, so posts a request for a
|
||||
background thread to do a checkpoint; does not care about the success of the
|
||||
checkpoint). Then the checkpoint is done by the checkpoint thread, at an
|
||||
unspecified moment ("later") (==soon, of course).
|
||||
- there are synchronous checkpoints: a thread requests a checkpoint to
|
||||
happen now and wants to know when it finishes and if it succeeded; then the
|
||||
checkpoint is done by that same thread.
|
||||
*/
|
||||
|
||||
#include "page_cache.h"
|
||||
#include "least_recently_dirtied.h"
|
||||
#include "transaction.h"
|
||||
#include "share.h"
|
||||
#include "log.h"
|
||||
|
||||
#define LSN_IMPOSSIBLE ((LSN)0) /* could also be called LSN_ERROR */
|
||||
#define LSN_MAX ((LSN)ULONGLONG_MAX)
|
||||
|
||||
/*
|
||||
this transaction is used for any system work (purge, checkpoint writing
|
||||
etc), that is, background threads. It will not be declared/initialized here
|
||||
in the final version.
|
||||
*/
|
||||
st_transaction system_trans= {0 /* long trans id */, 0 /* short trans id */,0,...};
|
||||
|
||||
/* those three are protected by the log's mutex */
|
||||
/*
|
||||
The maximum rec_lsn in the LRD when last checkpoint was run, serves for the
|
||||
MEDIUM checkpoint.
|
||||
*/
|
||||
LSN max_rec_lsn_at_last_checkpoint= 0;
|
||||
/* last submitted checkpoint request; cleared when starts */
|
||||
CHECKPOINT_LEVEL next_asynchronous_checkpoint_to_do= NONE;
|
||||
CHECKPOINT_LEVEL checkpoint_in_progress= NONE;
|
||||
|
||||
static inline ulonglong read_non_atomic(ulonglong volatile *x);
|
||||
|
||||
/*
|
||||
Used by MySQL client threads requesting a checkpoint (like "ALTER MARIA
|
||||
ENGINE DO CHECKPOINT"), and probably by maria_panic(), and at the end of the
|
||||
UNDO recovery phase.
|
||||
*/
|
||||
my_bool execute_synchronous_checkpoint(CHECKPOINT_LEVEL level)
|
||||
{
|
||||
my_bool result;
|
||||
DBUG_ENTER("execute_synchronous_checkpoint");
|
||||
DBUG_ASSERT(level > NONE);
|
||||
|
||||
lock(log_mutex);
|
||||
while (checkpoint_in_progress != NONE)
|
||||
wait_on_checkpoint_done_cond();
|
||||
|
||||
result= execute_checkpoint(level);
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
/*
|
||||
If no checkpoint is running, and there is a pending asynchronous checkpoint
|
||||
request, executes it.
|
||||
Is safe if multiple threads call it, though in first version only one will.
|
||||
It's intended to be used by a thread which regularly calls this function;
|
||||
this is why, if there is a request, it does not wait in a loop for
|
||||
synchronous checkpoints to be finished, but just exits (because the thread
|
||||
may want to do something useful meanwhile (flushing dirty pages for example)
|
||||
instead of waiting).
|
||||
*/
|
||||
my_bool execute_asynchronous_checkpoint_if_any()
|
||||
{
|
||||
my_bool result;
|
||||
CHECKPOINT_LEVEL level;
|
||||
DBUG_ENTER("execute_asynchronous_checkpoint");
|
||||
|
||||
/* first check without mutex, ok to see old data */
|
||||
if (likely((next_asynchronous_checkpoint_to_do == NONE) ||
|
||||
(checkpoint_in_progress != NONE)))
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
lock(log_mutex);
|
||||
if (likely((next_asynchronous_checkpoint_to_do == NONE) ||
|
||||
(checkpoint_in_progress != NONE)))
|
||||
{
|
||||
unlock(log_mutex);
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
result= execute_checkpoint(next_asynchronous_checkpoint_to_do);
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Does the actual checkpointing. Called by
|
||||
execute_synchronous_checkpoint() and
|
||||
execute_asynchronous_checkpoint_if_any().
|
||||
*/
|
||||
my_bool execute_checkpoint(CHECKPOINT_LEVEL level)
|
||||
{
|
||||
my_bool result;
|
||||
DBUG_ENTER("execute_checkpoint");
|
||||
|
||||
safemutex_assert_owner(log_mutex);
|
||||
if (next_asynchronous_checkpoint_to_do <= level)
|
||||
next_asynchronous_checkpoint_to_do= NONE;
|
||||
checkpoint_in_progress= level;
|
||||
|
||||
if (unlikely(level > INDIRECT))
|
||||
{
|
||||
LSN copy_of_max_rec_lsn_at_last_checkpoint=
|
||||
max_rec_lsn_at_last_checkpoint;
|
||||
/* much I/O work to do, release log mutex */
|
||||
unlock(log_mutex);
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case FULL:
|
||||
/* flush all pages up to the current end of the LRD */
|
||||
flush_all_LRD_to_lsn(LSN_MAX);
|
||||
/* this will go full speed (normal scheduling, no sleep) */
|
||||
break;
|
||||
case MEDIUM:
|
||||
/*
|
||||
flush all pages which were already dirty at last checkpoint:
|
||||
ensures that recovery will never start from before the next-to-last
|
||||
checkpoint (two-checkpoint rule).
|
||||
*/
|
||||
flush_all_LRD_to_lsn(copy_of_max_rec_lsn_at_last_checkpoint);
|
||||
/* this will go full speed (normal scheduling, no sleep) */
|
||||
break;
|
||||
}
|
||||
lock(log_mutex);
|
||||
}
|
||||
|
||||
result= execute_checkpoint_indirect();
|
||||
checkpoint_in_progress= NONE;
|
||||
unlock(log_mutex);
|
||||
broadcast(checkpoint_done_cond);
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Does an indirect checpoint (collects data from data structures, writes into
|
||||
a checkpoint log record).
|
||||
Starts and ends while having log's mutex (released in the middle).
|
||||
*/
|
||||
my_bool execute_checkpoint_indirect()
|
||||
{
|
||||
int error= 0, i;
|
||||
/* checkpoint record data: */
|
||||
LSN checkpoint_start_lsn;
|
||||
char checkpoint_start_lsn_char[8];
|
||||
LEX_STRING strings[6]=
|
||||
{checkpoint_start_lsn_char, 8}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0} };
|
||||
char *ptr;
|
||||
LSN checkpoint_lsn;
|
||||
LSN candidate_max_rec_lsn_at_last_checkpoint;
|
||||
DBUG_ENTER("execute_checkpoint_indirect");
|
||||
|
||||
DBUG_ASSERT(sizeof(byte *) <= 8);
|
||||
DBUG_ASSERT(sizeof(LSN) <= 8);
|
||||
|
||||
safemutex_assert_owner(log_mutex);
|
||||
|
||||
/* STEP 1: record current end-of-log LSN */
|
||||
checkpoint_start_lsn= log_read_end_lsn();
|
||||
if (LSN_IMPOSSIBLE == checkpoint_start_lsn) /* error */
|
||||
DBUG_RETURN(TRUE);
|
||||
unlock(log_mutex);
|
||||
|
||||
DBUG_PRINT("info",("checkpoint_start_lsn %lu", checkpoint_start_lsn));
|
||||
int8store(strings[0].str, checkpoint_start_lsn);
|
||||
|
||||
/* STEP 2: fetch information about dirty pages */
|
||||
|
||||
if (pagecache_collect_changed_blocks_with_LSN(pagecache, &strings[1],
|
||||
&candidate_max_rec_lsn_at_last_checkpoint))
|
||||
goto err;
|
||||
|
||||
/* STEP 3: fetch information about transactions */
|
||||
if (trnman_collect_transactions(&strings[2], &strings[3]))
|
||||
goto err;
|
||||
|
||||
/* STEP 4: fetch information about table files */
|
||||
|
||||
{
|
||||
/* This global mutex is in fact THR_LOCK_maria (see ma_open()) */
|
||||
lock(global_share_list_mutex);
|
||||
strings[4].length= 8+(8+8)*share_list->count;
|
||||
if (NULL == (strings[4].str= my_malloc(strings[4].length)))
|
||||
goto err;
|
||||
ptr= string3.str;
|
||||
/*
|
||||
Note that maria_open_list is a list of MARIA_HA*, while we would prefer
|
||||
a list of MARIA_SHARE* here (we are interested in the short id,
|
||||
unique file name, members of MARIA_SHARE*, and in file descriptors,
|
||||
which will in the end be in MARIA_SHARE*).
|
||||
*/
|
||||
for (iterate on the maria_open_list)
|
||||
{
|
||||
/* latch each MARIA_SHARE, one by one, like this: */
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
/*
|
||||
TODO:
|
||||
we need to prevent the share from going away while we later flush and
|
||||
force it without holding THR_LOCK_maria. For example if the share is
|
||||
free()d by maria_close() we'll have a problem. Or if the share's file
|
||||
descriptor is closed by maria_close() we will not be able to my_sync()
|
||||
it.
|
||||
*/
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
store the share pointer into a private array;
|
||||
}
|
||||
unlock(global_share_list_mutex);
|
||||
|
||||
/* work on copy */
|
||||
int8store(ptr, elements_in_array);
|
||||
ptr+= 8;
|
||||
for (el in array)
|
||||
{
|
||||
int8store(ptr, array[...].short_id);
|
||||
ptr+= 8;
|
||||
memcpy(ptr, array[...].unique_file_name[_length], ...);
|
||||
ptr+= ...;
|
||||
/* maybe we need to lock share->intern_lock here */
|
||||
/*
|
||||
these two are long ops (involving disk I/O) that's why we copied the
|
||||
list, to not keep the list locked for long:
|
||||
*/
|
||||
flush_bitmap_pages(el);
|
||||
/* TODO: and also autoinc counter, logical file end, free page list */
|
||||
|
||||
/*
|
||||
fsyncs the fd, that's the loooong operation (e.g. max 150 fsync per
|
||||
second, so if you have touched 1000 files it's 7 seconds).
|
||||
*/
|
||||
force_file(el);
|
||||
}
|
||||
}
|
||||
|
||||
/* LAST STEP: now write the checkpoint log record */
|
||||
|
||||
checkpoint_lsn= log_write_record(LOGREC_CHECKPOINT,
|
||||
&system_trans, strings);
|
||||
|
||||
/*
|
||||
Do nothing between the log write and the control file write, for the
|
||||
"repair control file" tool to be possible one day.
|
||||
*/
|
||||
|
||||
if (LSN_IMPOSSIBLE == checkpoint_lsn)
|
||||
goto err;
|
||||
|
||||
if (0 != control_file_write_and_force(checkpoint_lsn, NULL))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
Note that we should not alter memory structures until we have successfully
|
||||
written the checkpoint record and control file.
|
||||
Btw, a log write failure is serious:
|
||||
- if we know how many bytes we managed to write, we should try to write
|
||||
more, keeping the log's mutex (MY_FULL_IO)
|
||||
- if we don't know, this log record is corrupted and we have no way to
|
||||
"de-corrupt" it, so it will stay corrupted, and as the log is sequential,
|
||||
any log record written after it will not be reachable (for example if we
|
||||
would write UNDOs and crash, we would not be able to read the log and so
|
||||
not be able to rollback), so we should stop the engine now (holding the
|
||||
log's mutex) and do a recovery.
|
||||
*/
|
||||
goto end;
|
||||
|
||||
err:
|
||||
print_error_to_error_log(the_error_message);
|
||||
candidate_max_rec_lsn_at_last_checkpoint= LSN_IMPOSSIBLE;
|
||||
|
||||
end:
|
||||
|
||||
for (i= 1; i<6; i++)
|
||||
my_free(strings[i].str, MYF(MY_ALLOW_ZERO_PTR));
|
||||
|
||||
/*
|
||||
this portion cannot be done as a hook in write_log_record() for the
|
||||
LOGREC_CHECKPOINT type because:
|
||||
- at that moment we still have not written to the control file so cannot
|
||||
mark the request as done; this could be solved by writing to the control
|
||||
file in the hook but that would be an I/O under the log's mutex, bad.
|
||||
- it would not be nice organisation of code (I tried it :).
|
||||
*/
|
||||
if (candidate_max_rec_lsn_at_last_checkpoint != LSN_IMPOSSIBLE)
|
||||
{
|
||||
/* checkpoint succeeded */
|
||||
/*
|
||||
TODO: compute log's low water mark (how to do that with our fuzzy
|
||||
ARIES-like reads of data structures? TODO think about it :).
|
||||
*/
|
||||
lock(log_mutex);
|
||||
/* That LSN is used for the "two-checkpoint rule" (MEDIUM checkpoints) */
|
||||
maximum_rec_lsn_last_checkpoint= candidate_max_rec_lsn_at_last_checkpoint;
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
lock(log_mutex);
|
||||
DBUG_RETURN(TRUE);
|
||||
/*
|
||||
keep mutex locked upon exit because callers will want to clear
|
||||
mutex-protected status variables
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Here's what should be put in log_write_record() in the log handler:
|
||||
*/
|
||||
log_write_record(...)
|
||||
{
|
||||
...;
|
||||
lock(log_mutex);
|
||||
...;
|
||||
write_to_log(length);
|
||||
written_since_last_checkpoint+= length;
|
||||
if (written_since_last_checkpoint >
|
||||
MAX_LOG_BYTES_WRITTEN_BETWEEN_CHECKPOINTS)
|
||||
{
|
||||
/*
|
||||
ask one system thread (the "LRD background flusher and checkpointer
|
||||
thread" WL#3261) to do a checkpoint
|
||||
*/
|
||||
request_asynchronous_checkpoint(INDIRECT);
|
||||
/* prevent similar redundant requests */
|
||||
written_since_last_checkpoint= (my_off_t)0;
|
||||
}
|
||||
...;
|
||||
unlock(log_mutex);
|
||||
...;
|
||||
}
|
||||
|
||||
/*
|
||||
Requests a checkpoint from the background thread, *asynchronously*
|
||||
(requestor does not wait for completion, and does not even later check the
|
||||
result).
|
||||
In real life it will be called by log_write_record().
|
||||
*/
|
||||
void request_asynchronous_checkpoint(CHECKPOINT_LEVEL level);
|
||||
{
|
||||
safemutex_assert_owner(log_mutex);
|
||||
|
||||
DBUG_ASSERT(level > NONE);
|
||||
if ((next_asynchronous_checkpoint_to_do < level) &&
|
||||
(checkpoint_in_progress < level))
|
||||
{
|
||||
/* no equal or stronger running or to run, we post request */
|
||||
/*
|
||||
We just don't broacast a cond, the checkpoint thread
|
||||
(see ma_least_recently_dirtied.c) will notice our request in max a few
|
||||
seconds.
|
||||
*/
|
||||
next_asynchronous_checkpoint_to_do= level; /* post request */
|
||||
}
|
||||
|
||||
/*
|
||||
If there was an error, only an error
|
||||
message to the error log will say it; normal, for a checkpoint triggered
|
||||
by a log write, we probably don't want the client's log write to throw an
|
||||
error, as the log write succeeded and a checkpoint failure is not
|
||||
critical: the failure in this case is more for the DBA to know than for
|
||||
the end user.
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
If a 64-bit variable transitions from both halves being zero to both halves
|
||||
being non-zero, and never changes after that (like the transaction's
|
||||
first_undo_lsn), this function can be used to do a read of it (without
|
||||
mutex, without atomic load) which always produces a correct (though maybe
|
||||
slightly old) value (even on 32-bit CPUs).
|
||||
The prototype will change with Sanja's new LSN type.
|
||||
*/
|
||||
static inline ulonglong read_non_atomic(ulonglong volatile *x)
|
||||
{
|
||||
#if ( SIZEOF_CHARP >= 8 )
|
||||
/* 64-bit CPU (right?), 64-bit reads are atomic */
|
||||
return *x;
|
||||
#else
|
||||
/*
|
||||
32-bit CPU, 64-bit reads may give a mixed of old half and new half (old
|
||||
low bits and new high bits, or the contrary).
|
||||
As the variable we read transitions from both halves being zero to both
|
||||
halves being non-zero, and never changes then, we can detect atomicity
|
||||
problems:
|
||||
*/
|
||||
ulonglong y;
|
||||
for (;;) /* loop until no atomicity problems */
|
||||
{
|
||||
y= *x;
|
||||
if (likely(((0 == y) ||
|
||||
((0 != (y >> 32)) && (0 != (y << 32)))))
|
||||
return y;
|
||||
/* Worth seeing it! */
|
||||
DBUG_PRINT("info",("atomicity problem"));
|
||||
}
|
||||
#endif
|
||||
}
|
35
storage/maria/ma_checkpoint.h
Normal file
35
storage/maria/ma_checkpoint.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/*
|
||||
WL#3071 Maria checkpoint
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/* This is the interface of this module. */
|
||||
|
||||
typedef enum enum_checkpoint_level {
|
||||
NONE=-1,
|
||||
INDIRECT, /* just write dirty_pages, transactions table and sync files */
|
||||
MEDIUM, /* also flush all dirty pages which were already dirty at prev checkpoint*/
|
||||
FULL /* also flush all dirty pages */
|
||||
} CHECKPOINT_LEVEL;
|
||||
|
||||
void request_asynchronous_checkpoint(CHECKPOINT_LEVEL level);
|
||||
my_bool execute_synchronous_checkpoint(CHECKPOINT_LEVEL level);
|
||||
my_bool execute_asynchronous_checkpoint_if_any();
|
||||
/* that's all that's needed in the interface */
|
68
storage/maria/ma_checksum.c
Normal file
68
storage/maria/ma_checksum.c
Normal file
@ -0,0 +1,68 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/* Calculate a checksum for a row */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
ha_checksum _ma_checksum(MARIA_HA *info, const byte *record)
|
||||
{
|
||||
uint i;
|
||||
ha_checksum crc=0;
|
||||
MARIA_COLUMNDEF *rec=info->s->rec;
|
||||
|
||||
if (info->s->base.null_bytes)
|
||||
crc= my_checksum(crc, record, info->s->base.null_bytes);
|
||||
|
||||
for (i=info->s->base.fields ; i-- ; )
|
||||
{
|
||||
const byte *pos= record + rec->offset;
|
||||
ulong length;
|
||||
|
||||
switch (rec->type) {
|
||||
case FIELD_BLOB:
|
||||
{
|
||||
length= _ma_calc_blob_length(rec->length-
|
||||
maria_portable_sizeof_char_ptr,
|
||||
pos);
|
||||
memcpy((char*) &pos, pos+rec->length- maria_portable_sizeof_char_ptr,
|
||||
sizeof(char*));
|
||||
break;
|
||||
}
|
||||
case FIELD_VARCHAR:
|
||||
{
|
||||
uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length-1);
|
||||
if (pack_length == 1)
|
||||
length= (ulong) *(uchar*) pos;
|
||||
else
|
||||
length= uint2korr(pos);
|
||||
pos+= pack_length;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
length= rec->length;
|
||||
break;
|
||||
}
|
||||
crc= my_checksum(crc, pos ? pos : "", length);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
ha_checksum _ma_static_checksum(MARIA_HA *info, const byte *pos)
|
||||
{
|
||||
return my_checksum(0, pos, info->s->base.reclength);
|
||||
}
|
133
storage/maria/ma_close.c
Normal file
133
storage/maria/ma_close.c
Normal file
@ -0,0 +1,133 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/* close a isam-database */
|
||||
/*
|
||||
TODO:
|
||||
We need to have a separate mutex on the closed file to allow other threads
|
||||
to open other files during the time we flush the cache and close this file
|
||||
*/
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
int maria_close(register MARIA_HA *info)
|
||||
{
|
||||
int error=0,flag;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_close");
|
||||
DBUG_PRINT("enter",("base: 0x%lx reopen: %u locks: %u",
|
||||
(long) info, (uint) share->reopen,
|
||||
(uint) share->tot_locks));
|
||||
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
if (info->lock_type == F_EXTRA_LCK)
|
||||
info->lock_type=F_UNLCK; /* HA_EXTRA_NO_USER_CHANGE */
|
||||
|
||||
if (share->reopen == 1 && share->kfile >= 0)
|
||||
_ma_decrement_open_count(info);
|
||||
|
||||
if (info->lock_type != F_UNLCK)
|
||||
{
|
||||
if (maria_lock_database(info,F_UNLCK))
|
||||
error=my_errno;
|
||||
}
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
|
||||
if (share->options & HA_OPTION_READ_ONLY_DATA)
|
||||
{
|
||||
share->r_locks--;
|
||||
share->tot_locks--;
|
||||
}
|
||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||
{
|
||||
if (end_io_cache(&info->rec_cache))
|
||||
error=my_errno;
|
||||
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
|
||||
}
|
||||
flag= !--share->reopen;
|
||||
/*
|
||||
RECOVERYTODO:
|
||||
If "flag" is TRUE, in the line below we are going to make the table
|
||||
unknown to future checkpoints, so it needs to have fsync'ed itself
|
||||
entirely (bitmap, pages, etc) at this point.
|
||||
The flushing is currently done a few lines further (which is ok, as we
|
||||
still hold THR_LOCK_maria), but syncing is missing.
|
||||
*/
|
||||
maria_open_list=list_delete(maria_open_list,&info->open_list);
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
|
||||
my_free(info->rec_buff, MYF(MY_ALLOW_ZERO_PTR));
|
||||
(share->end)(info);
|
||||
|
||||
if (info->s->data_file_type == BLOCK_RECORD)
|
||||
info->dfile= -1; /* Closed in ma_end_once_block_row */
|
||||
if (flag)
|
||||
{
|
||||
if (share->kfile >= 0)
|
||||
{
|
||||
if ((*share->once_end)(share))
|
||||
error= my_errno;
|
||||
if (flush_key_blocks(share->key_cache, share->kfile,
|
||||
share->temporary ? FLUSH_IGNORE_CHANGED :
|
||||
FLUSH_RELEASE))
|
||||
error= my_errno;
|
||||
|
||||
/*
|
||||
If we are crashed, we can safely flush the current state as it will
|
||||
not change the crashed state.
|
||||
We can NOT write the state in other cases as other threads
|
||||
may be using the file at this point
|
||||
*/
|
||||
if (share->mode != O_RDONLY && maria_is_crashed(info))
|
||||
_ma_state_info_write(share->kfile, &share->state, 1);
|
||||
if (my_close(share->kfile, MYF(0)))
|
||||
error= my_errno;
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
if (share->file_map)
|
||||
_ma_unmap_file(info);
|
||||
#endif
|
||||
#ifdef THREAD
|
||||
thr_lock_delete(&share->lock);
|
||||
VOID(pthread_mutex_destroy(&share->intern_lock));
|
||||
{
|
||||
int i,keys;
|
||||
keys = share->state.header.keys;
|
||||
VOID(rwlock_destroy(&share->mmap_lock));
|
||||
for(i=0; i<keys; i++) {
|
||||
VOID(rwlock_destroy(&share->key_root_lock[i]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
my_free((gptr) info->s,MYF(0));
|
||||
}
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
if (info->ftparser_param)
|
||||
{
|
||||
my_free((gptr)info->ftparser_param, MYF(0));
|
||||
info->ftparser_param= 0;
|
||||
}
|
||||
if (info->dfile >= 0 && my_close(info->dfile,MYF(0)))
|
||||
error = my_errno;
|
||||
|
||||
my_free((gptr) info,MYF(0));
|
||||
|
||||
if (error)
|
||||
{
|
||||
DBUG_RETURN(my_errno=error);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
} /* maria_close */
|
320
storage/maria/ma_control_file.c
Normal file
320
storage/maria/ma_control_file.c
Normal file
@ -0,0 +1,320 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/*
|
||||
WL#3234 Maria control file
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Here is the implementation of this module */
|
||||
|
||||
/*
|
||||
a control file contains 3 objects: magic string, LSN of last checkpoint,
|
||||
number of last log.
|
||||
*/
|
||||
|
||||
/* total size should be < sector size for atomic write operation */
|
||||
#define CONTROL_FILE_MAGIC_STRING "\xfe\xfe\xc\1MACF"
|
||||
#define CONTROL_FILE_MAGIC_STRING_OFFSET 0
|
||||
#define CONTROL_FILE_MAGIC_STRING_SIZE (sizeof(CONTROL_FILE_MAGIC_STRING)-1)
|
||||
#define CONTROL_FILE_CHECKSUM_OFFSET (CONTROL_FILE_MAGIC_STRING_OFFSET + CONTROL_FILE_MAGIC_STRING_SIZE)
|
||||
#define CONTROL_FILE_CHECKSUM_SIZE 1
|
||||
#define CONTROL_FILE_LSN_OFFSET (CONTROL_FILE_CHECKSUM_OFFSET + CONTROL_FILE_CHECKSUM_SIZE)
|
||||
#define CONTROL_FILE_LSN_SIZE LSN_STORE_SIZE
|
||||
#define CONTROL_FILE_FILENO_OFFSET (CONTROL_FILE_LSN_OFFSET + CONTROL_FILE_LSN_SIZE)
|
||||
#define CONTROL_FILE_FILENO_SIZE 4
|
||||
#define CONTROL_FILE_SIZE (CONTROL_FILE_FILENO_OFFSET + CONTROL_FILE_FILENO_SIZE)
|
||||
|
||||
/*
|
||||
This module owns these two vars.
|
||||
uint32 is always atomically updated, but LSN is 8 bytes, we will need
|
||||
provisions to ensure that it's updated atomically in
|
||||
ma_control_file_write_and_force(). Probably the log mutex could be
|
||||
used. TODO.
|
||||
*/
|
||||
LSN last_checkpoint_lsn;
|
||||
uint32 last_logno;
|
||||
|
||||
|
||||
/*
|
||||
Control file is less then 512 bytes (a disk sector),
|
||||
to be as atomic as possible
|
||||
*/
|
||||
static int control_file_fd= -1;
|
||||
|
||||
static char simple_checksum(char *buffer, uint size)
|
||||
{
|
||||
/* TODO: improve this sum if we want */
|
||||
char s= 0;
|
||||
uint i;
|
||||
for (i= 0; i<size; i++)
|
||||
s+= buffer[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize control file subsystem
|
||||
|
||||
SYNOPSIS
|
||||
ma_control_file_create_or_open()
|
||||
|
||||
Looks for the control file. If absent, it's a fresh start, creates file.
|
||||
If present, reads it to find out last checkpoint's LSN and last log, updates
|
||||
the last_checkpoint_lsn and last_logno global variables.
|
||||
Called at engine's start.
|
||||
|
||||
The format of the control file is:
|
||||
4 bytes: magic string
|
||||
1 byte: checksum of the following bytes
|
||||
4 bytes: number of log where last checkpoint is
|
||||
4 bytes: offset in log where last checkpoint is
|
||||
4 bytes: number of last log
|
||||
|
||||
RETURN
|
||||
0 - OK
|
||||
1 - Error (in which case the file is left closed)
|
||||
*/
|
||||
CONTROL_FILE_ERROR ma_control_file_create_or_open()
|
||||
{
|
||||
char buffer[CONTROL_FILE_SIZE];
|
||||
char name[FN_REFLEN];
|
||||
MY_STAT stat_buff;
|
||||
my_bool create_file;
|
||||
int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR;
|
||||
int error= CONTROL_FILE_UNKNOWN_ERROR;
|
||||
DBUG_ENTER("ma_control_file_create_or_open");
|
||||
|
||||
/*
|
||||
If you change sizes in the #defines, you at least have to change the
|
||||
"*store" and "*korr" calls in this file, and can even create backward
|
||||
compatibility problems. Beware!
|
||||
*/
|
||||
DBUG_ASSERT(CONTROL_FILE_LSN_SIZE == (3+4));
|
||||
DBUG_ASSERT(CONTROL_FILE_FILENO_SIZE == 4);
|
||||
|
||||
if (control_file_fd >= 0) /* already open */
|
||||
DBUG_RETURN(0);
|
||||
|
||||
if (fn_format(name, CONTROL_FILE_BASE_NAME,
|
||||
maria_data_root, "", MYF(MY_WME)) == NullS)
|
||||
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
|
||||
|
||||
create_file= test(my_access(name,F_OK));
|
||||
|
||||
if (create_file)
|
||||
{
|
||||
if ((control_file_fd= my_create(name, 0,
|
||||
open_flags, MYF(MY_SYNC_DIR))) < 0)
|
||||
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
|
||||
|
||||
/*
|
||||
To be safer we should make sure that there are no logs or data/index
|
||||
files around (indeed it could be that the control file alone was deleted
|
||||
or not restored, and we should not go on with life at this point).
|
||||
|
||||
TODO: For now we trust (this is alpha version), but for beta if would
|
||||
be great to verify.
|
||||
|
||||
We could have a tool which can rebuild the control file, by reading the
|
||||
directory of logs, finding the newest log, reading it to find last
|
||||
checkpoint... Slow but can save your db. For this to be possible, we
|
||||
must always write to the control file right after writing the checkpoint
|
||||
log record, and do nothing in between (i.e. the checkpoint must be
|
||||
usable as soon as it has been written to the log).
|
||||
*/
|
||||
|
||||
/* init the file with these "undefined" values */
|
||||
DBUG_RETURN(ma_control_file_write_and_force(CONTROL_FILE_IMPOSSIBLE_LSN,
|
||||
CONTROL_FILE_IMPOSSIBLE_FILENO,
|
||||
CONTROL_FILE_UPDATE_ALL));
|
||||
}
|
||||
|
||||
/* Otherwise, file exists */
|
||||
|
||||
if ((control_file_fd= my_open(name, open_flags, MYF(MY_WME))) < 0)
|
||||
goto err;
|
||||
|
||||
if (my_stat(name, &stat_buff, MYF(MY_WME)) == NULL)
|
||||
goto err;
|
||||
|
||||
if ((uint)stat_buff.st_size < CONTROL_FILE_SIZE)
|
||||
{
|
||||
/*
|
||||
Given that normally we write only a sector and it's atomic, the only
|
||||
possibility for a file to be of too short size is if we crashed at the
|
||||
very first startup, between file creation and file write. Quite unlikely
|
||||
(and can be made even more unlikely by doing this: create a temp file,
|
||||
write it, and then rename it to be the control file).
|
||||
What's more likely is if someone forgot to restore the control file,
|
||||
just did a "touch control" to try to get Maria to start, or if the
|
||||
disk/filesystem has a problem.
|
||||
So let's be rigid.
|
||||
*/
|
||||
/*
|
||||
TODO: store a message "too small file" somewhere, so that it goes to
|
||||
MySQL's error log at startup.
|
||||
*/
|
||||
error= CONTROL_FILE_TOO_SMALL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((uint)stat_buff.st_size > CONTROL_FILE_SIZE)
|
||||
{
|
||||
/* TODO: store "too big file" message */
|
||||
error= CONTROL_FILE_TOO_BIG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (my_read(control_file_fd, buffer, CONTROL_FILE_SIZE,
|
||||
MYF(MY_FNABP | MY_WME)))
|
||||
goto err;
|
||||
if (memcmp(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
|
||||
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE))
|
||||
{
|
||||
/* TODO: store message "bad magic string" somewhere */
|
||||
error= CONTROL_FILE_BAD_MAGIC_STRING;
|
||||
goto err;
|
||||
}
|
||||
if (simple_checksum(buffer + CONTROL_FILE_LSN_OFFSET,
|
||||
CONTROL_FILE_SIZE - CONTROL_FILE_LSN_OFFSET) !=
|
||||
buffer[CONTROL_FILE_CHECKSUM_OFFSET])
|
||||
{
|
||||
/* TODO: store message "checksum mismatch" somewhere */
|
||||
error= CONTROL_FILE_BAD_CHECKSUM;
|
||||
goto err;
|
||||
}
|
||||
last_checkpoint_lsn= lsn_korr(buffer + CONTROL_FILE_LSN_OFFSET);
|
||||
last_logno= uint4korr(buffer + CONTROL_FILE_FILENO_OFFSET);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
err:
|
||||
ma_control_file_end();
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Write information durably to the control file; stores this information into
|
||||
the last_checkpoint_lsn and last_logno global variables.
|
||||
Called when we have created a new log (after syncing this log's creation)
|
||||
and when we have written a checkpoint (after syncing this log record).
|
||||
|
||||
SYNOPSIS
|
||||
ma_control_file_write_and_force()
|
||||
checkpoint_lsn LSN of last checkpoint
|
||||
logno last log file number
|
||||
objs_to_write which of the arguments should be used as new values
|
||||
(for example, CONTROL_FILE_UPDATE_ONLY_LSN will not
|
||||
write the logno argument to the control file and will
|
||||
not update the last_logno global variable); can be:
|
||||
CONTROL_FILE_UPDATE_ALL
|
||||
CONTROL_FILE_UPDATE_ONLY_LSN
|
||||
CONTROL_FILE_UPDATE_ONLY_LOGNO.
|
||||
|
||||
NOTE
|
||||
We always want to do one single my_pwrite() here to be as atomic as
|
||||
possible.
|
||||
|
||||
RETURN
|
||||
0 - OK
|
||||
1 - Error
|
||||
*/
|
||||
|
||||
int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno,
|
||||
uint objs_to_write)
|
||||
{
|
||||
char buffer[CONTROL_FILE_SIZE];
|
||||
my_bool update_checkpoint_lsn= FALSE, update_logno= FALSE;
|
||||
DBUG_ENTER("ma_control_file_write_and_force");
|
||||
|
||||
DBUG_ASSERT(control_file_fd >= 0); /* must be open */
|
||||
|
||||
memcpy(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
|
||||
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE);
|
||||
|
||||
/* TODO: you need some protection to be able to read last_* global vars */
|
||||
|
||||
if (objs_to_write == CONTROL_FILE_UPDATE_ONLY_LSN)
|
||||
update_checkpoint_lsn= TRUE;
|
||||
else if (objs_to_write == CONTROL_FILE_UPDATE_ONLY_LOGNO)
|
||||
update_logno= TRUE;
|
||||
else if (objs_to_write == CONTROL_FILE_UPDATE_ALL)
|
||||
update_checkpoint_lsn= update_logno= TRUE;
|
||||
else /* incorrect value of objs_to_write */
|
||||
DBUG_ASSERT(0);
|
||||
|
||||
if (update_checkpoint_lsn)
|
||||
lsn_store(buffer + CONTROL_FILE_LSN_OFFSET, checkpoint_lsn);
|
||||
else /* store old value == change nothing */
|
||||
lsn_store(buffer + CONTROL_FILE_LSN_OFFSET, last_checkpoint_lsn);
|
||||
|
||||
if (update_logno)
|
||||
int4store(buffer + CONTROL_FILE_FILENO_OFFSET, logno);
|
||||
else
|
||||
int4store(buffer + CONTROL_FILE_FILENO_OFFSET, last_logno);
|
||||
|
||||
buffer[CONTROL_FILE_CHECKSUM_OFFSET]=
|
||||
simple_checksum(buffer + CONTROL_FILE_LSN_OFFSET,
|
||||
CONTROL_FILE_SIZE - CONTROL_FILE_LSN_OFFSET);
|
||||
|
||||
if (my_pwrite(control_file_fd, buffer, sizeof(buffer),
|
||||
0, MYF(MY_FNABP | MY_WME)) ||
|
||||
my_sync(control_file_fd, MYF(MY_WME)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/* TODO: you need some protection to be able to write last_* global vars */
|
||||
if (update_checkpoint_lsn)
|
||||
last_checkpoint_lsn= checkpoint_lsn;
|
||||
if (update_logno)
|
||||
last_logno= logno;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Free resources taken by control file subsystem
|
||||
|
||||
SYNOPSIS
|
||||
ma_control_file_end()
|
||||
*/
|
||||
|
||||
int ma_control_file_end()
|
||||
{
|
||||
int close_error;
|
||||
DBUG_ENTER("ma_control_file_end");
|
||||
|
||||
if (control_file_fd < 0) /* already closed */
|
||||
DBUG_RETURN(0);
|
||||
|
||||
close_error= my_close(control_file_fd, MYF(MY_WME));
|
||||
/*
|
||||
As my_close() frees structures even if close() fails, we do the same,
|
||||
i.e. we mark the file as closed in all cases.
|
||||
*/
|
||||
control_file_fd= -1;
|
||||
/*
|
||||
As this module owns these variables, closing the module forbids access to
|
||||
them (just a safety):
|
||||
*/
|
||||
last_checkpoint_lsn= CONTROL_FILE_IMPOSSIBLE_LSN;
|
||||
last_logno= CONTROL_FILE_IMPOSSIBLE_FILENO;
|
||||
|
||||
DBUG_RETURN(close_error);
|
||||
}
|
77
storage/maria/ma_control_file.h
Normal file
77
storage/maria/ma_control_file.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/*
|
||||
WL#3234 Maria control file
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
*/
|
||||
|
||||
#define CONTROL_FILE_BASE_NAME "maria_control"
|
||||
/*
|
||||
indicate absence of the log file number; first log is always number 1, 0 is
|
||||
impossible.
|
||||
*/
|
||||
#define CONTROL_FILE_IMPOSSIBLE_FILENO 0
|
||||
/* logs always have a header */
|
||||
#define CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET 0
|
||||
/* indicate absence of LSN. */
|
||||
#define CONTROL_FILE_IMPOSSIBLE_LSN ((LSN)0)
|
||||
|
||||
/* Here is the interface of this module */
|
||||
|
||||
/*
|
||||
LSN of the last checkoint
|
||||
(if last_checkpoint_lsn == CONTROL_FILE_IMPOSSIBLE_LSN
|
||||
then there was never a checkpoint)
|
||||
*/
|
||||
extern LSN last_checkpoint_lsn;
|
||||
/*
|
||||
Last log number (if last_logno ==
|
||||
CONTROL_FILE_IMPOSSIBLE_FILENO then there is no log file yet)
|
||||
*/
|
||||
extern uint32 last_logno;
|
||||
|
||||
typedef enum enum_control_file_error {
|
||||
CONTROL_FILE_OK= 0,
|
||||
CONTROL_FILE_TOO_SMALL,
|
||||
CONTROL_FILE_TOO_BIG,
|
||||
CONTROL_FILE_BAD_MAGIC_STRING,
|
||||
CONTROL_FILE_BAD_CHECKSUM,
|
||||
CONTROL_FILE_UNKNOWN_ERROR /* any other error */
|
||||
} CONTROL_FILE_ERROR;
|
||||
|
||||
#define CONTROL_FILE_UPDATE_ALL 0
|
||||
#define CONTROL_FILE_UPDATE_ONLY_LSN 1
|
||||
#define CONTROL_FILE_UPDATE_ONLY_LOGNO 2
|
||||
|
||||
|
||||
/*
|
||||
Looks for the control file. If absent, it's a fresh start, create file.
|
||||
If present, read it to find out last checkpoint's LSN and last log.
|
||||
Called at engine's start.
|
||||
*/
|
||||
CONTROL_FILE_ERROR ma_control_file_create_or_open();
|
||||
/*
|
||||
Write information durably to the control file.
|
||||
Called when we have created a new log (after syncing this log's creation)
|
||||
and when we have written a checkpoint (after syncing this log record).
|
||||
*/
|
||||
int ma_control_file_write_and_force(const LSN checkpoint_lsn, uint32 logno,
|
||||
uint objs_to_write);
|
||||
|
||||
|
||||
/* Free resources taken by control file subsystem */
|
||||
int ma_control_file_end();
|
1043
storage/maria/ma_create.c
Normal file
1043
storage/maria/ma_create.c
Normal file
File diff suppressed because it is too large
Load Diff
193
storage/maria/ma_dbug.c
Normal file
193
storage/maria/ma_dbug.c
Normal file
@ -0,0 +1,193 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/* Support rutiner with are using with dbug */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Print a key in user understandable format */
|
||||
|
||||
void _ma_print_key(FILE *stream, register HA_KEYSEG *keyseg,
|
||||
const byte *key, uint length)
|
||||
{
|
||||
int flag;
|
||||
short int s_1;
|
||||
long int l_1;
|
||||
float f_1;
|
||||
double d_1;
|
||||
const byte *end;
|
||||
const byte *key_end= key + length;
|
||||
|
||||
VOID(fputs("Key: \"",stream));
|
||||
flag=0;
|
||||
for (; keyseg->type && key < key_end ;keyseg++)
|
||||
{
|
||||
if (flag++)
|
||||
VOID(putc('-',stream));
|
||||
end= key+ keyseg->length;
|
||||
if (keyseg->flag & HA_NULL_PART)
|
||||
{
|
||||
/* A NULL value is encoded by a 1-byte flag. Zero means NULL. */
|
||||
if (! *(key++))
|
||||
{
|
||||
fprintf(stream,"NULL");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (keyseg->type) {
|
||||
case HA_KEYTYPE_BINARY:
|
||||
if (!(keyseg->flag & HA_SPACE_PACK) && keyseg->length == 1)
|
||||
{ /* packed binary digit */
|
||||
VOID(fprintf(stream,"%d",(uint) *key++));
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case HA_KEYTYPE_TEXT:
|
||||
case HA_KEYTYPE_NUM:
|
||||
if (keyseg->flag & HA_SPACE_PACK)
|
||||
{
|
||||
VOID(fprintf(stream,"%.*s",(int) *key,key+1));
|
||||
key+= (int) *key+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
VOID(fprintf(stream,"%.*s",(int) keyseg->length,key));
|
||||
key=end;
|
||||
}
|
||||
break;
|
||||
case HA_KEYTYPE_INT8:
|
||||
VOID(fprintf(stream,"%d",(int) *((signed char*) key)));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
s_1= mi_sint2korr(key);
|
||||
VOID(fprintf(stream,"%d",(int) s_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
{
|
||||
ushort u_1;
|
||||
u_1= mi_uint2korr(key);
|
||||
VOID(fprintf(stream,"%u",(uint) u_1));
|
||||
key=end;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
l_1=mi_sint4korr(key);
|
||||
VOID(fprintf(stream,"%ld",l_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
l_1=mi_sint4korr(key);
|
||||
VOID(fprintf(stream,"%lu",(ulong) l_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
VOID(fprintf(stream,"%ld",(long) mi_sint3korr(key)));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
VOID(fprintf(stream,"%lu",(ulong) mi_uint3korr(key)));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
mi_float4get(f_1,key);
|
||||
VOID(fprintf(stream,"%g",(double) f_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
mi_float8get(d_1,key);
|
||||
VOID(fprintf(stream,"%g",d_1));
|
||||
key=end;
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
{
|
||||
char buff[21];
|
||||
longlong2str(mi_sint8korr(key),buff,-10);
|
||||
VOID(fprintf(stream,"%s",buff));
|
||||
key=end;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
{
|
||||
char buff[21];
|
||||
longlong2str(mi_sint8korr(key),buff,10);
|
||||
VOID(fprintf(stream,"%s",buff));
|
||||
key=end;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_BIT:
|
||||
{
|
||||
uint i;
|
||||
fputs("0x",stream);
|
||||
for (i=0 ; i < keyseg->length ; i++)
|
||||
fprintf(stream, "%02x", (uint) *key++);
|
||||
key= end;
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
case HA_KEYTYPE_VARTEXT1: /* VARCHAR and TEXT */
|
||||
case HA_KEYTYPE_VARTEXT2: /* VARCHAR and TEXT */
|
||||
case HA_KEYTYPE_VARBINARY1: /* VARBINARY and BLOB */
|
||||
case HA_KEYTYPE_VARBINARY2: /* VARBINARY and BLOB */
|
||||
{
|
||||
uint tmp_length;
|
||||
get_key_length(tmp_length,key);
|
||||
/*
|
||||
The following command sometimes gives a warning from valgrind.
|
||||
Not yet sure if the bug is in valgrind, glibc or mysqld
|
||||
*/
|
||||
VOID(fprintf(stream,"%.*s",(int) tmp_length,key));
|
||||
key+=tmp_length;
|
||||
break;
|
||||
}
|
||||
default: break; /* This never happens */
|
||||
}
|
||||
}
|
||||
VOID(fputs("\"\n",stream));
|
||||
return;
|
||||
} /* print_key */
|
||||
|
||||
|
||||
#ifdef EXTRA_DEBUG
|
||||
|
||||
my_bool _ma_check_table_is_closed(const char *name, const char *where)
|
||||
{
|
||||
char filename[FN_REFLEN];
|
||||
LIST *pos;
|
||||
DBUG_ENTER("_ma_check_table_is_closed");
|
||||
|
||||
(void) fn_format(filename,name,"",MARIA_NAME_IEXT,4+16+32);
|
||||
for (pos=maria_open_list ; pos ; pos=pos->next)
|
||||
{
|
||||
MARIA_HA *info=(MARIA_HA*) pos->data;
|
||||
MARIA_SHARE *share=info->s;
|
||||
if (!strcmp(share->unique_file_name,filename))
|
||||
{
|
||||
if (share->last_version)
|
||||
{
|
||||
fprintf(stderr,"Warning: Table: %s is open on %s\n", name,where);
|
||||
DBUG_PRINT("warning",("Table: %s is open on %s", name,where));
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
#endif /* EXTRA_DEBUG */
|
891
storage/maria/ma_delete.c
Normal file
891
storage/maria/ma_delete.c
Normal file
@ -0,0 +1,891 @@
|
||||
/* Copyright (C) 2006 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 */
|
||||
|
||||
/* Remove a row from a MARIA table */
|
||||
|
||||
#include "ma_fulltext.h"
|
||||
#include "ma_rt_index.h"
|
||||
|
||||
static int d_search(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uint comp_flag,
|
||||
byte *key,uint key_length,my_off_t page,byte *anc_buff);
|
||||
static int del(MARIA_HA *info,MARIA_KEYDEF *keyinfo,byte *key,byte *anc_buff,
|
||||
my_off_t leaf_page,byte *leaf_buff,byte *keypos,
|
||||
my_off_t next_block,byte *ret_key);
|
||||
static int underflow(MARIA_HA *info,MARIA_KEYDEF *keyinfo,byte *anc_buff,
|
||||
my_off_t leaf_page,byte *leaf_buff,byte *keypos);
|
||||
static uint remove_key(MARIA_KEYDEF *keyinfo,uint nod_flag,byte *keypos,
|
||||
byte *lastkey,byte *page_end,
|
||||
my_off_t *next_block);
|
||||
static int _ma_ck_real_delete(register MARIA_HA *info,MARIA_KEYDEF *keyinfo,
|
||||
byte *key, uint key_length, my_off_t *root);
|
||||
|
||||
|
||||
int maria_delete(MARIA_HA *info,const byte *record)
|
||||
{
|
||||
uint i;
|
||||
byte *old_key;
|
||||
int save_errno;
|
||||
char lastpos[8];
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_delete");
|
||||
|
||||
/* Test if record is in datafile */
|
||||
|
||||
DBUG_EXECUTE_IF("maria_pretend_crashed_table_on_usage",
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
DBUG_RETURN(my_errno= HA_ERR_CRASHED););
|
||||
DBUG_EXECUTE_IF("my_error_test_undefined_error",
|
||||
maria_print_error(info->s, INT_MAX);
|
||||
DBUG_RETURN(my_errno= INT_MAX););
|
||||
if (!(info->update & HA_STATE_AKTIV))
|
||||
{
|
||||
DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); /* No database read */
|
||||
}
|
||||
if (share->options & HA_OPTION_READ_ONLY_DATA)
|
||||
{
|
||||
DBUG_RETURN(my_errno=EACCES);
|
||||
}
|
||||
if (_ma_readinfo(info,F_WRLCK,1))
|
||||
DBUG_RETURN(my_errno);
|
||||
if ((*share->compare_record)(info,record))
|
||||
goto err; /* Error on read-check */
|
||||
|
||||
if (_ma_mark_file_changed(info))
|
||||
goto err;
|
||||
|
||||
/* Remove all keys from the index file */
|
||||
|
||||
old_key= info->lastkey2;
|
||||
for (i=0 ; i < share->base.keys ; i++ )
|
||||
{
|
||||
if (maria_is_key_active(info->s->state.key_map, i))
|
||||
{
|
||||
info->s->keyinfo[i].version++;
|
||||
if (info->s->keyinfo[i].flag & HA_FULLTEXT )
|
||||
{
|
||||
if (_ma_ft_del(info,i,(char*) old_key,record,info->cur_row.lastpos))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info->s->keyinfo[i].ck_delete(info,i,old_key,
|
||||
_ma_make_key(info,i,old_key,record,info->cur_row.lastpos)))
|
||||
goto err;
|
||||
}
|
||||
/* The above changed info->lastkey2. Inform maria_rnext_same(). */
|
||||
info->update&= ~HA_STATE_RNEXT_SAME;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*share->delete_record)(info))
|
||||
goto err; /* Remove record from database */
|
||||
|
||||
/*
|
||||
We can't use the row based checksum as this doesn't have enough
|
||||
precision.
|
||||
*/
|
||||
if (info->s->calc_checksum)
|
||||
{
|
||||
info->cur_row.checksum= (*info->s->calc_checksum)(info,record);
|
||||
info->state->checksum-= info->cur_row.checksum;
|
||||
}
|
||||
|
||||
info->update= HA_STATE_CHANGED+HA_STATE_DELETED+HA_STATE_ROW_CHANGED;
|
||||
info->state->records--;
|
||||
|
||||
mi_sizestore(lastpos, info->cur_row.lastpos);
|
||||
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
||||
allow_break(); /* Allow SIGHUP & SIGINT */
|
||||
if (info->invalidator != 0)
|
||||
{
|
||||
DBUG_PRINT("info", ("invalidator... '%s' (delete)", info->filename));
|
||||
(*info->invalidator)(info->filename);
|
||||
info->invalidator=0;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
save_errno=my_errno;
|
||||
mi_sizestore(lastpos, info->cur_row.lastpos);
|
||||
if (save_errno != HA_ERR_RECORD_CHANGED)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info); /* mark table crashed */
|
||||
}
|
||||
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
||||
info->update|=HA_STATE_WRITTEN; /* Buffer changed */
|
||||
allow_break(); /* Allow SIGHUP & SIGINT */
|
||||
my_errno=save_errno;
|
||||
if (save_errno == HA_ERR_KEY_NOT_FOUND)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno=HA_ERR_CRASHED;
|
||||
}
|
||||
|
||||
DBUG_RETURN(my_errno);
|
||||
} /* maria_delete */
|
||||
|
||||
|
||||
/* Remove a key from the btree index */
|
||||
|
||||
int _ma_ck_delete(register MARIA_HA *info, uint keynr, byte *key,
|
||||
uint key_length)
|
||||
{
|
||||
return _ma_ck_real_delete(info, info->s->keyinfo+keynr, key, key_length,
|
||||
&info->s->state.key_root[keynr]);
|
||||
} /* _ma_ck_delete */
|
||||
|
||||
|
||||
static int _ma_ck_real_delete(register MARIA_HA *info, MARIA_KEYDEF *keyinfo,
|
||||
byte *key, uint key_length, my_off_t *root)
|
||||
{
|
||||
int error;
|
||||
uint nod_flag;
|
||||
my_off_t old_root;
|
||||
byte *root_buff;
|
||||
DBUG_ENTER("_ma_ck_real_delete");
|
||||
|
||||
if ((old_root=*root) == HA_OFFSET_ERROR)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
DBUG_RETURN(my_errno=HA_ERR_CRASHED);
|
||||
}
|
||||
if (!(root_buff= (byte*) my_alloca((uint) keyinfo->block_length+
|
||||
HA_MAX_KEY_BUFF*2)))
|
||||
{
|
||||
DBUG_PRINT("error",("Couldn't allocate memory"));
|
||||
DBUG_RETURN(my_errno=ENOMEM);
|
||||
}
|
||||
DBUG_PRINT("info",("root_page: %ld", (long) old_root));
|
||||
if (!_ma_fetch_keypage(info,keyinfo,old_root,DFLT_INIT_HITS,root_buff,0))
|
||||
{
|
||||
error= -1;
|
||||
goto err;
|
||||
}
|
||||
if ((error=d_search(info,keyinfo,
|
||||
(keyinfo->flag & HA_FULLTEXT ? SEARCH_FIND | SEARCH_UPDATE
|
||||
: SEARCH_SAME),
|
||||
key,key_length,old_root,root_buff)) >0)
|
||||
{
|
||||
if (error == 2)
|
||||
{
|
||||
DBUG_PRINT("test",("Enlarging of root when deleting"));
|
||||
error= _ma_enlarge_root(info,keyinfo,key,root);
|
||||
}
|
||||
else /* error == 1 */
|
||||
{
|
||||
if (maria_getint(root_buff) <= (nod_flag=_ma_test_if_nod(root_buff))+3)
|
||||
{
|
||||
error=0;
|
||||
if (nod_flag)
|
||||
*root= _ma_kpos(nod_flag,root_buff+2+nod_flag);
|
||||
else
|
||||
*root=HA_OFFSET_ERROR;
|
||||
if (_ma_dispose(info,keyinfo,old_root,DFLT_INIT_HITS))
|
||||
error= -1;
|
||||
}
|
||||
else
|
||||
error= _ma_write_keypage(info,keyinfo,old_root,
|
||||
DFLT_INIT_HITS,root_buff);
|
||||
}
|
||||
}
|
||||
err:
|
||||
my_afree((gptr) root_buff);
|
||||
DBUG_PRINT("exit",("Return: %d",error));
|
||||
DBUG_RETURN(error);
|
||||
} /* _ma_ck_real_delete */
|
||||
|
||||
|
||||
/*
|
||||
** Remove key below key root
|
||||
** Return values:
|
||||
** 1 if there are less buffers; In this case anc_buff is not saved
|
||||
** 2 if there are more buffers
|
||||
** -1 on errors
|
||||
*/
|
||||
|
||||
static int d_search(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
||||
uint comp_flag, byte *key, uint key_length,
|
||||
my_off_t page, byte *anc_buff)
|
||||
{
|
||||
int flag,ret_value,save_flag;
|
||||
uint length,nod_flag,search_key_length;
|
||||
my_bool last_key;
|
||||
byte *leaf_buff,*keypos;
|
||||
my_off_t leaf_page,next_block;
|
||||
byte lastkey[HA_MAX_KEY_BUFF];
|
||||
DBUG_ENTER("d_search");
|
||||
DBUG_DUMP("page",anc_buff,maria_getint(anc_buff));
|
||||
|
||||
search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY;
|
||||
flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key, search_key_length,
|
||||
comp_flag, &keypos, lastkey, &last_key);
|
||||
if (flag == MARIA_FOUND_WRONG_KEY)
|
||||
{
|
||||
DBUG_PRINT("error",("Found wrong key"));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
nod_flag=_ma_test_if_nod(anc_buff);
|
||||
|
||||
if (!flag && keyinfo->flag & HA_FULLTEXT)
|
||||
{
|
||||
uint off;
|
||||
int subkeys;
|
||||
|
||||
get_key_full_length_rdonly(off, lastkey);
|
||||
subkeys=ft_sintXkorr(lastkey+off);
|
||||
DBUG_ASSERT(info->ft1_to_ft2==0 || subkeys >=0);
|
||||
comp_flag=SEARCH_SAME;
|
||||
if (subkeys >= 0)
|
||||
{
|
||||
/* normal word, one-level tree structure */
|
||||
if (info->ft1_to_ft2)
|
||||
{
|
||||
/* we're in ft1->ft2 conversion mode. Saving key data */
|
||||
insert_dynamic(info->ft1_to_ft2, (char*) (lastkey+off));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we need exact match only if not in ft1->ft2 conversion mode */
|
||||
flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key,USE_WHOLE_KEY,
|
||||
comp_flag, &keypos, lastkey, &last_key);
|
||||
}
|
||||
/* fall through to normal delete */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* popular word. two-level tree. going down */
|
||||
uint tmp_key_length;
|
||||
my_off_t root;
|
||||
byte *kpos=keypos;
|
||||
|
||||
if (!(tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,lastkey)))
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno= HA_ERR_CRASHED;
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
root= _ma_dpos(info,nod_flag,kpos);
|
||||
if (subkeys == -1)
|
||||
{
|
||||
/* the last entry in sub-tree */
|
||||
if (_ma_dispose(info, keyinfo, root,DFLT_INIT_HITS))
|
||||
DBUG_RETURN(-1);
|
||||
/* fall through to normal delete */
|
||||
}
|
||||
else
|
||||
{
|
||||
keyinfo=&info->s->ft2_keyinfo;
|
||||
kpos-=keyinfo->keylength+nod_flag; /* we'll modify key entry 'in vivo' */
|
||||
get_key_full_length_rdonly(off, key);
|
||||
key+=off;
|
||||
ret_value= _ma_ck_real_delete(info, &info->s->ft2_keyinfo,
|
||||
key, HA_FT_WLEN, &root);
|
||||
_ma_dpointer(info, kpos+HA_FT_WLEN, root);
|
||||
subkeys++;
|
||||
ft_intXstore(kpos, subkeys);
|
||||
if (!ret_value)
|
||||
ret_value= _ma_write_keypage(info,keyinfo,page,
|
||||
DFLT_INIT_HITS,anc_buff);
|
||||
DBUG_PRINT("exit",("Return: %d",ret_value));
|
||||
DBUG_RETURN(ret_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
leaf_buff=0;
|
||||
LINT_INIT(leaf_page);
|
||||
if (nod_flag)
|
||||
{
|
||||
leaf_page= _ma_kpos(nod_flag,keypos);
|
||||
if (!(leaf_buff= (byte*) my_alloca((uint) keyinfo->block_length+
|
||||
HA_MAX_KEY_BUFF*2)))
|
||||
{
|
||||
DBUG_PRINT("error",("Couldn't allocate memory"));
|
||||
my_errno=ENOMEM;
|
||||
DBUG_PRINT("exit",("Return: %d",-1));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
if (!_ma_fetch_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff,0))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (flag != 0)
|
||||
{
|
||||
if (!nod_flag)
|
||||
{
|
||||
DBUG_PRINT("error",("Didn't find key"));
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno=HA_ERR_CRASHED; /* This should newer happend */
|
||||
goto err;
|
||||
}
|
||||
save_flag=0;
|
||||
ret_value=d_search(info,keyinfo,comp_flag,key,key_length,
|
||||
leaf_page,leaf_buff);
|
||||
}
|
||||
else
|
||||
{ /* Found key */
|
||||
uint tmp;
|
||||
length=maria_getint(anc_buff);
|
||||
if (!(tmp= remove_key(keyinfo,nod_flag,keypos,lastkey,anc_buff+length,
|
||||
&next_block)))
|
||||
goto err;
|
||||
|
||||
length-= tmp;
|
||||
|
||||
maria_putint(anc_buff,length,nod_flag);
|
||||
if (!nod_flag)
|
||||
{ /* On leaf page */
|
||||
if (_ma_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,anc_buff))
|
||||
{
|
||||
DBUG_PRINT("exit",("Return: %d",-1));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
/* Page will be update later if we return 1 */
|
||||
DBUG_RETURN(test(length <= (info->quick_mode ? MARIA_MIN_KEYBLOCK_LENGTH :
|
||||
(uint) keyinfo->underflow_block_length)));
|
||||
}
|
||||
save_flag=1;
|
||||
ret_value=del(info,keyinfo,key,anc_buff,leaf_page,leaf_buff,keypos,
|
||||
next_block,lastkey);
|
||||
}
|
||||
if (ret_value >0)
|
||||
{
|
||||
save_flag=1;
|
||||
if (ret_value == 1)
|
||||
ret_value= underflow(info,keyinfo,anc_buff,leaf_page,leaf_buff,keypos);
|
||||
else
|
||||
{ /* This happens only with packed keys */
|
||||
DBUG_PRINT("test",("Enlarging of key when deleting"));
|
||||
if (!_ma_get_last_key(info,keyinfo,anc_buff,lastkey,keypos,&length))
|
||||
goto err;
|
||||
ret_value= _ma_insert(info,keyinfo,key,anc_buff,keypos,lastkey,
|
||||
(byte*) 0,(byte*) 0,(my_off_t) 0,(my_bool) 0);
|
||||
}
|
||||
}
|
||||
if (ret_value == 0 && maria_getint(anc_buff) > keyinfo->block_length)
|
||||
{
|
||||
save_flag=1;
|
||||
ret_value= _ma_split_page(info,keyinfo,key,anc_buff,lastkey,0) | 2;
|
||||
}
|
||||
if (save_flag && ret_value != 1)
|
||||
ret_value|= _ma_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,anc_buff);
|
||||
else
|
||||
{
|
||||
DBUG_DUMP("page",anc_buff,maria_getint(anc_buff));
|
||||
}
|
||||
my_afree(leaf_buff);
|
||||
DBUG_PRINT("exit",("Return: %d",ret_value));
|
||||
DBUG_RETURN(ret_value);
|
||||
|
||||
err:
|
||||
my_afree(leaf_buff);
|
||||
DBUG_PRINT("exit",("Error: %d",my_errno));
|
||||
DBUG_RETURN (-1);
|
||||
} /* d_search */
|
||||
|
||||
|
||||
/* Remove a key that has a page-reference */
|
||||
|
||||
static int del(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
||||
byte *key, byte *anc_buff, my_off_t leaf_page,
|
||||
byte *leaf_buff,
|
||||
byte *keypos, /* Pos to where deleted key was */
|
||||
my_off_t next_block,
|
||||
byte *ret_key) /* key before keypos in anc_buff */
|
||||
{
|
||||
int ret_value,length;
|
||||
uint a_length,nod_flag,tmp;
|
||||
my_off_t next_page;
|
||||
byte keybuff[HA_MAX_KEY_BUFF],*endpos,*next_buff,*key_start, *prev_key;
|
||||
MARIA_SHARE *share=info->s;
|
||||
MARIA_KEY_PARAM s_temp;
|
||||
DBUG_ENTER("del");
|
||||
DBUG_PRINT("enter",("leaf_page: %ld keypos: 0x%lx", (long) leaf_page,
|
||||
(ulong) keypos));
|
||||
DBUG_DUMP("leaf_buff",leaf_buff,maria_getint(leaf_buff));
|
||||
|
||||
endpos= leaf_buff+ maria_getint(leaf_buff);
|
||||
if (!(key_start= _ma_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
|
||||
&tmp)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if ((nod_flag=_ma_test_if_nod(leaf_buff)))
|
||||
{
|
||||
next_page= _ma_kpos(nod_flag,endpos);
|
||||
if (!(next_buff= (byte*) my_alloca((uint) keyinfo->block_length+
|
||||
HA_MAX_KEY_BUFF*2)))
|
||||
DBUG_RETURN(-1);
|
||||
if (!_ma_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,next_buff,0))
|
||||
ret_value= -1;
|
||||
else
|
||||
{
|
||||
DBUG_DUMP("next_page",next_buff,maria_getint(next_buff));
|
||||
if ((ret_value=del(info,keyinfo,key,anc_buff,next_page,next_buff,
|
||||
keypos,next_block,ret_key)) >0)
|
||||
{
|
||||
endpos=leaf_buff+maria_getint(leaf_buff);
|
||||
if (ret_value == 1)
|
||||
{
|
||||
ret_value=underflow(info,keyinfo,leaf_buff,next_page,
|
||||
next_buff,endpos);
|
||||
if (ret_value == 0 && maria_getint(leaf_buff) > keyinfo->block_length)
|
||||
{
|
||||
ret_value= _ma_split_page(info,keyinfo,key,leaf_buff,ret_key,0) | 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("test",("Inserting of key when deleting"));
|
||||
if (!_ma_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
|
||||
&tmp))
|
||||
goto err;
|
||||
ret_value= _ma_insert(info,keyinfo,key,leaf_buff,endpos,keybuff,
|
||||
(byte*) 0,(byte*) 0,(my_off_t) 0,0);
|
||||
}
|
||||
}
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
}
|
||||
my_afree(next_buff);
|
||||
DBUG_RETURN(ret_value);
|
||||
}
|
||||
|
||||
/* Remove last key from leaf page */
|
||||
|
||||
maria_putint(leaf_buff,key_start-leaf_buff,nod_flag);
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
|
||||
/* Place last key in ancestor page on deleted key position */
|
||||
|
||||
a_length=maria_getint(anc_buff);
|
||||
endpos=anc_buff+a_length;
|
||||
if (keypos != anc_buff+2+share->base.key_reflength &&
|
||||
!_ma_get_last_key(info,keyinfo,anc_buff,ret_key,keypos,&tmp))
|
||||
goto err;
|
||||
prev_key=(keypos == anc_buff+2+share->base.key_reflength ?
|
||||
0 : ret_key);
|
||||
length=(*keyinfo->pack_key)(keyinfo,share->base.key_reflength,
|
||||
keypos == endpos ? (byte*) 0 : keypos,
|
||||
prev_key, prev_key,
|
||||
keybuff,&s_temp);
|
||||
if (length > 0)
|
||||
bmove_upp(endpos+length,endpos,(uint) (endpos-keypos));
|
||||
else
|
||||
bmove(keypos,keypos-length, (int) (endpos-keypos)+length);
|
||||
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
||||
/* Save pointer to next leaf */
|
||||
if (!(*keyinfo->get_key)(keyinfo,share->base.key_reflength,&keypos,ret_key))
|
||||
goto err;
|
||||
_ma_kpointer(info,keypos - share->base.key_reflength,next_block);
|
||||
maria_putint(anc_buff,a_length+length,share->base.key_reflength);
|
||||
|
||||
DBUG_RETURN( maria_getint(leaf_buff) <=
|
||||
(info->quick_mode ? MARIA_MIN_KEYBLOCK_LENGTH :
|
||||
(uint) keyinfo->underflow_block_length));
|
||||
err:
|
||||
DBUG_RETURN(-1);
|
||||
} /* del */
|
||||
|
||||
|
||||
/* Balances adjacent pages if underflow occours */
|
||||
|
||||
static int underflow(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
||||
byte *anc_buff,
|
||||
my_off_t leaf_page,/* Ancestor page and underflow page */
|
||||
byte *leaf_buff,
|
||||
byte *keypos) /* Position to pos after key */
|
||||
{
|
||||
int t_length;
|
||||
uint length,anc_length,buff_length,leaf_length,p_length,s_length,nod_flag,
|
||||
key_reflength,key_length;
|
||||
my_off_t next_page;
|
||||
byte anc_key[HA_MAX_KEY_BUFF],leaf_key[HA_MAX_KEY_BUFF];
|
||||
byte *buff,*endpos,*next_keypos,*anc_pos,*half_pos,*temp_pos,*prev_key;
|
||||
byte *after_key;
|
||||
MARIA_KEY_PARAM s_temp;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("underflow");
|
||||
DBUG_PRINT("enter",("leaf_page: %ld keypos: 0x%lx",(long) leaf_page,
|
||||
(ulong) keypos));
|
||||
DBUG_DUMP("anc_buff",anc_buff,maria_getint(anc_buff));
|
||||
DBUG_DUMP("leaf_buff",leaf_buff,maria_getint(leaf_buff));
|
||||
|
||||
buff=info->buff;
|
||||
info->keybuff_used=1;
|
||||
next_keypos=keypos;
|
||||
nod_flag=_ma_test_if_nod(leaf_buff);
|
||||
p_length=nod_flag+2;
|
||||
anc_length=maria_getint(anc_buff);
|
||||
leaf_length=maria_getint(leaf_buff);
|
||||
key_reflength=share->base.key_reflength;
|
||||
if (info->s->keyinfo+info->lastinx == keyinfo)
|
||||
info->page_changed=1;
|
||||
|
||||
if ((keypos < anc_buff+anc_length && (info->state->records & 1)) ||
|
||||
keypos == anc_buff+2+key_reflength)
|
||||
{ /* Use page right of anc-page */
|
||||
DBUG_PRINT("test",("use right page"));
|
||||
|
||||
if (keyinfo->flag & HA_BINARY_PACK_KEY)
|
||||
{
|
||||
if (!(next_keypos= _ma_get_key(info, keyinfo,
|
||||
anc_buff, buff, keypos, &length)))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Got to end of found key */
|
||||
buff[0]=buff[1]=0; /* Avoid length error check if packed key */
|
||||
if (!(*keyinfo->get_key)(keyinfo,key_reflength,&next_keypos,
|
||||
buff))
|
||||
goto err;
|
||||
}
|
||||
next_page= _ma_kpos(key_reflength,next_keypos);
|
||||
if (!_ma_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
|
||||
goto err;
|
||||
buff_length=maria_getint(buff);
|
||||
DBUG_DUMP("next",buff,buff_length);
|
||||
|
||||
/* find keys to make a big key-page */
|
||||
bmove(next_keypos-key_reflength, buff+2, key_reflength);
|
||||
if (!_ma_get_last_key(info,keyinfo,anc_buff,anc_key,next_keypos,&length)
|
||||
|| !_ma_get_last_key(info,keyinfo,leaf_buff,leaf_key,
|
||||
leaf_buff+leaf_length,&length))
|
||||
goto err;
|
||||
|
||||
/* merge pages and put parting key from anc_buff between */
|
||||
prev_key=(leaf_length == p_length ? (byte*) 0 : leaf_key);
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,buff+p_length,
|
||||
prev_key, prev_key,
|
||||
anc_key, &s_temp);
|
||||
length=buff_length-p_length;
|
||||
endpos=buff+length+leaf_length+t_length;
|
||||
/* buff will always be larger than before !*/
|
||||
bmove_upp(endpos, buff+buff_length,length);
|
||||
memcpy(buff, leaf_buff,(size_t) leaf_length);
|
||||
(*keyinfo->store_key)(keyinfo,buff+leaf_length,&s_temp);
|
||||
buff_length=(uint) (endpos-buff);
|
||||
maria_putint(buff,buff_length,nod_flag);
|
||||
|
||||
/* remove key from anc_buff */
|
||||
|
||||
if (!(s_length=remove_key(keyinfo,key_reflength,keypos,anc_key,
|
||||
anc_buff+anc_length,(my_off_t *) 0)))
|
||||
goto err;
|
||||
|
||||
anc_length-=s_length;
|
||||
maria_putint(anc_buff,anc_length,key_reflength);
|
||||
|
||||
if (buff_length <= keyinfo->block_length)
|
||||
{ /* Keys in one page */
|
||||
memcpy(leaf_buff,buff,(size_t) buff_length);
|
||||
if (_ma_dispose(info,keyinfo,next_page,DFLT_INIT_HITS))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{ /* Page is full */
|
||||
endpos=anc_buff+anc_length;
|
||||
DBUG_PRINT("test",("anc_buff: 0x%lx endpos: 0x%lx",
|
||||
(long) anc_buff, (long) endpos));
|
||||
if (keypos != anc_buff+2+key_reflength &&
|
||||
!_ma_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length))
|
||||
goto err;
|
||||
if (!(half_pos= _ma_find_half_pos(nod_flag, keyinfo, buff, leaf_key,
|
||||
&key_length, &after_key)))
|
||||
goto err;
|
||||
length=(uint) (half_pos-buff);
|
||||
memcpy(leaf_buff,buff,(size_t) length);
|
||||
maria_putint(leaf_buff,length,nod_flag);
|
||||
|
||||
/* Correct new keypointer to leaf_page */
|
||||
half_pos=after_key;
|
||||
_ma_kpointer(info,leaf_key+key_length,next_page);
|
||||
/* Save key in anc_buff */
|
||||
prev_key=(keypos == anc_buff+2+key_reflength ? (byte*) 0 : anc_key),
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,key_reflength,
|
||||
(keypos == endpos ? (byte*) 0 :
|
||||
keypos),
|
||||
prev_key, prev_key,
|
||||
leaf_key, &s_temp);
|
||||
if (t_length >= 0)
|
||||
bmove_upp(endpos+t_length, endpos, (uint) (endpos-keypos));
|
||||
else
|
||||
bmove(keypos,keypos-t_length,(uint) (endpos-keypos)+t_length);
|
||||
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
||||
maria_putint(anc_buff,(anc_length+=t_length),key_reflength);
|
||||
|
||||
/* Store key first in new page */
|
||||
if (nod_flag)
|
||||
bmove(buff+2,half_pos-nod_flag,(size_t) nod_flag);
|
||||
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&half_pos,leaf_key))
|
||||
goto err;
|
||||
t_length=(int) (*keyinfo->pack_key)(keyinfo, nod_flag, (byte*) 0,
|
||||
(byte*) 0, (byte*) 0,
|
||||
leaf_key, &s_temp);
|
||||
/* t_length will always be > 0 for a new page !*/
|
||||
length=(uint) ((buff+maria_getint(buff))-half_pos);
|
||||
bmove(buff+p_length+t_length, half_pos, (size_t) length);
|
||||
(*keyinfo->store_key)(keyinfo,buff+p_length,&s_temp);
|
||||
maria_putint(buff,length+t_length+p_length,nod_flag);
|
||||
|
||||
if (_ma_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff))
|
||||
goto err;
|
||||
}
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
DBUG_RETURN(anc_length <= ((info->quick_mode ? MARIA_MIN_BLOCK_LENGTH :
|
||||
(uint) keyinfo->underflow_block_length)));
|
||||
}
|
||||
|
||||
DBUG_PRINT("test",("use left page"));
|
||||
|
||||
keypos= _ma_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length);
|
||||
if (!keypos)
|
||||
goto err;
|
||||
next_page= _ma_kpos(key_reflength,keypos);
|
||||
if (!_ma_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
|
||||
goto err;
|
||||
buff_length=maria_getint(buff);
|
||||
endpos=buff+buff_length;
|
||||
DBUG_DUMP("prev",buff,buff_length);
|
||||
|
||||
/* find keys to make a big key-page */
|
||||
bmove(next_keypos - key_reflength, leaf_buff+2, key_reflength);
|
||||
next_keypos=keypos;
|
||||
if (!(*keyinfo->get_key)(keyinfo,key_reflength,&next_keypos,
|
||||
anc_key))
|
||||
goto err;
|
||||
if (!_ma_get_last_key(info,keyinfo,buff,leaf_key,endpos,&length))
|
||||
goto err;
|
||||
|
||||
/* merge pages and put parting key from anc_buff between */
|
||||
prev_key=(leaf_length == p_length ? (byte*) 0 : leaf_key);
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
|
||||
(leaf_length == p_length ?
|
||||
(byte*) 0 : leaf_buff+p_length),
|
||||
prev_key, prev_key,
|
||||
anc_key, &s_temp);
|
||||
if (t_length >= 0)
|
||||
bmove(endpos+t_length, leaf_buff+p_length,
|
||||
(size_t) (leaf_length-p_length));
|
||||
else /* We gained space */
|
||||
bmove(endpos,leaf_buff+((int) p_length-t_length),
|
||||
(size_t) (leaf_length-p_length+t_length));
|
||||
|
||||
(*keyinfo->store_key)(keyinfo,endpos,&s_temp);
|
||||
buff_length=buff_length+leaf_length-p_length+t_length;
|
||||
maria_putint(buff,buff_length,nod_flag);
|
||||
|
||||
/* remove key from anc_buff */
|
||||
if (!(s_length= remove_key(keyinfo,key_reflength,keypos,anc_key,
|
||||
anc_buff+anc_length,(my_off_t *) 0)))
|
||||
goto err;
|
||||
|
||||
anc_length-=s_length;
|
||||
maria_putint(anc_buff,anc_length,key_reflength);
|
||||
|
||||
if (buff_length <= keyinfo->block_length)
|
||||
{ /* Keys in one page */
|
||||
if (_ma_dispose(info,keyinfo,leaf_page,DFLT_INIT_HITS))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{ /* Page is full */
|
||||
if (keypos == anc_buff+2+key_reflength)
|
||||
anc_pos=0; /* First key */
|
||||
else if (!_ma_get_last_key(info,keyinfo,anc_buff,anc_pos=anc_key,keypos,
|
||||
&length))
|
||||
goto err;
|
||||
endpos= _ma_find_half_pos(nod_flag,keyinfo,buff,leaf_key,
|
||||
&key_length, &half_pos);
|
||||
if (!endpos)
|
||||
goto err;
|
||||
_ma_kpointer(info,leaf_key+key_length,leaf_page);
|
||||
/* Save key in anc_buff */
|
||||
DBUG_DUMP("anc_buff",anc_buff,anc_length);
|
||||
DBUG_DUMP("key_to_anc",leaf_key,key_length);
|
||||
|
||||
temp_pos=anc_buff+anc_length;
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,key_reflength,
|
||||
keypos == temp_pos ? (byte*) 0
|
||||
: keypos,
|
||||
anc_pos, anc_pos,
|
||||
leaf_key,&s_temp);
|
||||
if (t_length > 0)
|
||||
bmove_upp(temp_pos+t_length, temp_pos, (uint) (temp_pos-keypos));
|
||||
else
|
||||
bmove(keypos,keypos-t_length,(uint) (temp_pos-keypos)+t_length);
|
||||
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
||||
maria_putint(anc_buff,(anc_length+=t_length),key_reflength);
|
||||
|
||||
/* Store first key on new page */
|
||||
if (nod_flag)
|
||||
bmove(leaf_buff+2,half_pos-nod_flag,(size_t) nod_flag);
|
||||
if (!(length=(*keyinfo->get_key)(keyinfo,nod_flag,&half_pos,leaf_key)))
|
||||
goto err;
|
||||
DBUG_DUMP("key_to_leaf",leaf_key,length);
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag, (byte*) 0,
|
||||
(byte*) 0, (byte*) 0, leaf_key, &s_temp);
|
||||
length=(uint) ((buff+buff_length)-half_pos);
|
||||
DBUG_PRINT("info",("t_length: %d length: %d",t_length,(int) length));
|
||||
bmove(leaf_buff+p_length+t_length,half_pos,
|
||||
(size_t) length);
|
||||
(*keyinfo->store_key)(keyinfo,leaf_buff+p_length,&s_temp);
|
||||
maria_putint(leaf_buff,length+t_length+p_length,nod_flag);
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
maria_putint(buff,endpos-buff,nod_flag);
|
||||
}
|
||||
if (_ma_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff))
|
||||
goto err;
|
||||
DBUG_RETURN(anc_length <= (uint) keyinfo->block_length/2);
|
||||
|
||||
err:
|
||||
DBUG_RETURN(-1);
|
||||
} /* underflow */
|
||||
|
||||
|
||||
/*
|
||||
remove a key from packed buffert
|
||||
The current code doesn't handle the case that the next key may be
|
||||
packed better against the previous key if there is a case difference
|
||||
returns how many chars was removed or 0 on error
|
||||
*/
|
||||
|
||||
static uint remove_key(MARIA_KEYDEF *keyinfo, uint nod_flag,
|
||||
byte *keypos, /* Where key starts */
|
||||
byte *lastkey, /* key to be removed */
|
||||
byte *page_end, /* End of page */
|
||||
my_off_t *next_block) /* ptr to next block */
|
||||
{
|
||||
int s_length;
|
||||
byte *start;
|
||||
DBUG_ENTER("remove_key");
|
||||
DBUG_PRINT("enter",("keypos: 0x%lx page_end: 0x%lx",(long) keypos, (long) page_end));
|
||||
|
||||
start=keypos;
|
||||
if (!(keyinfo->flag &
|
||||
(HA_PACK_KEY | HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY |
|
||||
HA_BINARY_PACK_KEY)))
|
||||
{
|
||||
s_length=(int) (keyinfo->keylength+nod_flag);
|
||||
if (next_block && nod_flag)
|
||||
*next_block= _ma_kpos(nod_flag,keypos+s_length);
|
||||
}
|
||||
else
|
||||
{ /* Let keypos point at next key */
|
||||
/* Calculate length of key */
|
||||
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey))
|
||||
DBUG_RETURN(0); /* Error */
|
||||
|
||||
if (next_block && nod_flag)
|
||||
*next_block= _ma_kpos(nod_flag,keypos);
|
||||
s_length=(int) (keypos-start);
|
||||
if (keypos != page_end)
|
||||
{
|
||||
if (keyinfo->flag & HA_BINARY_PACK_KEY)
|
||||
{
|
||||
byte *old_key=start;
|
||||
uint next_length,prev_length,prev_pack_length;
|
||||
get_key_length(next_length,keypos);
|
||||
get_key_pack_length(prev_length,prev_pack_length,old_key);
|
||||
if (next_length > prev_length)
|
||||
{
|
||||
/* We have to copy data from the current key to the next key */
|
||||
bmove_upp((char*) keypos,(char*) (lastkey+next_length),
|
||||
(next_length-prev_length));
|
||||
keypos-=(next_length-prev_length)+prev_pack_length;
|
||||
store_key_length(keypos,prev_length);
|
||||
s_length=(int) (keypos-start);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check if a variable length first key part */
|
||||
if ((keyinfo->seg->flag & HA_PACK_KEY) && *keypos & 128)
|
||||
{
|
||||
/* Next key is packed against the current one */
|
||||
uint next_length,prev_length,prev_pack_length,lastkey_length,
|
||||
rest_length;
|
||||
if (keyinfo->seg[0].length >= 127)
|
||||
{
|
||||
if (!(prev_length=mi_uint2korr(start) & 32767))
|
||||
goto end;
|
||||
next_length=mi_uint2korr(keypos) & 32767;
|
||||
keypos+=2;
|
||||
prev_pack_length=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(prev_length= *start & 127))
|
||||
goto end; /* Same key as previous*/
|
||||
next_length= *keypos & 127;
|
||||
keypos++;
|
||||
prev_pack_length=1;
|
||||
}
|
||||
if (!(*start & 128))
|
||||
prev_length=0; /* prev key not packed */
|
||||
if (keyinfo->seg[0].flag & HA_NULL_PART)
|
||||
lastkey++; /* Skip null marker */
|
||||
get_key_length(lastkey_length,lastkey);
|
||||
if (!next_length) /* Same key after */
|
||||
{
|
||||
next_length=lastkey_length;
|
||||
rest_length=0;
|
||||
}
|
||||
else
|
||||
get_key_length(rest_length,keypos);
|
||||
|
||||
if (next_length >= prev_length)
|
||||
{ /* Key after is based on deleted key */
|
||||
uint pack_length,tmp;
|
||||
bmove_upp((char*) keypos,(char*) (lastkey+next_length),
|
||||
tmp=(next_length-prev_length));
|
||||
rest_length+=tmp;
|
||||
pack_length= prev_length ? get_pack_length(rest_length): 0;
|
||||
keypos-=tmp+pack_length+prev_pack_length;
|
||||
s_length=(int) (keypos-start);
|
||||
if (prev_length) /* Pack against prev key */
|
||||
{
|
||||
*keypos++= start[0];
|
||||
if (prev_pack_length == 2)
|
||||
*keypos++= start[1];
|
||||
store_key_length(keypos,rest_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Next key is not packed anymore */
|
||||
if (keyinfo->seg[0].flag & HA_NULL_PART)
|
||||
{
|
||||
rest_length++; /* Mark not null */
|
||||
}
|
||||
if (prev_pack_length == 2)
|
||||
{
|
||||
mi_int2store(keypos,rest_length);
|
||||
}
|
||||
else
|
||||
*keypos= rest_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end:
|
||||
bmove(start, start+s_length, (uint) (page_end-start-s_length));
|
||||
DBUG_RETURN((uint) s_length);
|
||||
} /* remove_key */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user