mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
Merge jamppa@bk-internal.mysql.com:/home/bk/mysql-5.1
into hynda.mysql.fi:/home/my/mysql-maria BitKeeper/etc/ignore: auto-union Makefile.am: Auto merged BUILD/SETUP.sh: Auto merged BitKeeper/deleted/.del-init_db.sql~a77d572c39d5a1f8: Auto merged client/mysqldump.c: Auto merged include/Makefile.am: Auto merged include/m_string.h: Auto merged include/my_base.h: Auto merged include/my_dbug.h: Auto merged libmysql/CMakeLists.txt: Auto merged libmysql/Makefile.shared: Auto merged libmysqld/Makefile.am: Auto merged mysql-test/include/varchar.inc: Auto merged mysql-test/lib/mtr_cases.pl: Auto merged mysql-test/lib/mtr_io.pl: Auto merged mysql-test/lib/mtr_misc.pl: Auto merged mysql-test/mysql-test-run.pl: Auto merged mysql-test/lib/mtr_process.pl: Auto merged mysql-test/lib/mtr_report.pl: Auto merged mysql-test/r/events_logs_tests.result: Auto merged mysql-test/r/view.result: Auto merged mysql-test/t/disabled.def: Auto merged mysql-test/t/events_logs_tests.test: Auto merged mysql-test/t/myisam.test: Auto merged mysql-test/t/view.test: Auto merged mysys/Makefile.am: Auto merged mysys/my_create.c: Auto merged mysys/my_handler.c: Auto merged mysys/my_init.c: Auto merged mysys/my_open.c: Auto merged mysys/safemalloc.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.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/opt_range.cc: Auto merged sql/slave.cc: Auto merged sql/slave.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_select.cc: Auto merged sql/share/errmsg.txt: Auto merged sql/sql_table.cc: Auto merged sql/sql_test.cc: Auto merged sql/udf_example.c: Auto merged sql/uniques.cc: Auto merged sql/unireg.cc: Auto merged storage/csv/ha_tina.cc: 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_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_checksum.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_key.c: Auto merged storage/myisam/mi_log.c: Auto merged storage/myisam/mi_open.c: Auto merged storage/myisam/mi_range.c: Auto merged storage/myisam/mi_rkey.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/myisamlog.c: Auto merged storage/myisam/myisampack.c: Auto merged storage/myisam/rt_index.c: Auto merged storage/myisam/sort.c: Auto merged storage/myisam/sp_test.c: Auto merged storage/myisammrg/ha_myisammrg.h: Auto merged storage/ndb/src/mgmapi/mgmapi.cpp: Auto merged unittest/Makefile.am: Auto merged BitKeeper/triggers/post-commit: Manual merge from mysql-5.1 to mysql-maria configure.in: Manual merge from mysql-5.1 to mysql-maria include/ft_global.h: Manual merge from mysql-5.1 to mysql-maria include/keycache.h: Manual merge from mysql-5.1 to mysql-maria include/my_atomic.h: Manual merge from mysql-5.1 to mysql-maria include/my_global.h: Manual merge from mysql-5.1 to mysql-maria include/my_sys.h: Manual merge from mysql-5.1 to mysql-maria include/myisam.h: Manual merge from mysql-5.1 to mysql-maria mysys/array.c: Manual merge from mysql-5.1 to mysql-maria mysys/mf_keycache.c: Manual merge from mysql-5.1 to mysql-maria mysys/mf_keycaches.c: Manual merge from mysql-5.1 to mysql-maria mysys/my_pread.c: Manual merge from mysql-5.1 to mysql-maria sql/mysqld.cc: Manual merge from mysql-5.1 to mysql-maria sql/net_serv.cc: Manual merge from mysql-5.1 to mysql-maria sql/set_var.cc: Manual merge from mysql-5.1 to mysql-maria sql/set_var.h: Manual merge from mysql-5.1 to mysql-maria sql/sql_class.h: Manual merge from mysql-5.1 to mysql-maria storage/myisam/ft_static.c: Manual merge from mysql-5.1 to mysql-maria storage/myisam/ha_myisam.cc: Manual merge from mysql-5.1 to mysql-maria storage/myisam/mi_check.c: Manual merge from mysql-5.1 to mysql-maria storage/myisam/mi_dynrec.c: Manual merge from mysql-5.1 to mysql-maria storage/myisam/mi_packrec.c: Manual merge from mysql-5.1 to mysql-maria storage/myisam/mi_write.c: Manual merge from mysql-5.1 to mysql-maria storage/myisam/myisamchk.c: Manual merge from mysql-5.1 to mysql-maria storage/myisam/myisamdef.h: Manual merge from mysql-5.1 to mysql-maria storage/myisammrg/ha_myisammrg.cc: Manual merge from mysql-5.1 to mysql-maria unittest/mysys/Makefile.am: Manual merge from mysql-5.1 to mysql-maria unittest/mysys/my_atomic-t.c: Manual merge from mysql-5.1 to mysql-maria
This commit is contained in:
commit
5444b55cbb
253 changed files with 76960 additions and 1613 deletions
53
.bzrignore
53
.bzrignore
|
@ -1,6 +1,7 @@
|
|||
*-t
|
||||
*.Plo
|
||||
*.Po
|
||||
*.Tpo
|
||||
*.a
|
||||
*.bb
|
||||
*.bbg
|
||||
|
@ -38,9 +39,9 @@
|
|||
*/.libs/*
|
||||
*/.pure
|
||||
*/debug/*
|
||||
*/minsizerel/*
|
||||
*/release/*
|
||||
*/relwithdebinfo/*
|
||||
*/minsizerel/*
|
||||
*~
|
||||
.*.swp
|
||||
./CMakeCache.txt
|
||||
|
@ -1021,8 +1022,8 @@ libmysqld/.deps/unireg.Po
|
|||
libmysqld/backup_dir
|
||||
libmysqld/client.c
|
||||
libmysqld/client_settings.h
|
||||
libmysqld/convert.cc
|
||||
libmysqld/cmake_dummy.c
|
||||
libmysqld/convert.cc
|
||||
libmysqld/derror.cc
|
||||
libmysqld/discover.cc
|
||||
libmysqld/emb_qcache.cpp
|
||||
|
@ -1072,6 +1073,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
|
||||
|
@ -2414,6 +2416,41 @@ 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_control
|
||||
storage/maria/maria_ftdump
|
||||
storage/maria/maria_log
|
||||
storage/maria/maria_log.*
|
||||
storage/maria/maria_pack
|
||||
storage/maria/unittest/ma_pagecache_consist_1k-t-big
|
||||
storage/maria/unittest/ma_pagecache_consist_1kHC-t-big
|
||||
storage/maria/unittest/ma_pagecache_consist_1kRD-t-big
|
||||
storage/maria/unittest/ma_pagecache_consist_1kWR-t-big
|
||||
storage/maria/unittest/ma_pagecache_consist_64k-t-big
|
||||
storage/maria/unittest/ma_pagecache_consist_64kHC-t-big
|
||||
storage/maria/unittest/ma_pagecache_consist_64kRD-t-big
|
||||
storage/maria/unittest/ma_pagecache_consist_64kWR-t-big
|
||||
storage/maria/unittest/ma_pagecache_single_64k-t-big
|
||||
storage/maria/unittest/ma_test_loghandler_long-t-big
|
||||
storage/maria/unittest/maria_control
|
||||
storage/maria/unittest/mf_pagecache_consist_1k-t-big
|
||||
storage/maria/unittest/mf_pagecache_consist_1kHC-t-big
|
||||
storage/maria/unittest/mf_pagecache_consist_1kRD-t-big
|
||||
storage/maria/unittest/mf_pagecache_consist_1kWR-t-big
|
||||
storage/maria/unittest/mf_pagecache_consist_64k-t-big
|
||||
storage/maria/unittest/mf_pagecache_consist_64kHC-t-big
|
||||
storage/maria/unittest/mf_pagecache_consist_64kRD-t-big
|
||||
storage/maria/unittest/mf_pagecache_consist_64kWR-t-big
|
||||
storage/maria/unittest/mf_pagecache_single_64k-t-big
|
||||
storage/myisam/.deps/ft_boolean_search.Po
|
||||
storage/myisam/.deps/ft_nlq_search.Po
|
||||
storage/myisam/.deps/ft_parser.Po
|
||||
|
@ -2964,13 +3001,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?
|
||||
|
|
0
.tree-is-private
Normal file
0
.tree-is-private
Normal file
|
@ -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
|
||||
|
|
|
@ -93,7 +93,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
|
||||
|
||||
|
|
|
@ -2464,8 +2464,6 @@ AC_SUBST(readline_basedir)
|
|||
AC_SUBST(readline_link)
|
||||
AC_SUBST(readline_h_ln_cmd)
|
||||
|
||||
|
||||
|
||||
# Include man pages, if desired, adapted to the configured parts.
|
||||
if test X"$with_man" = Xyes
|
||||
then
|
||||
|
|
|
@ -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 my_attribute.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 \
|
||||
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 wqueue.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, uchar *, uint,CHARSET_INFO *, uchar *);
|
||||
my_bool ft_boolean_check_syntax_string(const uchar *);
|
||||
|
||||
/* 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
|
||||
|
|
|
@ -132,7 +132,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(uchar *key, uint length);
|
||||
extern KEY_CACHE *multi_key_cache_search(uchar *key, uint length,
|
||||
KEY_CACHE *def);
|
||||
extern my_bool multi_key_cache_set(const uchar *key, uint length,
|
||||
KEY_CACHE *key_cache);
|
||||
extern void multi_key_cache_change(KEY_CACHE *old_data,
|
||||
|
|
260
include/lf.h
Normal file
260
include/lf.h
Normal file
|
@ -0,0 +1,260 @@
|
|||
/* Copyright (C) 2007 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 *, void*);
|
||||
|
||||
typedef struct {
|
||||
LF_DYNARRAY pinarray;
|
||||
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_array; /* number of elements in array */
|
||||
} LF_PINBOX;
|
||||
|
||||
typedef struct {
|
||||
void * volatile pin[LF_PINBOX_PINS];
|
||||
LF_PINBOX *pinbox;
|
||||
void *stack_ends_here;
|
||||
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*)
|
||||
-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->pinarray.lock)
|
||||
#define lf_rwunlock_by_pins(PINS) \
|
||||
my_atomic_rwlock_wrunlock(&(PINS)->pinbox->pinarray.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, void *stack_end),
|
||||
(pinbox, stack_end),
|
||||
&pinbox->pinarray.lock);
|
||||
lock_wrap_void(lf_pinbox_put_pins,
|
||||
(LF_PINS *pins),
|
||||
(pins),
|
||||
&pins->pinbox->pinarray.lock);
|
||||
lock_wrap_void(lf_pinbox_free,
|
||||
(LF_PINS *pins, void *addr),
|
||||
(pins, addr),
|
||||
&pins->pinbox->pinarray.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_pool_count(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(A, ST) _lf_pinbox_get_pins(&(A)->pinbox, (ST))
|
||||
#define lf_alloc_get_pins(A, ST) lf_pinbox_get_pins(&(A)->pinbox, (ST))
|
||||
#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_direct_free(ALLOC, ADDR) my_free((gptr)(ADDR), MYF(0))
|
||||
|
||||
lock_wrap(lf_alloc_new, void *,
|
||||
(LF_PINS *pins),
|
||||
(pins),
|
||||
&pins->pinbox->pinarray.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, ST) _lf_alloc_get_pins(&(HASH)->alloc, (ST))
|
||||
#define lf_hash_get_pins(HASH, ST) lf_alloc_get_pins(&(HASH)->alloc, (ST))
|
||||
#define _lf_hash_put_pins(PINS) _lf_pinbox_put_pins(PINS)
|
||||
#define lf_hash_put_pins(PINS) lf_pinbox_put_pins(PINS)
|
||||
#define lf_hash_search_unpin(PINS) lf_unpin((PINS), 2)
|
||||
/*
|
||||
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)
|
||||
|
@ -129,7 +129,10 @@ extern size_t bcmp(const uchar *s1,const uchar *s2,size_t len);
|
|||
extern size_t my_bcmp(const uchar *s1,const uchar *s2,size_t 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(uchar *dst,const uchar *src,size_t len);
|
||||
|
|
442
include/maria.h
Normal file
442
include/maria.h
Normal file
|
@ -0,0 +1,442 @@
|
|||
/* 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
|
||||
#include "../storage/maria/ma_pagecache.h"
|
||||
#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
|
||||
/* Minimal page cache when we only want to be able to scan a table */
|
||||
#define MARIA_MIN_PAGE_CACHE_SIZE 65536
|
||||
|
||||
/*
|
||||
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;
|
||||
ulonglong auto_increment;
|
||||
ulonglong key_map; /* Which keys are used */
|
||||
time_t create_time; /* When table was created */
|
||||
time_t check_time;
|
||||
time_t update_time;
|
||||
ulong record_offset;
|
||||
ulong *rec_per_key; /* for sql optimizing */
|
||||
ulong reclength; /* Recordlength */
|
||||
ulong mean_reclength; /* Mean recordlength (if packed) */
|
||||
char *data_file_name, *index_file_name;
|
||||
enum data_file_type data_file_type;
|
||||
uint keys; /* Number of keys in use */
|
||||
uint options; /* HA_OPTION_... used */
|
||||
uint reflength;
|
||||
int errkey, /* With key was dupplicated on err */
|
||||
sortkey; /* clustered by this key */
|
||||
File filenr; /* (uniq) filenr for datafile */
|
||||
} 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;
|
||||
/* Size of null bitmap at start of row */
|
||||
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 */
|
||||
/* Intern variable (size of total storage area for the row) */
|
||||
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 */
|
||||
/* Intern. Set if column should be zero packed (part of empty_bits) */
|
||||
uint8 empty_bit;
|
||||
|
||||
#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;
|
||||
extern my_off_t maria_max_temp_length;
|
||||
extern ulong maria_bulk_insert_tree_size, maria_data_pointer_size;
|
||||
extern PAGECACHE maria_pagecache_var, *maria_pagecache;
|
||||
|
||||
|
||||
/* 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 struct st_maria_info *maria_clone(struct st_maria_share *share, int mode);
|
||||
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_pagecache(MARIA_HA *info, ulonglong key_map,
|
||||
PAGECACHE *key_cache);
|
||||
void maria_change_pagecache(PAGECACHE *old_key_cache,
|
||||
PAGECACHE *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,28 +191,49 @@ 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
|
||||
|
||||
#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
|
||||
|
||||
/*
|
||||
the macro below defines (as an expression) the code that
|
||||
will be run in spin-loops. Intel manuals recummend to have PAUSE there.
|
||||
It is expected to be defined in include/atomic/ *.h files
|
||||
*/
|
||||
#ifndef LF_BACKOFF
|
||||
#define LF_BACKOFF (1)
|
||||
#endif
|
||||
|
||||
#define MY_ATOMIC_OK 0
|
||||
#define MY_ATOMIC_NOT_1CPU 1
|
||||
extern int my_atomic_initialize();
|
||||
|
|
|
@ -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
|
||||
|
@ -461,7 +460,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 */
|
||||
|
@ -513,4 +512,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
|
||||
|
|
|
@ -231,6 +231,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
|
||||
|
@ -1001,6 +1003,8 @@ typedef long long intptr;
|
|||
#error sizeof(void *) is neither sizeof(int) nor sizeof(long) nor sizeof(long long)
|
||||
#endif
|
||||
|
||||
#define MY_ERRPTR ((void*)(intptr)1)
|
||||
|
||||
#ifdef USE_RAID
|
||||
/*
|
||||
The following is done with a if to not get problems with pre-processors
|
||||
|
@ -1462,6 +1466,7 @@ do { doubleget_union _tmp; \
|
|||
#define dlerror() ""
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __NETWARE__
|
||||
/*
|
||||
* Include standard definitions of operator new and delete.
|
||||
|
@ -1487,5 +1492,13 @@ inline void operator delete[](void*, void*) { /* Do nothing */ }
|
|||
|
||||
/* Length of decimal number represented by INT64. */
|
||||
#define MY_INT64_NUM_DECIMAL_DIGITS 21
|
||||
|
||||
/*
|
||||
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,
|
||||
|
@ -89,4 +111,11 @@ extern int ha_key_cmp(register HA_KEYSEG *keyseg, register uchar *a,
|
|||
|
||||
extern HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, uchar *a);
|
||||
|
||||
/*
|
||||
Inside an in-memory data record, memory pointers to pieces of the
|
||||
record (like BLOBs) are stored in their native byte order and in
|
||||
this amount of bytes.
|
||||
*/
|
||||
#define portable_sizeof_char_ptr 8
|
||||
|
||||
#endif /* _my_handler_h */
|
||||
|
|
|
@ -51,6 +51,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() */
|
||||
|
@ -212,6 +213,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;
|
||||
|
@ -275,7 +277,12 @@ enum cache_type
|
|||
|
||||
enum flush_type
|
||||
{
|
||||
FLUSH_KEEP, FLUSH_RELEASE, FLUSH_IGNORE_CHANGED, FLUSH_FORCE_WRITE
|
||||
FLUSH_KEEP, /* flush block and keep it in the cache */
|
||||
FLUSH_RELEASE, /* flush block and remove it from the cache */
|
||||
FLUSH_IGNORE_CHANGED, /* remove block from the cache */
|
||||
/* as my_disable_flush_pagecache_blocks is always 0, it is
|
||||
strictly equivalent to FLUSH_KEEP */
|
||||
FLUSH_FORCE_WRITE
|
||||
};
|
||||
|
||||
typedef struct st_record_cache /* Used when cacheing records */
|
||||
|
@ -626,6 +633,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, ...))
|
||||
|
@ -660,7 +669,7 @@ extern char *my_tmpdir(MY_TMPDIR *tmpdir);
|
|||
extern void free_tmpdir(MY_TMPDIR *tmpdir);
|
||||
|
||||
extern void my_remember_signal(int signal_number,sig_handler (*func)(int));
|
||||
extern size_t dirname_part(char * to, const char *name, size_t *to_res_length);
|
||||
extern size_t dirname_part(char * to,const char *name, size_t *to_res_length);
|
||||
extern size_t dirname_length(const char *name);
|
||||
#define base_name(A) (A+dirname_length(A))
|
||||
extern int test_if_hard_path(const char *dir_name);
|
||||
|
@ -706,14 +715,14 @@ extern sig_handler sigtstp_handler(int signal_number);
|
|||
extern void handle_recived_signals(void);
|
||||
|
||||
extern sig_handler my_set_alarm_variable(int signo);
|
||||
extern void my_string_ptr_sort(uchar *base, uint items, size_t size);
|
||||
extern void my_string_ptr_sort(uchar *base,uint items,size_t size);
|
||||
extern void radixsort_for_str_ptr(uchar* base[], uint number_of_elements,
|
||||
size_t size_of_element,uchar *buffer[]);
|
||||
size_s size_of_element,uchar *buffer[]);
|
||||
extern qsort_t qsort2(void *base_ptr, size_t total_elems, size_t size,
|
||||
qsort2_cmp cmp, void *cmp_argument);
|
||||
extern qsort2_cmp get_ptr_compare(size_t);
|
||||
void my_store_ptr(uchar *buff, size_t pack_length, my_off_t pos);
|
||||
my_off_t my_get_ptr(uchar *ptr, size_t pack_length);
|
||||
my_off_t my_get_ptr(cuhar *ptr, size_t pack_length);
|
||||
extern int init_io_cache(IO_CACHE *info,File file,size_t cachesize,
|
||||
enum cache_type type,my_off_t seek_offset,
|
||||
pbool use_async_io, myf cache_myflags);
|
||||
|
@ -772,6 +781,7 @@ extern my_bool insert_dynamic(DYNAMIC_ARRAY *array,uchar * element);
|
|||
extern uchar *alloc_dynamic(DYNAMIC_ARRAY *array);
|
||||
extern uchar *pop_dynamic(DYNAMIC_ARRAY*);
|
||||
extern my_bool set_dynamic(DYNAMIC_ARRAY *array,uchar * element,uint array_index);
|
||||
extern my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements);
|
||||
extern void get_dynamic(DYNAMIC_ARRAY *array,uchar * element,uint array_index);
|
||||
extern void delete_dynamic(DYNAMIC_ARRAY *array);
|
||||
extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index);
|
||||
|
@ -830,19 +840,16 @@ extern void free_defaults(char **argv);
|
|||
extern void my_print_default_files(const char *conf_file);
|
||||
extern void print_defaults(const char *conf_file, const char **groups);
|
||||
extern my_bool my_compress(uchar *, size_t *, size_t *);
|
||||
extern my_bool my_uncompress(uchar *, size_t , size_t *);
|
||||
extern my_bool my_uncompress(uchar *, size_t *, size_t *);
|
||||
extern uchar *my_compress_alloc(const uchar *packet, size_t *len,
|
||||
size_t *complen);
|
||||
extern int packfrm(const uchar *, size_t, uchar **, size_t *);
|
||||
extern int unpackfrm(uchar **, size_t *, const uchar *);
|
||||
extern int unpackfrm(const uchar **, size_t *, const uchar *);
|
||||
|
||||
extern ha_checksum my_checksum(ha_checksum crc, const uchar *mem,
|
||||
size_t 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);
|
||||
void my_free_open_file_info(void);
|
||||
|
||||
|
@ -856,7 +863,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
|
||||
|
||||
|
|
272
include/myisam.h
272
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"
|
||||
|
@ -69,8 +55,6 @@ extern "C" {
|
|||
#define MI_MIN_KEY_BLOCK_LENGTH 1024 /* Min key block length */
|
||||
#define MI_MAX_KEY_BLOCK_LENGTH 16384
|
||||
|
||||
#define mi_portable_sizeof_char_ptr 8
|
||||
|
||||
/*
|
||||
In the following macros '_keyno_' is 0 .. keys-1.
|
||||
If there can be more keys than bits in the key_map, the highest bit
|
||||
|
@ -256,9 +240,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 char * myisam_log_filename; /* Name of logfile */
|
||||
extern ulong myisam_block_size;
|
||||
extern ulong myisam_concurrent_insert;
|
||||
|
@ -302,7 +283,7 @@ extern int mi_extra(struct st_myisam_info *file,
|
|||
enum ha_extra_function function,
|
||||
void *extra_arg);
|
||||
extern int mi_reset(struct st_myisam_info *file);
|
||||
extern ha_rows mi_records_in_range(MI_INFO *info, int inx,
|
||||
extern ha_rows mi_records_in_range(MI_INFO *info,int inx,
|
||||
key_range *min_key, key_range *max_key);
|
||||
extern int mi_log(int activate_log);
|
||||
extern int mi_is_changed(struct st_myisam_info *info);
|
||||
|
@ -310,195 +291,117 @@ extern int mi_delete_all_rows(struct st_myisam_info *info);
|
|||
extern ulong _mi_calc_blob_length(uint length , const uchar *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,
|
||||
char * name, int rep_quick);
|
||||
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name);
|
||||
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
|
||||
int mi_sort_index(HA_CHECK *param, register MI_INFO *info, char * 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,uchar *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);
|
||||
|
@ -512,6 +415,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, pagecache_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 */
|
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
|
|
@ -54,6 +54,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;
|
|
@ -1265,19 +1265,6 @@ sub command_line_setup () {
|
|||
$path_ndb_testrun_log= "$opt_vardir/log/ndb_testrun.log";
|
||||
|
||||
$path_snapshot= "$opt_tmpdir/snapshot_$opt_master_myport/";
|
||||
|
||||
if ( $opt_valgrind and $opt_debug )
|
||||
{
|
||||
# When both --valgrind and --debug is selected, send
|
||||
# all output to the trace file, making it possible to
|
||||
# see the exact location where valgrind complains
|
||||
foreach my $mysqld (@{$master}, @{$slave})
|
||||
{
|
||||
my $sidx= $mysqld->{idx} ? "$mysqld->{idx}" : "";
|
||||
$mysqld->{path_myerr}=
|
||||
"$opt_vardir/log/" . $mysqld->{type} . "$sidx.trace";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
|
@ -2330,6 +2317,25 @@ sub setup_vardir() {
|
|||
{
|
||||
unlink($name);
|
||||
}
|
||||
if ( $opt_valgrind and $opt_debug )
|
||||
{
|
||||
# When both --valgrind and --debug is selected, send
|
||||
# all output to the trace file, making it possible to
|
||||
# see the exact location where valgrind complains
|
||||
foreach my $mysqld (@{$master}, @{$slave})
|
||||
{
|
||||
my $sidx= $mysqld->{idx} ? "$mysqld->{idx}" : "";
|
||||
my $trace_name= "$opt_vardir/log/" . $mysqld->{type} . "$sidx.trace";
|
||||
open(LOG, ">$mysqld->{path_myerr}") or die "Can't create $mysqld->{path_myerr}\n";
|
||||
print LOG "
|
||||
NOTE: When running with --valgrind --debug the output from the .err file is
|
||||
stored together with the trace file to make it easier to find the exact
|
||||
position for valgrind errors.
|
||||
See trace file $trace_name.\n";
|
||||
close(LOG);
|
||||
$mysqld->{path_myerr}= $trace_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ slo_val val
|
|||
20 0
|
||||
1 0
|
||||
"Check slow log. Should see 1 row because 2 is over the threshold of 1 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
|
61
mysql-test/r/maria-big.result
Normal file
61
mysql-test/r/maria-big.result
Normal file
|
@ -0,0 +1,61 @@
|
|||
set storage_engine=maria;
|
||||
affected rows: 0
|
||||
drop table if exists t1, t2;
|
||||
affected rows: 0
|
||||
create table t1(a char(3));
|
||||
affected rows: 0
|
||||
insert into t1 values("abc");
|
||||
affected rows: 1
|
||||
insert into t1 select "def" from t1;
|
||||
affected rows: 1
|
||||
info: Records: 1 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "ghi" from t1;
|
||||
affected rows: 2
|
||||
info: Records: 2 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "jkl" from t1;
|
||||
affected rows: 4
|
||||
info: Records: 4 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "mno" from t1;
|
||||
affected rows: 8
|
||||
info: Records: 8 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "pqr" from t1;
|
||||
affected rows: 16
|
||||
info: Records: 16 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "stu" from t1;
|
||||
affected rows: 32
|
||||
info: Records: 32 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "vwx" from t1;
|
||||
affected rows: 64
|
||||
info: Records: 64 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "yza" from t1;
|
||||
affected rows: 128
|
||||
info: Records: 128 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "ceg" from t1;
|
||||
affected rows: 256
|
||||
info: Records: 256 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "ikm" from t1;
|
||||
affected rows: 512
|
||||
info: Records: 512 Duplicates: 0 Warnings: 0
|
||||
insert into t1 select "oqs" from t1;
|
||||
affected rows: 1024
|
||||
info: Records: 1024 Duplicates: 0 Warnings: 0
|
||||
select count(*) from t1;
|
||||
count(*)
|
||||
2048
|
||||
affected rows: 1
|
||||
insert into t1 select "uwy" from t1;
|
||||
affected rows: 2048
|
||||
info: Records: 2048 Duplicates: 0 Warnings: 0
|
||||
create table t2 select * from t1;
|
||||
affected rows: 4096
|
||||
info: Records: 4096 Duplicates: 0 Warnings: 0
|
||||
select count(*) from t1;
|
||||
count(*)
|
||||
4096
|
||||
affected rows: 1
|
||||
select count(*) from t2;
|
||||
count(*)
|
||||
4096
|
||||
affected rows: 1
|
||||
drop table t1, t2;
|
||||
affected rows: 0
|
1813
mysql-test/r/maria.result
Normal file
1813
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
|
@ -43,7 +43,6 @@ ctype_big5 : BUG#26711 2007-06-21 Lars Test has never worked on Do
|
|||
#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
|
||||
|
||||
#rpl_ndb_dd_advance : Bug#25913 rpl_ndb_dd_advance fails randomly
|
||||
|
||||
ndb_partition_error2 : HF is not sure if the test can work as internded on all the platforms
|
||||
|
|
|
@ -96,7 +96,7 @@ let $wait_condition= SELECT COUNT(*) = 1 FROM mysql.slow_log;
|
|||
SELECT * FROM slow_event_test;
|
||||
--echo "Check slow log. Should see 1 row because 2 is over the threshold of 1 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;
|
||||
|
|
26
mysql-test/t/maria-big.test
Normal file
26
mysql-test/t/maria-big.test
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Test of scenarios potentially too big for --valgrind or --mem
|
||||
enable_info;
|
||||
set storage_engine=maria;
|
||||
disable_warnings;
|
||||
drop table if exists t1, t2;
|
||||
enable_warnings;
|
||||
create table t1(a char(3));
|
||||
insert into t1 values("abc");
|
||||
insert into t1 select "def" from t1;
|
||||
insert into t1 select "ghi" from t1;
|
||||
insert into t1 select "jkl" from t1;
|
||||
insert into t1 select "mno" from t1;
|
||||
insert into t1 select "pqr" from t1;
|
||||
insert into t1 select "stu" from t1;
|
||||
insert into t1 select "vwx" from t1;
|
||||
insert into t1 select "yza" from t1;
|
||||
insert into t1 select "ceg" from t1;
|
||||
insert into t1 select "ikm" from t1;
|
||||
insert into t1 select "oqs" from t1;
|
||||
select count(*) from t1;
|
||||
insert into t1 select "uwy" from t1;
|
||||
create table t2 select * from t1;
|
||||
select count(*) from t1;
|
||||
select count(*) from t2;
|
||||
drop table t1, t2;
|
||||
disable_info;
|
1125
mysql-test/t/maria.test
Normal file
1125
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
|
|
@ -25,12 +25,14 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
|
|||
mf_path.c mf_loadpath.c my_file.c \
|
||||
my_open.c my_create.c my_dup.c my_seek.c my_read.c \
|
||||
my_pread.c my_write.c my_getpagesize.c \
|
||||
my_safehash.c \
|
||||
mf_keycache.c mf_keycaches.c my_crc32.c \
|
||||
mf_iocache.c mf_iocache2.c mf_cache.c mf_tempfile.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 +54,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 \
|
||||
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 +129,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.%
|
||||
|
|
|
@ -63,7 +63,7 @@ my_bool init_dynamic_array2(DYNAMIC_ARRAY *array, uint element_size,
|
|||
array->size_of_element=element_size;
|
||||
if ((array->buffer= init_buffer))
|
||||
DBUG_RETURN(FALSE);
|
||||
if (!(array->buffer=(uchar*) my_malloc_ci(element_size*init_alloc,MYF(MY_WME))))
|
||||
if (!(array->buffer=(uchar*) my_malloc_ci(element_size*init_alloc, MYF(MY_WME))))
|
||||
{
|
||||
array->max_element=0;
|
||||
DBUG_RETURN(TRUE);
|
||||
|
@ -179,7 +179,7 @@ uchar *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()
|
||||
|
@ -200,42 +200,69 @@ my_bool set_dynamic(DYNAMIC_ARRAY *array, uchar* 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 (array->buffer == (uchar *)(array + 1))
|
||||
{
|
||||
/*
|
||||
In this senerio, the buffer is statically preallocated,
|
||||
so we have to create an all-new malloc since we overflowed
|
||||
*/
|
||||
if (!(new_ptr= (char *) my_malloc(size *
|
||||
array->size_of_element,
|
||||
MYF(MY_WME))))
|
||||
return 0;
|
||||
memcpy(new_ptr, array->buffer,
|
||||
array->elements * array->size_of_element);
|
||||
}
|
||||
else
|
||||
if (!(new_ptr=(char*) my_realloc(array->buffer,size*
|
||||
array->size_of_element,
|
||||
MYF(MY_WME | MY_ALLOW_ZERO_PTR))))
|
||||
return TRUE;
|
||||
array->buffer= (uchar*) new_ptr;
|
||||
array->max_element=size;
|
||||
}
|
||||
if (idx >= array->max_element && allocate_dynamic(array, idx))
|
||||
return TRUE;
|
||||
bzero((uchar*) (array->buffer+array->elements*array->size_of_element),
|
||||
(idx - array->elements)*array->size_of_element);
|
||||
(idx - array->elements)*array->size_of_element);
|
||||
array->elements=idx+1;
|
||||
}
|
||||
memcpy(array->buffer+(idx * array->size_of_element),element,
|
||||
(size_t) array->size_of_element);
|
||||
(size_t) array->size_of_element);
|
||||
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 (array->buffer == (uchar *)(array + 1))
|
||||
{
|
||||
/*
|
||||
In this senerio, the buffer is statically preallocated,
|
||||
so we have to create an all-new malloc since we overflowed
|
||||
*/
|
||||
if (!(new_ptr= (char *) my_malloc(size *
|
||||
array->size_of_element,
|
||||
MYF(MY_WME))))
|
||||
return 0;
|
||||
memcpy(new_ptr, array->buffer,
|
||||
array->elements * array->size_of_element);
|
||||
}
|
||||
else
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
|
523
mysys/lf_alloc-pin.c
Normal file
523
mysys/lf_alloc-pin.c
Normal file
|
@ -0,0 +1,523 @@
|
|||
/* QQ: TODO multi-pinbox */
|
||||
/* 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 */
|
||||
|
||||
/*
|
||||
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 other 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.
|
||||
|
||||
Explanations:
|
||||
|
||||
3. The loop is important. The following can occur:
|
||||
thread1> LOCAL_PTR= PTR
|
||||
thread2> free(PTR); PTR=0;
|
||||
thread1> pin(PTR, PIN_NUMBER);
|
||||
now thread1 cannot access LOCAL_PTR, even if it's pinned,
|
||||
because it points to a freed memory. That is, it *must*
|
||||
verify that it has indeed pinned PTR, the shared pointer.
|
||||
|
||||
6. When a thread wants to free some LOCAL_PTR, and it scans
|
||||
all lists of pins to see whether it's pinned, it does it
|
||||
upwards, from low pin numbers to high. Thus another thread
|
||||
must copy an address from one pin to another in the same
|
||||
direction - upwards, otherwise the scanning thread may
|
||||
miss it.
|
||||
|
||||
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 16 bit,
|
||||
upper 16 bits are used for a version.
|
||||
|
||||
It is assumed that pins belong to a thread and are not transferable
|
||||
between threads (LF_PINS::stack_ends_here being a primary reason
|
||||
for this limitation).
|
||||
*/
|
||||
|
||||
#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(free_ptr_offset % sizeof(void *) == 0);
|
||||
compile_time_assert(sizeof(LF_PINS) == 128);
|
||||
lf_dynarray_init(&pinbox->pinarray, sizeof(LF_PINS));
|
||||
pinbox->pinstack_top_ver= 0;
|
||||
pinbox->pins_in_array= 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->pinarray);
|
||||
}
|
||||
|
||||
/*
|
||||
Get pins from a pinbox. Usually called via lf_alloc_get_pins() or
|
||||
lf_hash_get_pins().
|
||||
|
||||
SYNOPSYS
|
||||
pinbox -
|
||||
stack_end - a pointer to the end (top/bottom, depending on the
|
||||
STACK_DIRECTION) of stack. Used for safe alloca. There's
|
||||
no safety margin deducted, a caller should take care of it,
|
||||
if necessary.
|
||||
|
||||
DESCRIPTION
|
||||
get a new LF_PINS structure from a stack of unused pins,
|
||||
or allocate a new one out of dynarray.
|
||||
|
||||
NOTE
|
||||
It is assumed that pins belong to a thread and are not transferable
|
||||
between threads.
|
||||
*/
|
||||
LF_PINS *_lf_pinbox_get_pins(LF_PINBOX *pinbox, void *stack_end)
|
||||
{
|
||||
uint32 pins, next, top_ver;
|
||||
LF_PINS *el;
|
||||
/*
|
||||
We have an array of max. 64k elements.
|
||||
The highest index currently allocated is pinbox->pins_in_array.
|
||||
Freed elements are in a lifo stack, pinstack_top_ver.
|
||||
pinstack_top_ver is 32 bits; 16 low bits are the index in the
|
||||
array, to the first element of the list. 16 high bits are a version
|
||||
(every time the 16 low bits are updated, the 16 high bits are
|
||||
incremented). Versioniong prevents the ABA problem.
|
||||
*/
|
||||
top_ver= pinbox->pinstack_top_ver;
|
||||
do
|
||||
{
|
||||
if (!(pins= top_ver % LF_PINBOX_MAX_PINS))
|
||||
{
|
||||
/* the stack of free elements is empty */
|
||||
pins= my_atomic_add32(&pinbox->pins_in_array, 1)+1;
|
||||
if (unlikely(pins >= LF_PINBOX_MAX_PINS))
|
||||
return 0;
|
||||
/*
|
||||
note that the first allocated element has index 1 (pins==1).
|
||||
index 0 is reserved to mean "NULL pointer"
|
||||
*/
|
||||
el= (LF_PINS *)_lf_dynarray_lvalue(&pinbox->pinarray, pins);
|
||||
if (unlikely(!el))
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
el= (LF_PINS *)_lf_dynarray_value(&pinbox->pinarray, pins);
|
||||
next= el->link;
|
||||
} while (!my_atomic_cas32(&pinbox->pinstack_top_ver, &top_ver,
|
||||
top_ver-pins+next+LF_PINBOX_MAX_PINS));
|
||||
/*
|
||||
set el->link to the index of el in the dynarray (el->link has two usages:
|
||||
- if element is allocated, it's its own index
|
||||
- if element is free, it's its next element in the free stack
|
||||
*/
|
||||
el->link= pins;
|
||||
el->purgatory_count= 0;
|
||||
el->pinbox= pinbox;
|
||||
el->stack_ends_here= stack_end;
|
||||
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->pinarray.lock);
|
||||
pthread_yield();
|
||||
my_atomic_rwlock_wrlock(&pins->pinbox->pinarray.lock);
|
||||
}
|
||||
}
|
||||
top_ver= pinbox->pinstack_top_ver;
|
||||
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));
|
||||
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 of 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 may become negative below, but it means that
|
||||
we're on the last dynarray page and harvest_pins() won't be
|
||||
called again. We don't bother to make hv->npins() correct
|
||||
(that is 0) in this case.
|
||||
*/
|
||||
hv->npins-= LF_DYNARRAY_LEVEL_LENGTH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
callback for _lf_dynarray_iterate:
|
||||
scan all pins of 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;
|
||||
}
|
||||
|
||||
#if STACK_DIRECTION < 0
|
||||
#define available_stack_size(END,CUR) (long) ((char*)(CUR) - (char*)(END))
|
||||
#else
|
||||
#define available_stack_size(END,CUR) (long) ((char*)(END) - (char*)(CUR))
|
||||
#endif
|
||||
|
||||
/*
|
||||
Scan the purgatory and free everything that can be freed
|
||||
*/
|
||||
static void _lf_pinbox_real_free(LF_PINS *pins)
|
||||
{
|
||||
int npins, alloca_size;
|
||||
void *list, **addr;
|
||||
struct st_lf_alloc_node *first, *last= NULL;
|
||||
LF_PINBOX *pinbox= pins->pinbox;
|
||||
|
||||
npins= pinbox->pins_in_array+1;
|
||||
|
||||
#ifdef HAVE_ALLOCA
|
||||
alloca_size= sizeof(void *)*LF_PINBOX_PINS*npins;
|
||||
/* create a sorted list of pinned addresses, to speed up searches */
|
||||
if (available_stack_size(&pinbox, pins->stack_ends_here) > alloca_size)
|
||||
{
|
||||
struct st_harvester hv;
|
||||
addr= (void **) alloca(alloca_size);
|
||||
hv.granary= addr;
|
||||
hv.npins= npins;
|
||||
/* scan the dynarray and accumulate all pinned addresses */
|
||||
_lf_dynarray_iterate(&pinbox->pinarray,
|
||||
(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->pinarray,
|
||||
(lf_dynarray_func)match_pins, cur))
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* not pinned - freeing */
|
||||
if (last)
|
||||
last= last->next= (struct st_lf_alloc_node *)cur;
|
||||
else
|
||||
first= last= (struct st_lf_alloc_node *)cur;
|
||||
continue;
|
||||
found:
|
||||
/* pinned - keeping */
|
||||
add_to_purgatory(pins, cur);
|
||||
}
|
||||
if (last)
|
||||
pinbox->free_func(first, last, pinbox->free_func_arg);
|
||||
}
|
||||
|
||||
/* lock-free memory allocator for fixed-size objects */
|
||||
|
||||
LF_REQUIRE_PINS(1);
|
||||
|
||||
/*
|
||||
callback for _lf_pinbox_real_free to free a list of unpinned objects -
|
||||
add it back to the allocator stack
|
||||
|
||||
DESCRIPTION
|
||||
'first' and 'last' are the ends of the linked list of st_lf_alloc_node's:
|
||||
first->el->el->....->el->last. Use first==last to free only one element.
|
||||
*/
|
||||
static void alloc_free(struct st_lf_alloc_node *first,
|
||||
struct st_lf_alloc_node *last,
|
||||
LF_ALLOCATOR *allocator)
|
||||
{
|
||||
struct st_lf_alloc_node *tmp;
|
||||
tmp= allocator->top;
|
||||
do
|
||||
{
|
||||
last->next= tmp;
|
||||
} while (!my_atomic_casptr((void **)&allocator->top, (void **)&tmp, first) &&
|
||||
LF_BACKOFF);
|
||||
}
|
||||
|
||||
/*
|
||||
initialize lock-free allocator
|
||||
|
||||
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.
|
||||
This memory will be used to link waiting-to-be-freed
|
||||
objects in a purgatory list.
|
||||
*/
|
||||
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 >= sizeof(void*) + free_ptr_offset);
|
||||
}
|
||||
|
||||
/*
|
||||
destroy the allocator, free everything that's in it
|
||||
|
||||
NOTE
|
||||
As every other init/destroy function here and elsewhere it
|
||||
is not thread safe. No, this function is no different, ensure
|
||||
that no thread needs the allocator before destroying it.
|
||||
We are not responsible for any damage that may be caused by
|
||||
accessing the allocator when it is being or has been destroyed.
|
||||
Oh yes, and don't put your cat in a microwave.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
node= (void *)my_malloc(allocator->element_size, MYF(MY_WME));
|
||||
#ifdef MY_LF_EXTRA_DEBUG
|
||||
if (likely(node))
|
||||
my_atomic_add32(&allocator->mallocs, 1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if (my_atomic_casptr((void **)&allocator->top, (void *)&node, node->next))
|
||||
break;
|
||||
}
|
||||
_lf_unpin(pins, 0);
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
count the number of objects in a pool.
|
||||
|
||||
NOTE
|
||||
This is NOT thread-safe !!!
|
||||
*/
|
||||
uint lf_alloc_pool_count(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;
|
||||
}
|
||||
|
208
mysys/lf_dynarray.c
Normal file
208
mysys/lf_dynarray.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
/* 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 */
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
With four levels the number of elements is limited to 4311810304
|
||||
(but as in all functions index is uint, the real limit is 2^32-1)
|
||||
|
||||
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)); }
|
||||
|
||||
NOTE
|
||||
if func() returns non-zero, the scan is aborted
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
493
mysys/lf_hash.c
Normal file
493
mysys/lf_hash.c
Normal file
|
@ -0,0 +1,493 @@
|
|||
/* 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 */
|
||||
|
||||
/*
|
||||
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;
|
||||
/*
|
||||
data is stored here, directly after the keylen.
|
||||
thus the pointer to data is (void*)(slist_element_ptr+1)
|
||||
*/
|
||||
} 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 { /* PTR() isn't necessary below, head is a dummy node */
|
||||
cursor->curr= (LF_SLIST *)(*cursor->prev);
|
||||
_lf_pin(pins, 1, cursor->curr);
|
||||
} while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF);
|
||||
for (;;)
|
||||
{
|
||||
if (unlikely(!cursor->curr))
|
||||
return 0; /* end of the list */
|
||||
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
|
||||
{
|
||||
/*
|
||||
we found a deleted node - be nice, help the other thread
|
||||
and remove this deleted node
|
||||
*/
|
||||
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.
|
||||
if there're nodes with the same key value, a new node is added before them.
|
||||
*/
|
||||
static LF_SLIST *linsert(LF_SLIST * volatile *head, CHARSET_INFO *cs,
|
||||
LF_SLIST *node, LF_PINS *pins, uint flags)
|
||||
{
|
||||
CURSOR cursor;
|
||||
int res;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (lfind(head, cs, node->hashnr, node->key, node->keylen,
|
||||
&cursor, pins) &&
|
||||
(flags & LF_HASH_UNIQUE))
|
||||
{
|
||||
res= 0; /* duplicate found */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->link= (intptr)cursor.curr;
|
||||
DBUG_ASSERT(node->link != (intptr)node); /* no circular references */
|
||||
DBUG_ASSERT(cursor.prev != &node->link); /* no circular references */
|
||||
if (my_atomic_casptr((void **)cursor.prev, (void **)&cursor.curr, node))
|
||||
{
|
||||
res= 1; /* inserted ok */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_lf_unpin(pins, 0);
|
||||
_lf_unpin(pins, 1);
|
||||
_lf_unpin(pins, 2);
|
||||
/*
|
||||
Note that cursor.curr is not pinned here and the pointer is unreliable,
|
||||
the object may dissapear anytime. But if it points to a dummy node, the
|
||||
pointer is safe, because dummy nodes are never freed - initialize_bucket()
|
||||
uses this fact.
|
||||
*/
|
||||
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;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!lfind(head, cs, hashnr, key, keylen, &cursor, pins))
|
||||
{
|
||||
res= 1; /* not found */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* mark the node deleted */
|
||||
if (my_atomic_casptr((void **)&(cursor.curr->link),
|
||||
(void **)&cursor.next,
|
||||
(void *)(((intptr)cursor.next) | 1)))
|
||||
{
|
||||
/* and remove it from the list */
|
||||
if (my_atomic_casptr((void **)cursor.prev,
|
||||
(void **)&cursor.curr, cursor.next))
|
||||
_lf_alloc_free(pins, cursor.curr);
|
||||
else
|
||||
{
|
||||
/*
|
||||
somebody already "helped" us and removed the node ?
|
||||
Let's check if we need to help that someone too!
|
||||
(to ensure the number of "set DELETED flag" actions
|
||||
is equal to the number of "remove from the list" actions)
|
||||
*/
|
||||
lfind(head, cs, hashnr, key, keylen, &cursor, pins);
|
||||
}
|
||||
res= 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_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;
|
||||
}
|
||||
|
||||
/*
|
||||
compute the hash key value from the raw key.
|
||||
note, that the hash value is limited to 2^31, because we need one
|
||||
bit to distinguish between normal and dummy nodes.
|
||||
*/
|
||||
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 /* average number of elements in a bucket */
|
||||
|
||||
static int 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, **head= (LF_SLIST **)_lf_dynarray_value(&hash->array, 0);
|
||||
|
||||
if (unlikely(!head))
|
||||
return;
|
||||
el= *head;
|
||||
|
||||
while (el)
|
||||
{
|
||||
intptr next= el->link;
|
||||
if (el->hashnr & 1)
|
||||
lf_alloc_direct_free(&hash->alloc, el); /* normal node */
|
||||
else
|
||||
my_free((void *)el, MYF(0)); /* dummy node */
|
||||
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)
|
||||
-1 - out of memory
|
||||
|
||||
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);
|
||||
if (unlikely(!node))
|
||||
return -1;
|
||||
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 (unlikely(!el))
|
||||
return -1;
|
||||
if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
|
||||
return -1;
|
||||
node->hashnr= my_reverse_bits(hashnr) | 1; /* normal node */
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
DESCRIPTION
|
||||
deletes an element with the given key from the hash (if a hash is
|
||||
not unique and there're many elements with this key - the "first"
|
||||
matching element is deleted)
|
||||
RETURN
|
||||
0 - deleted
|
||||
1 - didn't (not found)
|
||||
-1 - out of memory
|
||||
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 (unlikely(!el))
|
||||
return -1;
|
||||
/*
|
||||
note that we still need to initialize_bucket here,
|
||||
we cannot return "node not found", because an old bucket of that
|
||||
node may've been split and the node was assigned to a new bucket
|
||||
that was never accessed before and thus is not initialized.
|
||||
*/
|
||||
if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
|
||||
return -1;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
RETURN
|
||||
a pointer to an element with the given key (if a hash is not unique and
|
||||
there're many elements with this key - the "first" matching element)
|
||||
NULL if nothing is found
|
||||
MY_ERRPTR if OOM
|
||||
|
||||
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 (unlikely(!el))
|
||||
return MY_ERRPTR;
|
||||
if (*el == NULL && unlikely(initialize_bucket(hash, el, bucket, pins)))
|
||||
return MY_ERRPTR;
|
||||
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 byte *dummy_key= "";
|
||||
|
||||
/*
|
||||
RETURN
|
||||
0 - ok
|
||||
-1 - out of memory
|
||||
*/
|
||||
static int 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 (unlikely(!el || !dummy))
|
||||
return -1;
|
||||
if (*el == NULL && bucket &&
|
||||
unlikely(initialize_bucket(hash, el, parent, pins)))
|
||||
return -1;
|
||||
dummy->hashnr= my_reverse_bits(bucket) | 0; /* dummy node */
|
||||
dummy->key= (char*) dummy_key;
|
||||
dummy->keylen= 0;
|
||||
if ((cur= linsert(el, hash->charset, dummy, pins, LF_HASH_UNIQUE)))
|
||||
{
|
||||
my_free((void *)dummy, MYF(0));
|
||||
dummy= cur;
|
||||
}
|
||||
my_atomic_casptr((void **)node, (void **)&tmp, dummy);
|
||||
/*
|
||||
note that if the CAS above failed (after linsert() succeeded),
|
||||
it would mean that some other thread has executed linsert() for
|
||||
the same dummy node, its linsert() failed, it picked up our
|
||||
dummy node (in "dummy= cur") and executed the same CAS as above.
|
||||
Which means that even if CAS above failed we don't need to retry,
|
||||
and we should not free(dummy) - there's no memory leak here
|
||||
*/
|
||||
return 0;
|
||||
}
|
|
@ -105,6 +105,7 @@
|
|||
#include <keycache.h>
|
||||
#include "my_static.h"
|
||||
#include <m_string.h>
|
||||
#include <my_bit.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
@ -1261,12 +1262,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
|
||||
}
|
||||
|
||||
|
@ -2359,9 +2360,9 @@ restart:
|
|||
(block->hash_link->diskpos == filepos)));
|
||||
*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",
|
||||
|
@ -2512,17 +2513,19 @@ static void read_block(KEY_CACHE *keycache,
|
|||
*/
|
||||
|
||||
uchar *key_cache_read(KEY_CACHE *keycache,
|
||||
File file, my_off_t filepos, int level,
|
||||
uchar *buff, uint length,
|
||||
uint block_length __attribute__((unused)),
|
||||
int return_buffer __attribute__((unused)))
|
||||
File file, my_off_t filepos, int level,
|
||||
uchar *buff, uint length,
|
||||
uint block_length __attribute__((unused)),
|
||||
int return_buffer __attribute__((unused)))
|
||||
{
|
||||
my_bool locked_and_incremented= FALSE;
|
||||
int error=0;
|
||||
uchar *start= buff;
|
||||
DBUG_ENTER("key_cache_read");
|
||||
DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
|
||||
(uint) file, (ulong) filepos, length));
|
||||
DBUG_PRINT("enter", ("fd: %u pos: %lu page: %lu length: %u",
|
||||
(uint) file, (ulong) filepos,
|
||||
(ulong) (filepos / keycache->key_cache_block_size),
|
||||
length));
|
||||
|
||||
if (keycache->key_cache_inited)
|
||||
{
|
||||
|
@ -2533,12 +2536,13 @@ uchar *key_cache_read(KEY_CACHE *keycache,
|
|||
uint status;
|
||||
int page_st;
|
||||
|
||||
/*
|
||||
|
||||
/*
|
||||
When the key cache is once initialized, we use the cache_lock to
|
||||
reliably distinguish the cases of normal operation, resizing, and
|
||||
disabled cache. We always increment and decrement
|
||||
'cnt_for_resize_op' so that a resizer can wait for pending I/O.
|
||||
*/
|
||||
*/
|
||||
keycache_pthread_mutex_lock(&keycache->cache_lock);
|
||||
/*
|
||||
Cache resizing has two phases: Flushing and re-initializing. In
|
||||
|
@ -2565,7 +2569,7 @@ uchar *key_cache_read(KEY_CACHE *keycache,
|
|||
do
|
||||
{
|
||||
/* Cache could be disabled in a later iteration. */
|
||||
|
||||
|
||||
if (!keycache->can_be_used)
|
||||
goto no_key_cache;
|
||||
/* Start reading at the beginning of the cache block. */
|
||||
|
@ -2975,9 +2979,10 @@ int key_cache_write(KEY_CACHE *keycache,
|
|||
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));
|
||||
("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));
|
||||
|
||||
if (!dont_write)
|
||||
{
|
||||
|
@ -3169,7 +3174,7 @@ int key_cache_write(KEY_CACHE *keycache,
|
|||
|
||||
if (!dont_write)
|
||||
{
|
||||
/* Not used in the server. buff has been written to disk at start. */
|
||||
/* Not used in the server. buff has been written to disk at start. */
|
||||
if ((block->status & BLOCK_CHANGED) &&
|
||||
(!offset && read_length >= keycache->key_cache_block_size))
|
||||
link_to_file_list(keycache, block, block->hash_link->file, 1);
|
||||
|
@ -3183,7 +3188,6 @@ int key_cache_write(KEY_CACHE *keycache,
|
|||
a flush.
|
||||
*/
|
||||
block->status&= ~BLOCK_FOR_UPDATE;
|
||||
|
||||
set_if_smaller(block->offset, offset);
|
||||
set_if_bigger(block->length, read_length+offset);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2003 MySQL AB
|
||||
/* Copyright (C) 2003-2007 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
|
||||
|
@ -25,269 +25,7 @@
|
|||
#include <keycache.h>
|
||||
#include <hash.h>
|
||||
#include <m_string.h>
|
||||
|
||||
/*****************************************************************************
|
||||
General functions to handle SAFE_HASH objects.
|
||||
|
||||
A SAFE_HASH object is used to store the hash, the mutex and default value
|
||||
needed by the rest of the key cache code.
|
||||
This is a separate struct to make it easy to later reuse the code for other
|
||||
purposes
|
||||
|
||||
All entries are linked in a list to allow us to traverse all elements
|
||||
and delete selected ones. (HASH doesn't allow any easy ways to do this).
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
Struct to store a key and pointer to object
|
||||
*/
|
||||
|
||||
typedef struct st_safe_hash_entry
|
||||
{
|
||||
uchar *key;
|
||||
uint length;
|
||||
uchar *data;
|
||||
struct st_safe_hash_entry *next, **prev;
|
||||
} SAFE_HASH_ENTRY;
|
||||
|
||||
|
||||
typedef struct st_safe_hash_with_default
|
||||
{
|
||||
#ifdef THREAD
|
||||
rw_lock_t mutex;
|
||||
#endif
|
||||
HASH hash;
|
||||
uchar *default_value;
|
||||
SAFE_HASH_ENTRY *root;
|
||||
} SAFE_HASH;
|
||||
|
||||
|
||||
/*
|
||||
Free a SAFE_HASH_ENTRY
|
||||
|
||||
This function is called by the hash object on delete
|
||||
*/
|
||||
|
||||
static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry)
|
||||
{
|
||||
DBUG_ENTER("free_assign_entry");
|
||||
my_free((uchar*) entry, MYF(0));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/* Get key and length for a SAFE_HASH_ENTRY */
|
||||
|
||||
static uchar *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, size_t *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
*length=entry->length;
|
||||
return (uchar*) entry->key;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Init a SAFE_HASH object
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_init()
|
||||
hash safe_hash handler
|
||||
elements Expected max number of elements
|
||||
default_value default value
|
||||
|
||||
NOTES
|
||||
In case of error we set hash->default_value to 0 to allow one to call
|
||||
safe_hash_free on an object that couldn't be initialized.
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
1 error
|
||||
*/
|
||||
|
||||
static my_bool safe_hash_init(SAFE_HASH *hash, uint elements,
|
||||
uchar *default_value)
|
||||
{
|
||||
DBUG_ENTER("safe_hash");
|
||||
if (hash_init(&hash->hash, &my_charset_bin, elements,
|
||||
0, 0, (hash_get_key) safe_hash_entry_get,
|
||||
(void (*)(void*)) safe_hash_entry_free, 0))
|
||||
{
|
||||
hash->default_value= 0;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
my_rwlock_init(&hash->mutex, 0);
|
||||
hash->default_value= default_value;
|
||||
hash->root= 0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Free a SAFE_HASH object
|
||||
|
||||
NOTES
|
||||
This is safe to call on any object that has been sent to safe_hash_init()
|
||||
*/
|
||||
|
||||
static void safe_hash_free(SAFE_HASH *hash)
|
||||
{
|
||||
/*
|
||||
Test if safe_hash_init succeeded. This will also guard us against multiple
|
||||
free calls.
|
||||
*/
|
||||
if (hash->default_value)
|
||||
{
|
||||
hash_free(&hash->hash);
|
||||
rwlock_destroy(&hash->mutex);
|
||||
hash->default_value=0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Return the value stored for a key or default value if no key
|
||||
*/
|
||||
|
||||
static uchar *safe_hash_search(SAFE_HASH *hash, const uchar *key, uint length)
|
||||
{
|
||||
uchar *result;
|
||||
DBUG_ENTER("safe_hash_search");
|
||||
rw_rdlock(&hash->mutex);
|
||||
result= hash_search(&hash->hash, key, length);
|
||||
rw_unlock(&hash->mutex);
|
||||
if (!result)
|
||||
result= hash->default_value;
|
||||
else
|
||||
result= ((SAFE_HASH_ENTRY*) result)->data;
|
||||
DBUG_PRINT("exit",("data: 0x%lx", (long) result));
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Associate a key with some data
|
||||
|
||||
SYONOPSIS
|
||||
safe_hash_set()
|
||||
hash Hash handle
|
||||
key key (path to table etc..)
|
||||
length Length of key
|
||||
data data to to associate with the data
|
||||
|
||||
NOTES
|
||||
This can be used both to insert a new entry and change an existing
|
||||
entry.
|
||||
If one associates a key with the default key cache, the key is deleted
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
1 error (Can only be EOM). In this case my_message() is called.
|
||||
*/
|
||||
|
||||
static my_bool safe_hash_set(SAFE_HASH *hash, const uchar *key, uint length,
|
||||
uchar *data)
|
||||
{
|
||||
SAFE_HASH_ENTRY *entry;
|
||||
my_bool error= 0;
|
||||
DBUG_ENTER("safe_hash_set");
|
||||
DBUG_PRINT("enter",("key: %.*s data: 0x%lx", length, key, (long) data));
|
||||
|
||||
rw_wrlock(&hash->mutex);
|
||||
entry= (SAFE_HASH_ENTRY*) hash_search(&hash->hash, key, length);
|
||||
|
||||
if (data == hash->default_value)
|
||||
{
|
||||
/*
|
||||
The key is to be associated with the default entry. In this case
|
||||
we can just delete the entry (if it existed) from the hash as a
|
||||
search will return the default entry
|
||||
*/
|
||||
if (!entry) /* nothing to do */
|
||||
goto end;
|
||||
/* unlink entry from list */
|
||||
if ((*entry->prev= entry->next))
|
||||
entry->next->prev= entry->prev;
|
||||
hash_delete(&hash->hash, (uchar*) entry);
|
||||
goto end;
|
||||
}
|
||||
if (entry)
|
||||
{
|
||||
/* Entry existed; Just change the pointer to point at the new data */
|
||||
entry->data= data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(entry= (SAFE_HASH_ENTRY *) my_malloc(sizeof(*entry) + length,
|
||||
MYF(MY_WME))))
|
||||
{
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
entry->key= (uchar*) (entry +1);
|
||||
memcpy((char*) entry->key, (char*) key, length);
|
||||
entry->length= length;
|
||||
entry->data= data;
|
||||
/* Link entry to list */
|
||||
if ((entry->next= hash->root))
|
||||
entry->next->prev= &entry->next;
|
||||
entry->prev= &hash->root;
|
||||
hash->root= entry;
|
||||
if (my_hash_insert(&hash->hash, (uchar*) entry))
|
||||
{
|
||||
/* This can only happen if hash got out of memory */
|
||||
my_free((char*) entry, MYF(0));
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
rw_unlock(&hash->mutex);
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Change all entres with one data value to another data value
|
||||
|
||||
SYONOPSIS
|
||||
safe_hash_change()
|
||||
hash Hash handle
|
||||
old_data Old data
|
||||
new_data Change all 'old_data' to this
|
||||
|
||||
NOTES
|
||||
We use the linked list to traverse all elements in the hash as
|
||||
this allows us to delete elements in the case where 'new_data' is the
|
||||
default value.
|
||||
*/
|
||||
|
||||
static void safe_hash_change(SAFE_HASH *hash, uchar *old_data, uchar *new_data)
|
||||
{
|
||||
SAFE_HASH_ENTRY *entry, *next;
|
||||
DBUG_ENTER("safe_hash_set");
|
||||
|
||||
rw_wrlock(&hash->mutex);
|
||||
|
||||
for (entry= hash->root ; entry ; entry= next)
|
||||
{
|
||||
next= entry->next;
|
||||
if (entry->data == old_data)
|
||||
{
|
||||
if (new_data == hash->default_value)
|
||||
{
|
||||
if ((*entry->prev= entry->next))
|
||||
entry->next->prev= entry->prev;
|
||||
hash_delete(&hash->hash, (uchar*) entry);
|
||||
}
|
||||
else
|
||||
entry->data= new_data;
|
||||
}
|
||||
}
|
||||
|
||||
rw_unlock(&hash->mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
#include "my_safehash.h"
|
||||
|
||||
/*****************************************************************************
|
||||
Functions to handle the key cache objects
|
||||
|
@ -315,6 +53,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 +64,13 @@ void multi_keycache_free(void)
|
|||
key cache to use
|
||||
*/
|
||||
|
||||
KEY_CACHE *multi_key_cache_search(uchar *key, uint length)
|
||||
KEY_CACHE *multi_key_cache_search(uchar *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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -361,3 +102,5 @@ void multi_key_cache_change(KEY_CACHE *old_data,
|
|||
{
|
||||
safe_hash_change(&key_cache_hash, (uchar*) old_data, (uchar*) new_data);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -63,12 +63,12 @@ size_t my_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset,
|
|||
pthread_mutex_unlock(&my_file_info[Filedes].mutex);
|
||||
#else
|
||||
if ((error= ((readbytes= pread(Filedes, Buffer, Count, offset)) != Count)))
|
||||
my_errno= errno;
|
||||
my_errno= errno ? errno : -1;
|
||||
#endif
|
||||
if (error || readbytes != Count)
|
||||
{
|
||||
DBUG_PRINT("warning",("Read only %d bytes off %u from %d, errno: %d",
|
||||
(int) readbytes, (uint) Count,Filedes,my_errno));
|
||||
(int) readbytes, (uint) Count,Filedes,my_errno));
|
||||
#ifdef THREAD
|
||||
if ((readbytes == 0 || readbytes == (size_t) -1) && errno == EINTR)
|
||||
{
|
||||
|
@ -115,7 +115,7 @@ size_t my_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset,
|
|||
RETURN
|
||||
(size_t) -1 Error
|
||||
# Number of bytes read
|
||||
*/
|
||||
*/
|
||||
|
||||
size_t my_pwrite(int Filedes, const uchar *Buffer, size_t Count,
|
||||
my_off_t offset, myf MyFlags)
|
||||
|
|
|
@ -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 */
|
||||
|
|
297
mysys/my_safehash.c
Normal file
297
mysys/my_safehash.c
Normal file
|
@ -0,0 +1,297 @@
|
|||
/* Copyright (C) 2003-2007 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 */
|
||||
|
||||
/*
|
||||
Handling of multiple key caches
|
||||
|
||||
The idea is to have a thread safe hash on the table name,
|
||||
with a default key cache value that is returned if the table name is not in
|
||||
the cache.
|
||||
*/
|
||||
|
||||
#include "mysys_priv.h"
|
||||
#include <m_string.h>
|
||||
#include "my_safehash.h"
|
||||
|
||||
/*****************************************************************************
|
||||
General functions to handle SAFE_HASH objects.
|
||||
|
||||
A SAFE_HASH object is used to store the hash, the mutex and default value
|
||||
needed by the rest of the key cache code.
|
||||
This is a separate struct to make it easy to later reuse the code for other
|
||||
purposes
|
||||
|
||||
All entries are linked in a list to allow us to traverse all elements
|
||||
and delete selected ones. (HASH doesn't allow any easy ways to do this).
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
Free a SAFE_HASH_ENTRY
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_entry_free()
|
||||
entry The entry which should be freed
|
||||
|
||||
NOTE
|
||||
This function is called by the hash object on delete
|
||||
*/
|
||||
|
||||
static void safe_hash_entry_free(SAFE_HASH_ENTRY *entry)
|
||||
{
|
||||
DBUG_ENTER("safe_hash_entry_free");
|
||||
my_free((gptr) entry, MYF(0));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get key and length for a SAFE_HASH_ENTRY
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_entry_get()
|
||||
entry The entry for which the key should be returned
|
||||
length Length of the key
|
||||
|
||||
RETURN
|
||||
# reference on the key
|
||||
*/
|
||||
|
||||
static byte *safe_hash_entry_get(SAFE_HASH_ENTRY *entry, uint *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
*length= entry->length;
|
||||
return (byte*) entry->key;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Init a SAFE_HASH object
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_init()
|
||||
hash safe_hash handler
|
||||
elements Expected max number of elements
|
||||
default_value default value
|
||||
|
||||
NOTES
|
||||
In case of error we set hash->default_value to 0 to allow one to call
|
||||
safe_hash_free on an object that couldn't be initialized.
|
||||
|
||||
RETURN
|
||||
0 OK
|
||||
1 error
|
||||
*/
|
||||
|
||||
my_bool safe_hash_init(SAFE_HASH *hash, uint elements,
|
||||
byte *default_value)
|
||||
{
|
||||
DBUG_ENTER("safe_hash_init");
|
||||
if (hash_init(&hash->hash, &my_charset_bin, elements,
|
||||
0, 0, (hash_get_key) safe_hash_entry_get,
|
||||
(void (*)(void*)) safe_hash_entry_free, 0))
|
||||
{
|
||||
hash->default_value= 0;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
my_rwlock_init(&hash->mutex, 0);
|
||||
hash->default_value= default_value;
|
||||
hash->root= 0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Free a SAFE_HASH object
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_free()
|
||||
hash Hash handle
|
||||
|
||||
NOTES
|
||||
This is safe to call on any object that has been sent to safe_hash_init()
|
||||
*/
|
||||
|
||||
void safe_hash_free(SAFE_HASH *hash)
|
||||
{
|
||||
/*
|
||||
Test if safe_hash_init succeeded. This will also guard us against multiple
|
||||
free calls.
|
||||
*/
|
||||
if (hash->default_value)
|
||||
{
|
||||
hash_free(&hash->hash);
|
||||
rwlock_destroy(&hash->mutex);
|
||||
hash->default_value=0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Return the value stored for a key or default value if no key
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_search()
|
||||
hash Hash handle
|
||||
key key (path to table etc..)
|
||||
length Length of key
|
||||
def Default value of data
|
||||
|
||||
RETURN
|
||||
# data associated with the key of default value if data was not found
|
||||
*/
|
||||
|
||||
byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length,
|
||||
byte *def)
|
||||
{
|
||||
byte *result;
|
||||
DBUG_ENTER("safe_hash_search");
|
||||
rw_rdlock(&hash->mutex);
|
||||
result= hash_search(&hash->hash, key, length);
|
||||
rw_unlock(&hash->mutex);
|
||||
if (!result)
|
||||
result= def;
|
||||
else
|
||||
result= ((SAFE_HASH_ENTRY*) result)->data;
|
||||
DBUG_PRINT("exit",("data: 0x%lx", (long) result));
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Associate a key with some data
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_set()
|
||||
hash Hash handle
|
||||
key key (path to table etc..)
|
||||
length Length of key
|
||||
data data to to associate with the data
|
||||
|
||||
NOTES
|
||||
This can be used both to insert a new entry and change an existing
|
||||
entry.
|
||||
If one associates a key with the default key cache, the key is deleted
|
||||
|
||||
RETURN
|
||||
0 OK
|
||||
1 error (Can only be EOM). In this case my_message() is called.
|
||||
*/
|
||||
|
||||
my_bool safe_hash_set(SAFE_HASH *hash, const byte *key, uint length,
|
||||
byte *data)
|
||||
{
|
||||
SAFE_HASH_ENTRY *entry;
|
||||
my_bool error= 0;
|
||||
DBUG_ENTER("safe_hash_set");
|
||||
DBUG_PRINT("enter",("key: %.*s data: 0x%lx", length, key, (long) data));
|
||||
|
||||
rw_wrlock(&hash->mutex);
|
||||
entry= (SAFE_HASH_ENTRY*) hash_search(&hash->hash, key, length);
|
||||
|
||||
if (data == hash->default_value)
|
||||
{
|
||||
/*
|
||||
The key is to be associated with the default entry. In this case
|
||||
we can just delete the entry (if it existed) from the hash as a
|
||||
search will return the default entry
|
||||
*/
|
||||
if (!entry) /* nothing to do */
|
||||
goto end;
|
||||
/* unlink entry from list */
|
||||
if ((*entry->prev= entry->next))
|
||||
entry->next->prev= entry->prev;
|
||||
hash_delete(&hash->hash, (byte*) entry);
|
||||
goto end;
|
||||
}
|
||||
if (entry)
|
||||
{
|
||||
/* Entry existed; Just change the pointer to point at the new data */
|
||||
entry->data= data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(entry= (SAFE_HASH_ENTRY *) my_malloc(sizeof(*entry) + length,
|
||||
MYF(MY_WME))))
|
||||
{
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
entry->key= (byte*) (entry +1);
|
||||
memcpy((char*) entry->key, (char*) key, length);
|
||||
entry->length= length;
|
||||
entry->data= data;
|
||||
/* Link entry to list */
|
||||
if ((entry->next= hash->root))
|
||||
entry->next->prev= &entry->next;
|
||||
entry->prev= &hash->root;
|
||||
hash->root= entry;
|
||||
if (my_hash_insert(&hash->hash, (byte*) entry))
|
||||
{
|
||||
/* This can only happen if hash got out of memory */
|
||||
my_free((char*) entry, MYF(0));
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
rw_unlock(&hash->mutex);
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Change all entries with one data value to another data value
|
||||
|
||||
SYNOPSIS
|
||||
safe_hash_change()
|
||||
hash Hash handle
|
||||
old_data Old data
|
||||
new_data Change all 'old_data' to this
|
||||
|
||||
NOTES
|
||||
We use the linked list to traverse all elements in the hash as
|
||||
this allows us to delete elements in the case where 'new_data' is the
|
||||
default value.
|
||||
*/
|
||||
|
||||
void safe_hash_change(SAFE_HASH *hash, byte *old_data, byte *new_data)
|
||||
{
|
||||
SAFE_HASH_ENTRY *entry, *next;
|
||||
DBUG_ENTER("safe_hash_change");
|
||||
|
||||
rw_wrlock(&hash->mutex);
|
||||
|
||||
for (entry= hash->root ; entry ; entry= next)
|
||||
{
|
||||
next= entry->next;
|
||||
if (entry->data == old_data)
|
||||
{
|
||||
if (new_data == hash->default_value)
|
||||
{
|
||||
if ((*entry->prev= entry->next))
|
||||
entry->next->prev= entry->prev;
|
||||
hash_delete(&hash->hash, (byte*) entry);
|
||||
}
|
||||
else
|
||||
entry->data= new_data;
|
||||
}
|
||||
}
|
||||
|
||||
rw_unlock(&hash->mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
58
mysys/my_safehash.h
Normal file
58
mysys/my_safehash.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* Copyright (C) 2003 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 */
|
||||
|
||||
/*
|
||||
Handling of multiple key caches
|
||||
|
||||
The idea is to have a thread safe hash on the table name,
|
||||
with a default key cache value that is returned if the table name is not in
|
||||
the cache.
|
||||
*/
|
||||
|
||||
#include <hash.h>
|
||||
|
||||
/*
|
||||
Struct to store a key and pointer to object
|
||||
*/
|
||||
|
||||
typedef struct st_safe_hash_entry
|
||||
{
|
||||
byte *key;
|
||||
uint length;
|
||||
byte *data;
|
||||
struct st_safe_hash_entry *next, **prev;
|
||||
} SAFE_HASH_ENTRY;
|
||||
|
||||
|
||||
typedef struct st_safe_hash_with_default
|
||||
{
|
||||
#ifdef THREAD
|
||||
rw_lock_t mutex;
|
||||
#endif
|
||||
HASH hash;
|
||||
byte *default_value;
|
||||
SAFE_HASH_ENTRY *root;
|
||||
} SAFE_HASH;
|
||||
|
||||
|
||||
my_bool safe_hash_init(SAFE_HASH *hash, uint elements,
|
||||
byte *default_value);
|
||||
void safe_hash_free(SAFE_HASH *hash);
|
||||
byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length,
|
||||
byte *def);
|
||||
my_bool safe_hash_set(SAFE_HASH *hash, const byte *key, uint length,
|
||||
byte *data);
|
||||
void safe_hash_change(SAFE_HASH *hash, byte *old_data, byte *new_data);
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -428,6 +428,29 @@ void TERMINATE(FILE *file)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Report where a piece of memory was allocated
|
||||
|
||||
This is usefull to call from withing a debugger
|
||||
*/
|
||||
|
||||
|
||||
void sf_malloc_report_allocated(void *memory)
|
||||
{
|
||||
struct st_irem *irem;
|
||||
for (irem= sf_malloc_root ; irem ; irem=irem->next)
|
||||
{
|
||||
char *data= (((char*) irem) + ALIGN_SIZE(sizeof(struct st_irem)) +
|
||||
sf_malloc_prehunc);
|
||||
if (data <= (char*) memory && (char*) memory <= data + irem->datasize)
|
||||
{
|
||||
printf("%u bytes at 0x%lx, allocated at line %u in '%s'\n",
|
||||
irem->datasize, (long) data, irem->linenum, irem->filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns 0 if chunk is ok */
|
||||
|
||||
static int _checkchunk(register struct st_irem *irem, const char *filename,
|
||||
|
|
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];
|
||||
|
|
|
@ -457,7 +457,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
|
|||
ref_pos= ref_buff;
|
||||
quick_select=select && select->quick;
|
||||
record=0;
|
||||
flag= ((!indexfile && file->ha_table_flags() & HA_REC_NOT_IN_SEQ)
|
||||
flag= ((!indexfile && (file->ha_table_flags() & HA_REC_NOT_IN_SEQ))
|
||||
|| quick_select);
|
||||
if (indexfile || flag)
|
||||
ref_pos= &file->ref[0];
|
||||
|
@ -1076,7 +1076,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);
|
||||
|
@ -1146,7 +1146,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 */
|
||||
|
@ -1158,10 +1158,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
|
||||
|
@ -1250,7 +1250,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;
|
||||
|
||||
/*
|
||||
|
@ -1285,7 +1285,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\
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
#include <myisampack.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
#include <maria.h>
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
#include "ha_partition.h"
|
||||
#endif
|
||||
|
@ -2789,6 +2793,98 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
|
|||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
pagecache handling.
|
||||
|
||||
This code is only relevant for maria tables
|
||||
|
||||
pagecache->cache may be 0 only in the case where a key cache is not
|
||||
initialized or when we where not able to init the key cache in a previous
|
||||
call to ha_init_pagecache() (probably out of memory)
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
|
||||
/* Init a pagecache if it has not been initied before */
|
||||
|
||||
int ha_init_pagecache(const char *name, PAGECACHE *pagecache)
|
||||
{
|
||||
DBUG_ENTER("ha_init_key_cache");
|
||||
|
||||
if (!pagecache->inited)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
long tmp_buff_size= (long) pagecache->param_buff_size;
|
||||
uint division_limit= pagecache->param_division_limit;
|
||||
uint age_threshold= pagecache->param_age_threshold;
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
DBUG_RETURN(!init_pagecache(pagecache,
|
||||
tmp_buff_size, division_limit, age_threshold,
|
||||
MARIA_KEY_BLOCK_LENGTH));
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/* Resize key cache */
|
||||
/*
|
||||
TODO: uncomment when resize will be implemented
|
||||
int ha_resize_pagecache(PAGECACHE *pagecache)
|
||||
{
|
||||
DBUG_ENTER("ha_resize_pagecache");
|
||||
|
||||
if (pagecache->inited)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
long tmp_buff_size= (long) pagecache->param_buff_size;
|
||||
long tmp_block_size= (long) pagecache->param_block_size;
|
||||
uint division_limit= pagecache->param_division_limit;
|
||||
uint age_threshold= pagecache->param_age_threshold;
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
DBUG_RETURN(!resize_pagecache(pagecache, tmp_block_size,
|
||||
tmp_buff_size,
|
||||
division_limit, age_threshold));
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* Change parameters for key cache (like size) */
|
||||
|
||||
int ha_change_pagecache_param(PAGECACHE *pagecache)
|
||||
{
|
||||
if (pagecache->inited)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
uint division_limit= pagecache->param_division_limit;
|
||||
uint age_threshold= pagecache->param_age_threshold;
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
change_pagecache_param(pagecache, division_limit, age_threshold);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free memory allocated by a key cache */
|
||||
|
||||
int ha_end_pagecache(PAGECACHE *pagecache)
|
||||
{
|
||||
end_pagecache(pagecache, 1); // Can never fail
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Move all tables from one key cache to another one */
|
||||
|
||||
int ha_change_pagecache(PAGECACHE *old_pagecache,
|
||||
PAGECACHE *new_pagecache)
|
||||
{
|
||||
maria_change_pagecache(old_pagecache, new_pagecache);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
/** @brief
|
||||
Try to discover one table from handler(s)
|
||||
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
#pragma interface /* gcc class implementation */
|
||||
#endif
|
||||
|
||||
#include <my_handler.h>
|
||||
#include <ft_global.h>
|
||||
#include <keycache.h>
|
||||
#include "../storage/maria/ma_pagecache.h"
|
||||
|
||||
#ifndef NO_HASH
|
||||
#define NO_HASH /* Not yet implemented */
|
||||
|
@ -272,6 +274,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
|
||||
};
|
||||
|
@ -851,7 +854,8 @@ typedef struct st_ha_check_opt
|
|||
ulong sort_buffer_size;
|
||||
uint flags; /* isam layer flags (e.g. for myisamchk) */
|
||||
uint sql_flags; /* sql layer flags - for something myisamchk cannot do */
|
||||
KEY_CACHE *key_cache; /* new key cache when changing key cache */
|
||||
KEY_CACHE *key_cache; /* new key cache when changing key cache */
|
||||
PAGECACHE *pagecache; /* new pagecache when changing pagecache */
|
||||
void init();
|
||||
} HA_CHECK_OPT;
|
||||
|
||||
|
@ -1806,6 +1810,15 @@ int ha_resize_key_cache(KEY_CACHE *key_cache);
|
|||
int ha_change_key_cache_param(KEY_CACHE *key_cache);
|
||||
int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache);
|
||||
int ha_end_key_cache(KEY_CACHE *key_cache);
|
||||
/* pagecache */
|
||||
int ha_init_pagecache(const char *name, PAGECACHE *pagecache);
|
||||
/*
|
||||
TODO: uncomment when resizing will be implemented
|
||||
int ha_resize_pagecache(PAGECACHE *pagecache);
|
||||
*/
|
||||
int ha_change_pagecache_param(PAGECACHE *pagecache);
|
||||
int ha_change_pagecache(PAGECACHE *old_pagecache, PAGECACHE *new_pagecache);
|
||||
int ha_end_pagecache(PAGECACHE *pagecache);
|
||||
|
||||
/* report to InnoDB that control passes to the client */
|
||||
int ha_release_temporary_latches(THD *thd);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <hash.h>
|
||||
#include <time.h>
|
||||
#include <ft_global.h>
|
||||
#include <my_bit.h>
|
||||
|
||||
#include "sp_head.h"
|
||||
#include "sp_rcontext.h"
|
||||
|
|
|
@ -1368,7 +1368,6 @@ public:
|
|||
|
||||
|
||||
/* for fulltext search */
|
||||
#include <ft_global.h>
|
||||
|
||||
class Item_func_match :public Item_real_func
|
||||
{
|
||||
|
|
|
@ -2413,6 +2413,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;
|
||||
|
|
|
@ -1644,7 +1644,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;
|
||||
|
@ -1718,6 +1718,9 @@ extern pthread_cond_t COND_global_read_lock;
|
|||
extern pthread_attr_t connection_attrib;
|
||||
extern I_List<THD> threads;
|
||||
extern I_List<NAMED_LIST> key_caches;
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
extern I_List<NAMED_LIST> pagecaches;
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
extern MY_BITMAP temp_pool;
|
||||
extern String my_empty_string;
|
||||
extern const String my_null_string;
|
||||
|
@ -1743,7 +1746,7 @@ extern uint sql_command_flags[];
|
|||
extern TYPELIB log_output_typelib;
|
||||
|
||||
/* optional things, have_* variables */
|
||||
|
||||
extern SHOW_COMP_OPTION have_maria_db;
|
||||
extern handlerton *partition_hton;
|
||||
extern handlerton *myisam_hton;
|
||||
extern handlerton *heap_hton;
|
||||
|
|
158
sql/mysqld.cc
158
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 "rpl_mi.h"
|
||||
#include "sql_repl.h"
|
||||
|
@ -27,6 +28,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"
|
||||
|
||||
|
@ -439,7 +443,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;
|
||||
|
@ -500,6 +504,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>";
|
||||
|
@ -522,6 +527,9 @@ FILE *stderror_file=0;
|
|||
|
||||
I_List<THD> threads;
|
||||
I_List<NAMED_LIST> key_caches;
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
I_List<NAMED_LIST> pagecaches;
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
Rpl_filter* rpl_filter;
|
||||
Rpl_filter* binlog_filter;
|
||||
|
||||
|
@ -1201,7 +1209,13 @@ void clean_up(bool print_message)
|
|||
tc_log->close();
|
||||
xid_cache_free();
|
||||
delete_elements(&key_caches, (void (*)(const char*, uchar*)) free_key_cache);
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
delete_elements(&pagecaches, (void (*)(const char*, gptr)) free_pagecache);
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
multi_keycache_free();
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
multi_pagecache_free();
|
||||
#endif
|
||||
free_status_vars();
|
||||
end_thr_alarm(1); /* Free allocated memory */
|
||||
my_free_open_file_info();
|
||||
|
@ -2190,6 +2204,10 @@ the problem, but since we have already crashed, something is definitely wrong\n\
|
|||
and this may fail.\n\n");
|
||||
fprintf(stderr, "key_buffer_size=%lu\n",
|
||||
(ulong) dflt_key_cache->key_cache_mem_size);
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
fprintf(stderr, "page_buffer_size=%lu\n",
|
||||
(ulong) maria_pagecache->mem_size);
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size);
|
||||
fprintf(stderr, "max_used_connections=%lu\n", max_used_connections);
|
||||
fprintf(stderr, "max_threads=%u\n", thread_scheduler.max_threads);
|
||||
|
@ -2197,6 +2215,9 @@ and this may fail.\n\n");
|
|||
fprintf(stderr, "It is possible that mysqld could use up to \n\
|
||||
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = %lu K\n\
|
||||
bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size +
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
(ulong) maria_pagecache->mem_size +
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
(global_system_variables.read_buff_size +
|
||||
global_system_variables.sortbuff_size) *
|
||||
thread_scheduler.max_threads +
|
||||
|
@ -2220,7 +2241,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 ? (uchar*) thd->thread_stack : (uchar*) 0,
|
||||
thread_stack);
|
||||
my_thread_stack_size);
|
||||
}
|
||||
if (thd)
|
||||
{
|
||||
|
@ -2379,9 +2400,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
|
||||
|
||||
|
@ -2780,6 +2801,16 @@ static int init_common_variables(const char *conf_file_name, int argc,
|
|||
get_options(&defaults_argc, defaults_argv);
|
||||
set_server_version();
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
if (!(maria_pagecache= get_or_create_pagecache(maria_pagecache_base.str,
|
||||
maria_pagecache_base.length)))
|
||||
exit(1);
|
||||
/*
|
||||
maria_pagecache->param_buff_size= maria_pagecache_var.param_buff_size;
|
||||
maria_pagecache->param_block_size= maria_block_size;
|
||||
*/
|
||||
#endif
|
||||
|
||||
DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname,
|
||||
server_version, SYSTEM_TYPE,MACHINE_TYPE));
|
||||
|
||||
|
@ -3554,6 +3585,9 @@ server.");
|
|||
|
||||
/* call ha_init_key_cache() on all key caches to init them */
|
||||
process_key_caches(&ha_init_key_cache);
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
process_pagecaches(&ha_init_pagecache);
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && !defined(EMBEDDED_LIBRARY)
|
||||
if (locked_in_memory && !getuid())
|
||||
|
@ -3744,9 +3778,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
|
||||
{
|
||||
|
@ -3757,15 +3791,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
|
||||
}
|
||||
}
|
||||
|
@ -5009,10 +5043,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,
|
||||
|
@ -5026,7 +5067,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_ERROR_LOG_FILE,
|
||||
OPT_DEFAULT_WEEK_FORMAT,
|
||||
OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
|
||||
|
@ -5980,6 +6021,40 @@ 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.",
|
||||
(uchar**) &maria_block_size,
|
||||
(uchar**) &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_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.",
|
||||
(uchar**) &global_system_variables.maria_max_sort_file_size,
|
||||
(uchar**) &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.",
|
||||
(uchar**) &global_system_variables.maria_repair_threads,
|
||||
(uchar**) &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.",
|
||||
(uchar**) &global_system_variables.maria_sort_buff_size,
|
||||
(uchar**) &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\".",
|
||||
(uchar**) &maria_stats_method_str, (uchar**) &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.",
|
||||
(uchar**) &global_system_variables.max_allowed_packet,
|
||||
|
@ -6083,12 +6158,6 @@ The minimum value for this variable is 4096.",
|
|||
(uchar**) &myisam_data_pointer_size,
|
||||
(uchar**) &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",
|
||||
(uchar**) &global_system_variables.myisam_max_extra_sort_file_size,
|
||||
(uchar**) &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.",
|
||||
(uchar**) &global_system_variables.myisam_max_sort_file_size,
|
||||
|
@ -6136,7 +6205,7 @@ The minimum value for this variable is 4096.",
|
|||
(uchar**) &global_system_variables.net_write_timeout,
|
||||
(uchar**) &max_system_variables.net_write_timeout, 0, GET_ULONG,
|
||||
REQUIRED_ARG, NET_WRITE_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0},
|
||||
{ "old", OPT_OLD_MODE, "Use compatible behavior.",
|
||||
{"old", OPT_OLD_MODE, "Use compatible behavior.",
|
||||
(uchar**) &global_system_variables.old_mode,
|
||||
(uchar**) &max_system_variables.old_mode, 0, GET_BOOL, NO_ARG,
|
||||
0, 0, 0, 0, 0, 0},
|
||||
|
@ -6154,6 +6223,27 @@ The minimum value for this variable is 4096.",
|
|||
(uchar**) &global_system_variables.optimizer_search_depth,
|
||||
(uchar**) &max_system_variables.optimizer_search_depth,
|
||||
0, GET_ULONG, OPT_ARG, MAX_TABLES+1, 0, MAX_TABLES+2, 0, 1, 0},
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
{"pagecache_age_threshold", OPT_KEY_CACHE_AGE_THRESHOLD,
|
||||
"This characterizes the number of hits a hot block has to be untouched until it is considered aged enough to be downgraded to a warm block. This specifies the percentage ratio of that number of hits to the total number of blocks in key cache",
|
||||
(uchar**) &maria_pagecache_var.param_age_threshold,
|
||||
(uchar**) 0,
|
||||
0, (GET_ULONG | GET_ASK_ADDR), REQUIRED_ARG,
|
||||
300, 100, ~0L, 0, 100, 0},
|
||||
{"pagecache_buffer_size", OPT_KEY_BUFFER_SIZE,
|
||||
"The size of the buffer used for index blocks for MyISAM 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.",
|
||||
(uchar**) &maria_pagecache_var.param_buff_size,
|
||||
(uchar**) 0,
|
||||
0, (GET_ULL | GET_ASK_ADDR),
|
||||
REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, ~(ulong) 0, MALLOC_OVERHEAD,
|
||||
IO_SIZE, KEY_CACHE_SIZE},
|
||||
{"pagecache_division_limit", OPT_KEY_CACHE_DIVISION_LIMIT,
|
||||
"The minimum percentage of warm blocks in key cache",
|
||||
(uchar**) &maria_pagecache_var.param_division_limit,
|
||||
(uchar**) 0,
|
||||
0, (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100,
|
||||
1, 100, 0, 1, 0},
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
{"plugin_dir", OPT_PLUGIN_DIR,
|
||||
"Directory for plugins.",
|
||||
(uchar**) &opt_plugin_dir_ptr, (uchar**) &opt_plugin_dir_ptr, 0,
|
||||
|
@ -6162,7 +6252,6 @@ The minimum value for this variable is 4096.",
|
|||
"Optional colon separated list of plugins to load, where each plugin is "
|
||||
"identified by name and path to library seperated by an equals.",
|
||||
(uchar**) &opt_plugin_load, (uchar**) &opt_plugin_load, 0,
|
||||
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE,
|
||||
"The size of the buffer that is allocated when preloading indexes",
|
||||
(uchar**) &global_system_variables.preload_buff_size,
|
||||
|
@ -6310,8 +6399,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.", (uchar**) &thread_stack,
|
||||
(uchar**) &thread_stack, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK,
|
||||
"The stack size for each thread.", (uchar**) &my_thread_stack_size,
|
||||
(uchar**) &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).",
|
||||
|
@ -6324,12 +6413,12 @@ The minimum value for this variable is 4096.",
|
|||
(uchar**) &max_system_variables.tmp_table_size, 0, GET_ULL,
|
||||
REQUIRED_ARG, 16*1024*1024L, 1024, MAX_MEM_TABLE_SIZE, 0, 1, 0},
|
||||
{"transaction_alloc_block_size", OPT_TRANS_ALLOC_BLOCK_SIZE,
|
||||
"Allocation block size for various transaction-related structures",
|
||||
"Allocation block size for transactions to be stored in binary log",
|
||||
(uchar**) &global_system_variables.trans_alloc_block_size,
|
||||
(uchar**) &max_system_variables.trans_alloc_block_size, 0, GET_ULONG,
|
||||
REQUIRED_ARG, QUERY_ALLOC_BLOCK_SIZE, 1024, ~0L, 0, 1024, 0},
|
||||
{"transaction_prealloc_size", OPT_TRANS_PREALLOC_SIZE,
|
||||
"Persistent buffer for various transaction-related structures",
|
||||
"Persistent buffer for transactions to be stored in binary log",
|
||||
(uchar**) &global_system_variables.trans_prealloc_size,
|
||||
(uchar**) &max_system_variables.trans_prealloc_size, 0, GET_ULONG,
|
||||
REQUIRED_ARG, TRANS_ALLOC_PREALLOC_SIZE, 1024, ~0L, 0, 1024, 0},
|
||||
|
@ -7075,15 +7164,25 @@ 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();
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
pagecaches.empty();
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
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 */
|
||||
|
||||
/* set key_cache_hash.default_value = dflt_key_cache */
|
||||
multi_keycache_init();
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
/* set pagecache_hash.default_value = maria_pagecache */
|
||||
multi_pagecache_init();
|
||||
#endif
|
||||
|
||||
/* Set directory paths */
|
||||
strmake(language, LANGUAGE, sizeof(language)-1);
|
||||
|
@ -7126,6 +7225,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
|
||||
|
@ -7657,7 +7757,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
|||
int method;
|
||||
LINT_INIT(method_conv);
|
||||
|
||||
myisam_stats_method_str= argument;
|
||||
method= find_type_or_exit(argument, &myisam_stats_method_typelib,
|
||||
opt->name);
|
||||
switch (method-1) {
|
||||
|
@ -8131,6 +8230,9 @@ void refresh_status(THD *thd)
|
|||
|
||||
/* Reset the counters of all key caches (default and named). */
|
||||
process_key_caches(reset_key_cache_counters);
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
process_pagecaches(reset_pagecache_counters);
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
pthread_mutex_unlock(&LOCK_status);
|
||||
|
||||
/*
|
||||
|
|
|
@ -4299,7 +4299,6 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info,
|
|||
}
|
||||
|
||||
info->out_rows *= selectivity_mult;
|
||||
DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost));
|
||||
|
||||
if (is_cpk_scan)
|
||||
{
|
||||
|
|
192
sql/set_var.cc
192
sql/set_var.cc
|
@ -54,6 +54,9 @@
|
|||
#include <thr_alarm.h>
|
||||
#include <myisam.h>
|
||||
#include <my_dir.h>
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
#include <maria.h>
|
||||
#endif
|
||||
|
||||
#include "events.h"
|
||||
|
||||
|
@ -113,6 +116,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);
|
||||
|
@ -121,6 +125,9 @@ static void fix_thd_mem_root(THD *thd, enum_var_type type);
|
|||
static void fix_trans_mem_root(THD *thd, enum_var_type type);
|
||||
static void fix_server_id(THD *thd, enum_var_type type);
|
||||
static KEY_CACHE *create_key_cache(const char *name, uint length);
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
static PAGECACHE *create_pagecache(const char *name, uint length);
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
void fix_sql_mode_var(THD *thd, enum_var_type type);
|
||||
static uchar *get_error_count(THD *thd);
|
||||
static uchar *get_warning_count(THD *thd);
|
||||
|
@ -238,6 +245,14 @@ static sys_var_key_cache_long sys_key_cache_division_limit(&vars, "key_cache_div
|
|||
static sys_var_key_cache_long sys_key_cache_age_threshold(&vars, "key_cache_age_threshold",
|
||||
offsetof(KEY_CACHE,
|
||||
param_age_threshold));
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
sys_var_pagecache_long sys_pagecache_division_limit("pagecache_division_limit",
|
||||
offsetof(PAGECACHE,
|
||||
param_division_limit));
|
||||
sys_var_pagecache_long sys_pagecache_age_threshold("pagecache_age_threshold",
|
||||
offsetof(KEY_CACHE,
|
||||
param_age_threshold));
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
static sys_var_bool_ptr sys_local_infile(&vars, "local_infile",
|
||||
&opt_local_infile);
|
||||
static sys_var_trust_routine_creators
|
||||
|
@ -330,6 +345,14 @@ static sys_var_thd_enum sys_myisam_stats_method(&vars, "myisam_stats_met
|
|||
&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);
|
||||
|
||||
static sys_var_thd_ulong sys_net_buffer_length(&vars, "net_buffer_length",
|
||||
&SV::net_buffer_length);
|
||||
static sys_var_thd_ulong sys_net_read_timeout(&vars, "net_read_timeout",
|
||||
|
@ -624,6 +647,7 @@ static sys_var_have_plugin sys_have_csv(&vars, "have_csv", C_STRING_WITH_LEN("cs
|
|||
static sys_var_have_variable sys_have_dlopen(&vars, "have_dynamic_loading", &have_dlopen);
|
||||
static sys_var_have_variable sys_have_geometry(&vars, "have_geometry", &have_geometry);
|
||||
static sys_var_have_plugin sys_have_innodb(&vars, "have_innodb", C_STRING_WITH_LEN("innodb"), MYSQL_STORAGE_ENGINE_PLUGIN);
|
||||
static sys_var_have_variable sys_have_maria_db(&vars, "have_maria", &have_maria_db);
|
||||
static sys_var_have_plugin sys_have_ndbcluster(&vars, "have_ndbcluster", C_STRING_WITH_LEN("ndbcluster"), MYSQL_STORAGE_ENGINE_PLUGIN);
|
||||
static sys_var_have_variable sys_have_openssl(&vars, "have_openssl", &have_ssl);
|
||||
static sys_var_have_variable sys_have_ssl(&vars, "have_ssl", &have_ssl);
|
||||
|
@ -702,7 +726,7 @@ static SHOW_VAR fixed_vars[]= {
|
|||
#ifdef HAVE_THR_SETCONCURRENCY
|
||||
{"thread_concurrency", (char*) &concurrency, SHOW_LONG},
|
||||
#endif
|
||||
{"thread_stack", (char*) &thread_stack, SHOW_LONG},
|
||||
{"thread_stack", (char*) &my_thread_stack_size, SHOW_LONG},
|
||||
};
|
||||
|
||||
|
||||
|
@ -839,6 +863,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
|
||||
*/
|
||||
|
@ -1893,15 +1927,30 @@ LEX_STRING default_key_cache_base= {(char *) "default", 7 };
|
|||
|
||||
static KEY_CACHE zero_key_cache;
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
LEX_STRING maria_pagecache_base= {(char *) "default", 7 };
|
||||
static PAGECACHE zero_pagecache;
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
KEY_CACHE *get_key_cache(LEX_STRING *cache_name)
|
||||
{
|
||||
safe_mutex_assert_owner(&LOCK_global_system_variables);
|
||||
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));
|
||||
}
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
PAGECACHE *get_pagecache(LEX_STRING *cache_name)
|
||||
{
|
||||
safe_mutex_assert_owner(&LOCK_global_system_variables);
|
||||
if (!cache_name || ! cache_name->length)
|
||||
cache_name= &default_key_cache_base;
|
||||
return ((PAGECACHE*) find_named(&pagecaches,
|
||||
cache_name->str, cache_name->length, 0));
|
||||
}
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
uchar *sys_var_key_cache_param::value_ptr(THD *thd, enum_var_type type,
|
||||
LEX_STRING *base)
|
||||
|
@ -1913,6 +1962,18 @@ uchar *sys_var_key_cache_param::value_ptr(THD *thd, enum_var_type type,
|
|||
}
|
||||
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
byte *sys_var_pagecache_param::value_ptr(THD *thd, enum_var_type type,
|
||||
LEX_STRING *base)
|
||||
{
|
||||
PAGECACHE *pagecache= get_pagecache(base);
|
||||
if (!pagecache)
|
||||
pagecache= &zero_pagecache;
|
||||
return (byte*) pagecache + offset ;
|
||||
}
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
|
||||
bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
|
||||
{
|
||||
ulonglong tmp= var->save_result.ulonglong_value;
|
||||
|
@ -2049,6 +2110,60 @@ end:
|
|||
}
|
||||
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
bool sys_var_pagecache_long::update(THD *thd, set_var *var)
|
||||
{
|
||||
ulong tmp= (ulong) var->value->val_int();
|
||||
LEX_STRING *base_name= &var->base;
|
||||
bool error= 0;
|
||||
|
||||
if (!base_name->length)
|
||||
base_name= &maria_pagecache_base;
|
||||
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
PAGECACHE *pagecache= get_pagecache(base_name);
|
||||
|
||||
if (!pagecache && !(pagecache= create_pagecache(base_name->str,
|
||||
base_name->length)))
|
||||
{
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
Abort if some other thread is changing the key cache
|
||||
TODO: This should be changed so that we wait until the previous
|
||||
assignment is done and then do the new assign
|
||||
*/
|
||||
if (pagecache->in_init)
|
||||
goto end;
|
||||
|
||||
*((ulong*) (((char*) pagecache) + offset))=
|
||||
(ulong) getopt_ull_limit_value(tmp, option_limits);
|
||||
|
||||
/*
|
||||
Don't create a new key cache if it didn't exist
|
||||
(pagecaches are created only when the user sets block_size)
|
||||
*/
|
||||
pagecache->in_init= 1;
|
||||
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
|
||||
/*
|
||||
TODO: uncomment whan it will be implemented
|
||||
error= (bool) (ha_resize_pagecache(pagecache));
|
||||
*/
|
||||
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
pagecache->in_init= 0;
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
return error;
|
||||
}
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
|
||||
bool sys_var_log_state::update(THD *thd, set_var *var)
|
||||
{
|
||||
bool res= 0;
|
||||
|
@ -3538,12 +3653,85 @@ bool process_key_caches(int (* func) (const char *name, KEY_CACHE *))
|
|||
}
|
||||
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
|
||||
static PAGECACHE *create_pagecache(const char *name, uint length)
|
||||
{
|
||||
PAGECACHE *pagecache;
|
||||
DBUG_ENTER("create_pagecache");
|
||||
DBUG_PRINT("enter",("name: %.*s", length, name));
|
||||
|
||||
if ((pagecache= (PAGECACHE*) my_malloc(sizeof(PAGECACHE),
|
||||
MYF(MY_ZEROFILL | MY_WME))))
|
||||
{
|
||||
if (!new NAMED_LIST(&pagecaches, name, length, (gptr) pagecache))
|
||||
{
|
||||
my_free((char*) pagecache, MYF(0));
|
||||
pagecache= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Set default values for a key cache
|
||||
The values in maria_pagecache_var is set by my_getopt() at startup
|
||||
We don't set 'buff_size' as this is used to enable the key cache
|
||||
*/
|
||||
pagecache->param_buff_size= (maria_pagecache_var.param_buff_size ?
|
||||
maria_pagecache_var.param_buff_size:
|
||||
KEY_CACHE_SIZE);
|
||||
pagecache->param_division_limit= maria_pagecache_var.param_division_limit;
|
||||
pagecache->param_age_threshold= maria_pagecache_var.param_age_threshold;
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(pagecache);
|
||||
}
|
||||
|
||||
|
||||
PAGECACHE *get_or_create_pagecache(const char *name, uint length)
|
||||
{
|
||||
LEX_STRING pagecache_name;
|
||||
PAGECACHE *pagecache;
|
||||
|
||||
pagecache_name.str= (char *) name;
|
||||
pagecache_name.length= length;
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
if (!(pagecache= get_pagecache(&pagecache_name)))
|
||||
pagecache= create_pagecache(name, length);
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
return pagecache;
|
||||
}
|
||||
|
||||
|
||||
void free_pagecache(const char *name, PAGECACHE *pagecache)
|
||||
{
|
||||
ha_end_pagecache(pagecache);
|
||||
my_free((char*) pagecache, MYF(0));
|
||||
}
|
||||
|
||||
|
||||
bool process_pagecaches(int (* func) (const char *name, PAGECACHE *))
|
||||
{
|
||||
I_List_iterator<NAMED_LIST> it(pagecaches);
|
||||
NAMED_LIST *element;
|
||||
|
||||
while ((element= it++))
|
||||
{
|
||||
PAGECACHE *pagecache= (PAGECACHE *) element->data;
|
||||
func(element->name, pagecache);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
|
||||
void sys_var_trust_routine_creators::warn_deprecated(THD *thd)
|
||||
{
|
||||
WARN_DEPRECATED(thd, "5.2", "log_bin_trust_routine_creators",
|
||||
"'log_bin_trust_function_creators'");
|
||||
}
|
||||
|
||||
|
||||
void sys_var_trust_routine_creators::set_default(THD *thd, enum_var_type type)
|
||||
{
|
||||
warn_deprecated(thd);
|
||||
|
|
|
@ -733,6 +733,33 @@ public:
|
|||
};
|
||||
|
||||
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
class sys_var_pagecache_param :public sys_var
|
||||
{
|
||||
protected:
|
||||
size_t offset;
|
||||
public:
|
||||
sys_var_pagecache_param(const char *name_arg, size_t offset_arg)
|
||||
:sys_var(name_arg), offset(offset_arg)
|
||||
{}
|
||||
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
|
||||
bool check_default(enum_var_type type) { return 1; }
|
||||
bool is_struct() { return 1; }
|
||||
};
|
||||
|
||||
|
||||
class sys_var_pagecache_long :public sys_var_pagecache_param
|
||||
{
|
||||
public:
|
||||
sys_var_pagecache_long(const char *name_arg, size_t offset_arg)
|
||||
:sys_var_pagecache_param(name_arg, offset_arg)
|
||||
{}
|
||||
bool update(THD *thd, set_var *var);
|
||||
SHOW_TYPE type() { return SHOW_LONG; }
|
||||
};
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
|
||||
class sys_var_thd_date_time_format :public sys_var_thd
|
||||
{
|
||||
DATE_TIME_FORMAT *SV::*offset;
|
||||
|
@ -1131,7 +1158,11 @@ public:
|
|||
my_free((uchar*) name, MYF(0));
|
||||
}
|
||||
friend bool process_key_caches(int (* func) (const char *name,
|
||||
KEY_CACHE *));
|
||||
KEY_CACHE *));
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
friend bool process_pagecaches(int (* func) (const char *name,
|
||||
PAGECACHE *));
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
friend void delete_elements(I_List<NAMED_LIST> *list,
|
||||
void (*free_element)(const char*, uchar*));
|
||||
};
|
||||
|
@ -1141,6 +1172,9 @@ 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;
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
extern LEX_STRING maria_pagecache_base;
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
||||
/* For sql_yacc */
|
||||
struct sys_var_with_base
|
||||
|
@ -1182,3 +1216,8 @@ void free_key_cache(const char *name, KEY_CACHE *key_cache);
|
|||
bool process_key_caches(int (* func) (const char *name, KEY_CACHE *));
|
||||
void delete_elements(I_List<NAMED_LIST> *list,
|
||||
void (*free_element)(const char*, uchar*));
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
PAGECACHE *get_or_create_pagecache(const char *name, uint length);
|
||||
void free_pagecache(const char *name, PAGECACHE *pagecache);
|
||||
bool process_pagecaches(int (* func) (const char *name, PAGECACHE *));
|
||||
#endif /* WITH_MARIA_STORAGE_ENGINE */
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include <sql_common.h>
|
||||
#include <errmsg.h>
|
||||
|
||||
static Log_event* next_event(RELAY_LOG_INFO* rli);
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
|
||||
#include "rpl_tblmap.h"
|
||||
|
|
|
@ -236,6 +236,7 @@ struct system_variables
|
|||
uint dynamic_variables_size; /* how many bytes are in use */
|
||||
|
||||
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;
|
||||
|
@ -251,6 +252,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;
|
||||
|
|
|
@ -5053,10 +5053,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>
|
||||
|
||||
|
@ -9547,7 +9548,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
|
|||
table->s= share;
|
||||
init_tmp_table_share(share, "", 0, tmpname, tmppath);
|
||||
share->blob_field= blob_field;
|
||||
share->blob_ptr_size= mi_portable_sizeof_char_ptr;
|
||||
share->blob_ptr_size= portable_sizeof_char_ptr;
|
||||
share->db_low_byte_first=1; // True for HEAP and MyISAM
|
||||
share->table_charset= param->table_charset;
|
||||
share->primary_key= MAX_KEY; // Indicate no primary key
|
||||
|
@ -10112,7 +10113,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
|
|||
table->s= share;
|
||||
share->blob_field= blob_field;
|
||||
share->fields= field_count;
|
||||
share->blob_ptr_size= mi_portable_sizeof_char_ptr;
|
||||
share->blob_ptr_size= portable_sizeof_char_ptr;
|
||||
setup_tmp_table_column_bitmaps(table, bitmaps);
|
||||
|
||||
/* Create all fields and calculate the total length of record */
|
||||
|
|
|
@ -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();
|
||||
|
@ -536,7 +536,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::dump_internal_status();
|
||||
|
|
|
@ -448,7 +448,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(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
|
165
storage/maria/Makefile.am
Normal file
165
storage/maria/Makefile.am
Normal file
|
@ -0,0 +1,165 @@
|
|||
# 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; version 2 of the License.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
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_loghandler.h ma_loghandler_lsn.h ma_pagecache.h \
|
||||
ma_commit.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 \
|
||||
ma_pagecache.c ma_pagecaches.c \
|
||||
ma_commit.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.%
|
48
storage/maria/ft_maria.c
Normal file
48
storage/maria/ft_maria.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* 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
|
||||
};
|
||||
|
2275
storage/maria/ha_maria.cc
Normal file
2275
storage/maria/ha_maria.cc
Normal file
File diff suppressed because it is too large
Load diff
153
storage/maria/ha_maria.h
Normal file
153
storage/maria/ha_maria.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
|
||||
#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;
|
||||
enum data_file_type data_file_type;
|
||||
bool can_enable_indexes;
|
||||
/**
|
||||
@brief for temporarily disabling table's transactionality
|
||||
(if THD::transaction::on is false), remember the original value here
|
||||
*/
|
||||
bool save_transactional;
|
||||
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;
|
||||
uint max_supported_key_part_length() const
|
||||
{ return max_supported_key_length(); }
|
||||
enum row_type get_row_type() const;
|
||||
uint checksum() const;
|
||||
virtual double scan_time();
|
||||
|
||||
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_end(void);
|
||||
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 start_stmt(THD *thd, thr_lock_type 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
|
||||
};
|
786
storage/maria/lockman.c
Normal file
786
storage/maria/lockman.c
Normal file
|
@ -0,0 +1,786 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
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_direct_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
|
76
storage/maria/lockman.h
Normal file
76
storage/maria/lockman.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#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
|
2003
storage/maria/ma_bitmap.c
Normal file
2003
storage/maria/ma_bitmap.c
Normal file
File diff suppressed because it is too large
Load diff
3995
storage/maria/ma_blockrec.c
Normal file
3995
storage/maria/ma_blockrec.c
Normal file
File diff suppressed because it is too large
Load diff
180
storage/maria/ma_blockrec.h
Normal file
180
storage/maria/ma_blockrec.h
Normal file
|
@ -0,0 +1,180 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
Storage of records in block
|
||||
*/
|
||||
|
||||
#define LSN_SIZE 7
|
||||
#define DIR_COUNT_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 + DIR_COUNT_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 */
|
||||
/* Number of extents reserved MARIA_BITMAP_BLOCKS to store head part */
|
||||
#define ELEMENTS_RESERVED_FOR_MAIN_PART 4
|
||||
/* Fields before 'row->null_field_lengths' used by find_where_to_split_row */
|
||||
#define EXTRA_LENGTH_FIELDS 3
|
||||
|
||||
/* Size for the different parts in the row header (and head page) */
|
||||
|
||||
#define FLAG_SIZE 1
|
||||
#define TRANSID_SIZE 6
|
||||
#define VERPTR_SIZE 7
|
||||
#define DIR_ENTRY_SIZE 4
|
||||
#define FIELD_OFFSET_SIZE 2 /* size of pointers to field starts */
|
||||
|
||||
/* 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_COUNT_OFFSET LSN_SIZE+PAGE_TYPE_SIZE
|
||||
#define EMPTY_SPACE_OFFSET (DIR_COUNT_OFFSET + DIR_COUNT_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 ********/
|
||||
|
||||
/* Minium size of tail segment */
|
||||
#define MIN_TAIL_SIZE 32
|
||||
|
||||
/*
|
||||
Fixed length part of Max possible header size; See row data structure
|
||||
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 + \
|
||||
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 */
|
||||
/* We stored data on disk in the block */
|
||||
#define BLOCKUSED_USED 1
|
||||
/* Bitmap on disk is block->org_bitmap_value ; Happens only on update */
|
||||
#define BLOCKUSED_USE_ORG_BITMAP 2
|
||||
/* We stored tail data on disk for the block */
|
||||
#define BLOCKUSED_TAIL 4
|
||||
|
||||
/******* defines that affects allocation (density) of data *******/
|
||||
|
||||
/*
|
||||
If the tail part (from the main block or a blob) would use more than 75 % of
|
||||
the size of page, store the tail on a full page instead of a shared
|
||||
tail page.
|
||||
*/
|
||||
#define MAX_TAIL_SIZE(block_size) ((block_size) *3 / 4)
|
||||
|
||||
/* Don't allocate memory for too many row extents on the stack */
|
||||
#define ROW_EXTENTS_ON_STACK 32
|
||||
|
||||
extern uchar maria_bitmap_marker[2];
|
||||
|
||||
/* Functions to convert MARIA_RECORD_POS to/from page:offset */
|
||||
|
||||
static inline MARIA_RECORD_POS ma_recordpos(ulonglong page, uint dir_entry)
|
||||
{
|
||||
DBUG_ASSERT(dir_entry <= 255);
|
||||
return (MARIA_RECORD_POS) ((page << 8) | dir_entry);
|
||||
}
|
||||
|
||||
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_dir_entry(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_record(MARIA_SHARE *share, File dfile);
|
||||
my_bool _ma_once_end_block_record(MARIA_SHARE *share);
|
||||
my_bool _ma_init_block_record(MARIA_HA *info);
|
||||
void _ma_end_block_record(MARIA_HA *info);
|
||||
|
||||
my_bool _ma_update_block_record(MARIA_HA *info, MARIA_RECORD_POS pos,
|
||||
const byte *oldrec, const byte *newrec);
|
||||
my_bool _ma_delete_block_record(MARIA_HA *info, const byte *record);
|
||||
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_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);
|
||||
void _ma_bitmap_delete_all(MARIA_SHARE *share);
|
107
storage/maria/ma_cache.c
Normal file
107
storage/maria/ma_cache.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
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 */
|
33
storage/maria/ma_changed.c
Normal file
33
storage/maria/ma_changed.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* 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);
|
||||
}
|
5211
storage/maria/ma_check.c
Normal file
5211
storage/maria/ma_check.c
Normal file
File diff suppressed because it is too large
Load diff
428
storage/maria/ma_checkpoint.c
Normal file
428
storage/maria/ma_checkpoint.c
Normal file
|
@ -0,0 +1,428 @@
|
|||
/* 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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
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
|
||||
}
|
34
storage/maria/ma_checkpoint.h
Normal file
34
storage/maria/ma_checkpoint.h
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; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
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 */
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue