mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
Merge gbichot3.local:/home/mysql_src/mysql-5.1-clean
into gbichot3.local:/home/mysql_src/mysql-maria BitKeeper/etc/ignore: auto-union BUILD/SETUP.sh: Auto merged Makefile.am: Auto merged configure.in: Auto merged config/ac-macros/plugins.m4: Auto merged storage/myisammrg/ha_myisammrg.h: Auto merged unittest/Makefile.am: Auto merged BitKeeper/triggers/post-commit: merge include/my_base.h: merge libmysqld/Makefile.am: merge mysql-test/mysql-test-run.pl: merge mysys/Makefile.am: merge sql/Makefile.am: merge sql/handler.h: merge sql/item_func.h: merge sql/mysql_priv.h: merge sql/mysqld.cc: merge sql/set_var.cc: merge sql/set_var.h: merge sql/sql_class.h: merge storage/csv/ha_tina.cc: merge storage/myisam/Makefile.am: merge storage/myisam/ha_myisam.cc: merge storage/myisam/ha_myisam.h: merge storage/myisam/mi_check.c: merge storage/myisam/mi_delete.c: merge storage/myisam/mi_dynrec.c: merge storage/myisam/mi_packrec.c: merge storage/myisam/mi_unique.c: merge storage/myisam/mi_write.c: merge storage/myisam/myisamdef.h: merge
This commit is contained in:
13
.bzrignore
13
.bzrignore
@@ -2356,6 +2356,19 @@ storage/innobase/ut/.deps/ut0rnd.Po
|
||||
storage/innobase/ut/.deps/ut0ut.Po
|
||||
storage/innobase/ut/.deps/ut0vec.Po
|
||||
storage/innobase/ut/.deps/ut0wqueue.Po
|
||||
storage/maria/*.MAD
|
||||
storage/maria/*.MAI
|
||||
storage/maria/ma_rt_test
|
||||
storage/maria/ma_sp_test
|
||||
storage/maria/ma_test1
|
||||
storage/maria/ma_test2
|
||||
storage/maria/ma_test3
|
||||
storage/maria/ma_test_all
|
||||
storage/maria/maria.log
|
||||
storage/maria/maria_chk
|
||||
storage/maria/maria_ftdump
|
||||
storage/maria/maria_log
|
||||
storage/maria/maria_pack
|
||||
storage/myisam/.deps/ft_boolean_search.Po
|
||||
storage/myisam/.deps/ft_nlq_search.Po
|
||||
storage/myisam/.deps/ft_parser.Po
|
||||
|
||||
@@ -142,8 +142,6 @@ base_configs="$base_configs --with-extra-charsets=complex "
|
||||
base_configs="$base_configs --enable-thread-safe-client --with-readline "
|
||||
base_configs="$base_configs --with-big-tables"
|
||||
|
||||
static_link="--with-mysqld-ldflags=-all-static "
|
||||
static_link="$static_link --with-client-ldflags=-all-static"
|
||||
# we need local-infile in all binaries for rpl000001
|
||||
# if you need to disable local-infile in the client, write a build script
|
||||
# and unset local_infile_configs
|
||||
|
||||
@@ -5,7 +5,7 @@ FROM=$USER@mysql.com
|
||||
COMMITS=commits@lists.mysql.com
|
||||
DOCS=docs-commit@mysql.com
|
||||
LIMIT=10000
|
||||
VERSION="5.1"
|
||||
VERSION="maria"
|
||||
BKROOT=`bk root`
|
||||
|
||||
if [ -x /usr/sbin/sendmail ]; then
|
||||
@@ -68,55 +68,6 @@ EOF
|
||||
) > $BKROOT/BitKeeper/tmp/dev_public.txt
|
||||
|
||||
$SENDMAIL -t < $BKROOT/BitKeeper/tmp/dev_public.txt
|
||||
|
||||
#++
|
||||
# commits@ mail
|
||||
#--
|
||||
echo "Notifying commits list at $COMMITS"
|
||||
(
|
||||
cat <<EOF
|
||||
List-ID: <bk.mysql-$VERSION>
|
||||
From: $FROM
|
||||
To: $COMMITS
|
||||
Subject: bk commit into $VERSION tree ($CHANGESET)$BS
|
||||
X-CSetKey: <$CSETKEY>
|
||||
$BH
|
||||
Below is the list of changes that have just been committed into a local
|
||||
$VERSION repository of $USER. When $USER does a push these changes will
|
||||
be propagated to the main repository and, within 24 hours after the
|
||||
push, to the public repository.
|
||||
For information on how to access the public repository
|
||||
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
|
||||
|
||||
EOF
|
||||
bk changes -v -r+
|
||||
bk cset -r+ -d
|
||||
) | bk sed -e ${LIMIT}q > $BKROOT/BitKeeper/tmp/commits.txt
|
||||
|
||||
$SENDMAIL -t < $BKROOT/BitKeeper/tmp/commits.txt
|
||||
|
||||
#++
|
||||
# docs-commit@ mail
|
||||
# Picks up anything under the Docs subdirectory (relevant for docs team).
|
||||
#--
|
||||
bk changes -v -r+ | grep -q " Docs/"
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "Notifying docs list at $DOCS"
|
||||
(
|
||||
cat <<EOF
|
||||
List-ID: <bk.mysql-$VERSION>
|
||||
From: $FROM
|
||||
To: $DOCS
|
||||
Subject: bk commit - $VERSION tree (Manual) ($CHANGESET)$BS
|
||||
|
||||
EOF
|
||||
bk changes -v -r+
|
||||
bk cset -r+ -d
|
||||
) > $BKROOT/BitKeeper/tmp/docs.txt
|
||||
$SENDMAIL -t < $BKROOT/BitKeeper/tmp/docs.txt
|
||||
fi
|
||||
|
||||
else
|
||||
echo "commit failed because '$BK_STATUS', you may need to re-clone..."
|
||||
fi
|
||||
|
||||
@@ -2386,7 +2386,6 @@ AC_SUBST(readline_basedir)
|
||||
AC_SUBST(readline_link)
|
||||
AC_SUBST(readline_h_ln_cmd)
|
||||
|
||||
|
||||
# If we have threads generate some library functions and test programs
|
||||
sql_server_dirs=
|
||||
sql_server=
|
||||
|
||||
@@ -24,7 +24,7 @@ pkginclude_HEADERS = my_dbug.h m_string.h my_sys.h my_list.h my_xml.h \
|
||||
sslopt-vars.h sslopt-case.h sql_common.h keycache.h \
|
||||
mysql_time.h mysql/plugin.h $(BUILT_SOURCES)
|
||||
noinst_HEADERS = config-win.h config-netware.h \
|
||||
heap.h my_bitmap.h my_uctype.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 \
|
||||
@@ -33,7 +33,7 @@ noinst_HEADERS = config-win.h config-netware.h \
|
||||
mysql_version.h.in my_handler.h my_time.h decimal.h \
|
||||
my_vle.h my_user.h my_atomic.h atomic/nolock.h \
|
||||
atomic/rwlock.h atomic/x86-gcc.h atomic/x86-msvc.h \
|
||||
my_libwrap.h
|
||||
my_libwrap.h pagecache.h
|
||||
|
||||
# mysql_version.h are generated
|
||||
CLEANFILES = mysql_version.h my_config.h readline openssl
|
||||
|
||||
@@ -66,6 +66,17 @@ void ft_free_stopwords(void);
|
||||
FT_INFO *ft_init_search(uint,void *, uint, byte *, uint,CHARSET_INFO *, byte *);
|
||||
my_bool ft_boolean_check_syntax_string(const byte *);
|
||||
|
||||
/* Internal symbols for fulltext between maria and MyISAM */
|
||||
|
||||
#define HA_FT_WTYPE HA_KEYTYPE_FLOAT
|
||||
#define HA_FT_WLEN 4
|
||||
#define FT_SEGS 2
|
||||
|
||||
#define ft_sintXkorr(A) mi_sint4korr(A)
|
||||
#define ft_intXstore(T,A) mi_int4store(T,A)
|
||||
|
||||
extern const HA_KEYSEG ft_keysegs[FT_SEGS];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -128,7 +128,8 @@ extern void end_key_cache(KEY_CACHE *keycache, my_bool cleanup);
|
||||
/* Functions to handle multiple key caches */
|
||||
extern my_bool multi_keycache_init(void);
|
||||
extern void multi_keycache_free(void);
|
||||
extern KEY_CACHE *multi_key_cache_search(byte *key, uint length);
|
||||
extern KEY_CACHE *multi_key_cache_search(byte *key, uint length,
|
||||
KEY_CACHE *def);
|
||||
extern my_bool multi_key_cache_set(const byte *key, uint length,
|
||||
KEY_CACHE *key_cache);
|
||||
extern void multi_key_cache_change(KEY_CACHE *old_data,
|
||||
|
||||
416
include/maria.h
Normal file
416
include/maria.h
Normal file
@@ -0,0 +1,416 @@
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* This file should be included when using maria_funktions */
|
||||
|
||||
#ifndef _maria_h
|
||||
#define _maria_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#ifndef _my_base_h
|
||||
#include <my_base.h>
|
||||
#endif
|
||||
#ifndef _m_ctype_h
|
||||
#include <m_ctype.h>
|
||||
#endif
|
||||
#ifndef _keycache_h
|
||||
#include "keycache.h"
|
||||
#endif
|
||||
#include "my_handler.h"
|
||||
#include "ft_global.h"
|
||||
#include <myisamchk.h>
|
||||
#include <mysql/plugin.h>
|
||||
|
||||
/*
|
||||
Limit max keys according to HA_MAX_POSSIBLE_KEY; See myisamchk.h for details
|
||||
*/
|
||||
|
||||
#if MAX_INDEXES > HA_MAX_POSSIBLE_KEY
|
||||
#define MARIA_MAX_KEY HA_MAX_POSSIBLE_KEY /* Max allowed keys */
|
||||
#else
|
||||
#define MARIA_MAX_KEY MAX_INDEXES /* Max allowed keys */
|
||||
#endif
|
||||
|
||||
#define MARIA_MAX_MSG_BUF 1024 /* used in CHECK TABLE, REPAIR TABLE */
|
||||
#define MARIA_NAME_IEXT ".MAI"
|
||||
#define MARIA_NAME_DEXT ".MAD"
|
||||
/* Max extra space to use when sorting keys */
|
||||
#define MARIA_MAX_TEMP_LENGTH 2*1024L*1024L*1024L
|
||||
/* Possible values for maria_block_size (must be power of 2) */
|
||||
#define MARIA_KEY_BLOCK_LENGTH 8192 /* default key block length */
|
||||
#define MARIA_MIN_KEY_BLOCK_LENGTH 1024 /* Min key block length */
|
||||
#define MARIA_MAX_KEY_BLOCK_LENGTH 32768
|
||||
#define maria_portable_sizeof_char_ptr 8
|
||||
|
||||
/*
|
||||
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 struct st_maria_isaminfo /* Struct from h_info */
|
||||
{
|
||||
ha_rows records; /* Records in database */
|
||||
ha_rows deleted; /* Deleted records in database */
|
||||
my_off_t recpos; /* Pos for last used record */
|
||||
my_off_t newrecpos; /* Pos if we write new record */
|
||||
my_off_t dupp_key_pos; /* Position to record with dup key */
|
||||
my_off_t data_file_length; /* Length of data file */
|
||||
my_off_t max_data_file_length, index_file_length;
|
||||
my_off_t max_index_file_length, delete_length;
|
||||
ulong reclength; /* Recordlength */
|
||||
ulong mean_reclength; /* Mean recordlength (if packed) */
|
||||
ulonglong auto_increment;
|
||||
ulonglong key_map; /* Which keys are used */
|
||||
char *data_file_name, *index_file_name;
|
||||
uint keys; /* Number of keys in use */
|
||||
uint options; /* HA_OPTION_... used */
|
||||
int errkey, /* With key was dupplicated on err */
|
||||
sortkey; /* clustered by this key */
|
||||
File filenr; /* (uniq) filenr for datafile */
|
||||
time_t create_time; /* When table was created */
|
||||
time_t check_time;
|
||||
time_t update_time;
|
||||
uint reflength;
|
||||
ulong record_offset;
|
||||
ulong *rec_per_key; /* for sql optimizing */
|
||||
} MARIA_INFO;
|
||||
|
||||
|
||||
typedef struct st_maria_create_info
|
||||
{
|
||||
const char *index_file_name, *data_file_name; /* If using symlinks */
|
||||
ha_rows max_rows;
|
||||
ha_rows reloc_rows;
|
||||
ulonglong auto_increment;
|
||||
ulonglong data_file_length;
|
||||
ulonglong key_file_length;
|
||||
uint old_options;
|
||||
uint8 language;
|
||||
my_bool with_auto_increment;
|
||||
} 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) */
|
||||
uint16 block_size_index; /* block_size (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, uchar *page, uchar *key,
|
||||
uint key_len, uint comp_flag, uchar **ret_pos,
|
||||
uchar *buff, my_bool *was_last_key);
|
||||
uint(*get_key) (struct st_maria_keydef *keyinfo, uint nod_flag,
|
||||
uchar **page, uchar *key);
|
||||
int(*pack_key) (struct st_maria_keydef *keyinfo, uint nod_flag,
|
||||
uchar *next_key, uchar *org_key, uchar *prev_key,
|
||||
uchar *key, struct st_maria_s_param *s_temp);
|
||||
void(*store_key) (struct st_maria_keydef *keyinfo, uchar *key_pos,
|
||||
struct st_maria_s_param *s_temp);
|
||||
int(*ck_insert) (struct st_maria_info *inf, uint k_nr, uchar *k, uint klen);
|
||||
int(*ck_delete) (struct st_maria_info *inf, uint k_nr, uchar *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 */
|
||||
uchar 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 */
|
||||
{
|
||||
int16 type; /* en_fieldtype */
|
||||
uint16 length; /* length of field */
|
||||
uint32 offset; /* Offset to position in row */
|
||||
uint8 null_bit; /* If column may be 0 */
|
||||
uint16 null_pos; /* position for null marker */
|
||||
|
||||
#ifndef NOT_PACKED_DATABASES
|
||||
void(*unpack) (struct st_maria_columndef *rec,
|
||||
struct st_maria_bit_buff *buff,
|
||||
uchar *start, uchar *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_delay_key_write, maria_single_user;
|
||||
extern my_off_t maria_max_temp_length;
|
||||
extern ulong maria_bulk_insert_tree_size, maria_data_pointer_size;
|
||||
extern KEY_CACHE maria_key_cache_var, *maria_key_cache;
|
||||
|
||||
|
||||
/* Prototypes for maria-functions */
|
||||
|
||||
extern int maria_init(void);
|
||||
extern void maria_end(void);
|
||||
extern int maria_close(struct st_maria_info *file);
|
||||
extern int maria_delete(struct st_maria_info *file, const byte *buff);
|
||||
extern struct st_maria_info *maria_open(const char *name, int mode,
|
||||
uint wait_if_locked);
|
||||
extern int maria_panic(enum ha_panic_function function);
|
||||
extern int maria_rfirst(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rkey(struct st_maria_info *file, byte *buf, int inx,
|
||||
const byte *key,
|
||||
uint key_len, enum ha_rkey_function search_flag);
|
||||
extern int maria_rlast(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rnext(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rnext_same(struct st_maria_info *info, byte *buf);
|
||||
extern int maria_rprev(struct st_maria_info *file, byte *buf, int inx);
|
||||
extern int maria_rrnd(struct st_maria_info *file, byte *buf, my_off_t pos);
|
||||
extern int maria_scan_init(struct st_maria_info *file);
|
||||
extern int maria_scan(struct st_maria_info *file, byte *buf);
|
||||
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, my_off_t 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 my_off_t 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, 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 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_KEYDEF *keyinfo;
|
||||
MARIA_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 maria_update_key_parts for
|
||||
description.
|
||||
*/
|
||||
ulonglong unique[HA_MAX_KEY_SEG+1];
|
||||
ulonglong notnull[HA_MAX_KEY_SEG+1];
|
||||
|
||||
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;
|
||||
|
||||
int (*key_cmp)(struct st_maria_sort_param *, const void *, const void *);
|
||||
int (*key_read)(struct st_maria_sort_param *,void *);
|
||||
int (*key_write)(struct st_maria_sort_param *, const void *);
|
||||
void (*lock_in_memory)(HA_CHECK *);
|
||||
NEAR int (*write_keys)(struct st_maria_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_maria_sort_param *, IO_CACHE *,char *,
|
||||
uint, uint);
|
||||
} MARIA_SORT_PARAM;
|
||||
|
||||
|
||||
/* functions in maria_check */
|
||||
void mariachk_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, uint raid_chunks, myf myflags);
|
||||
void maria_lock_memory(HA_CHECK *param);
|
||||
int maria_update_state_info(HA_CHECK *param, MARIA_HA *info, uint update);
|
||||
void maria_update_key_parts(MARIA_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
||||
ulonglong *unique, ulonglong *notnull,
|
||||
ulonglong records);
|
||||
int maria_filecopy(HA_CHECK *param, File to, File from, my_off_t start,
|
||||
my_off_t length, const char *type);
|
||||
int maria_movepoint(MARIA_HA *info, byte *record, my_off_t oldpos,
|
||||
my_off_t newpos, uint prot_key);
|
||||
int maria_write_data_suffix(MARIA_SORT_INFO *sort_info, my_bool fix_datafile);
|
||||
int maria_test_if_almost_full(MARIA_HA *info);
|
||||
int maria_recreate_table(HA_CHECK *param, MARIA_HA ** org_info, char *filename);
|
||||
int maria_disable_indexes(MARIA_HA *info);
|
||||
int maria_enable_indexes(MARIA_HA *info);
|
||||
int maria_indexes_are_disabled(MARIA_HA *info);
|
||||
void maria_disable_non_unique_index(MARIA_HA *info, ha_rows rows);
|
||||
my_bool maria_test_if_sort_rep(MARIA_HA *info, ha_rows rows, ulonglong key_map,
|
||||
my_bool force);
|
||||
|
||||
int maria_init_bulk_insert(MARIA_HA *info, ulong cache_size, ha_rows rows);
|
||||
void maria_flush_bulk_insert(MARIA_HA *info, uint inx);
|
||||
void maria_end_bulk_insert(MARIA_HA *info);
|
||||
int maria_assign_to_key_cache(MARIA_HA *info, ulonglong key_map,
|
||||
KEY_CACHE *key_cache);
|
||||
void maria_change_key_cache(KEY_CACHE *old_key_cache,
|
||||
KEY_CACHE *new_key_cache);
|
||||
int maria_preload(MARIA_HA *info, ulonglong key_map, my_bool ignore_leaves);
|
||||
|
||||
/* fulltext functions */
|
||||
FT_INFO *maria_ft_init_search(uint,void *, uint, byte *, uint,
|
||||
CHARSET_INFO *, byte *);
|
||||
|
||||
/* 'Almost-internal' Maria functions */
|
||||
|
||||
void _ma_update_auto_increment_key(HA_CHECK *param, MARIA_HA *info,
|
||||
my_bool repair);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -15,7 +15,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
|
||||
@@ -487,4 +486,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 */
|
||||
|
||||
@@ -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;
|
||||
@@ -81,7 +101,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,
|
||||
|
||||
261
include/myisam.h
261
include/myisam.h
@@ -32,33 +32,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"
|
||||
@@ -257,9 +243,6 @@ typedef struct st_columndef /* column information */
|
||||
#endif
|
||||
} MI_COLUMNDEF;
|
||||
|
||||
/* invalidator function reference for Query Cache */
|
||||
typedef void (* invalidator_by_filename)(const char * filename);
|
||||
|
||||
extern my_string myisam_log_filename; /* Name of logfile */
|
||||
extern ulong myisam_block_size;
|
||||
extern ulong myisam_concurrent_insert;
|
||||
@@ -312,195 +295,106 @@ extern int mi_delete_all_rows(struct st_myisam_info *info);
|
||||
extern ulong _mi_calc_blob_length(uint length , const byte *pos);
|
||||
extern uint mi_get_pointer_length(ulonglong file_length, uint def);
|
||||
|
||||
/* this is used to pass to mysql_myisamchk_table -- by Sasha Pachev */
|
||||
/* this is used to pass to mysql_myisamchk_table */
|
||||
|
||||
#define MYISAMCHK_REPAIR 1 /* equivalent to myisamchk -r */
|
||||
#define MYISAMCHK_VERIFY 2 /* Verify, run repair if failure */
|
||||
|
||||
/*
|
||||
Definitions needed for myisamchk.c
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#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;
|
||||
#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;
|
||||
|
||||
|
||||
/*
|
||||
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, calc_checksum;
|
||||
char temp_filename[FN_REFLEN],*isam_file_name;
|
||||
MY_TMPDIR *tmpdir;
|
||||
int tmpfile_createflag;
|
||||
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_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;
|
||||
|
||||
int (*key_cmp)(struct st_mi_sort_param *, const void *, const void *);
|
||||
int (*key_read)(struct st_mi_sort_param *,void *);
|
||||
int (*key_write)(struct st_mi_sort_param *, const void *);
|
||||
void (*lock_in_memory)(HA_CHECK *);
|
||||
NEAR int (*write_keys)(struct st_mi_sort_param *, register uchar **,
|
||||
uint , struct st_buffpek *, IO_CACHE *);
|
||||
NEAR uint (*read_to_buffer)(IO_CACHE *,struct st_buffpek *, uint);
|
||||
NEAR int (*write_key)(struct st_mi_sort_param *, IO_CACHE *,char *,
|
||||
uint, uint);
|
||||
} MI_SORT_PARAM;
|
||||
|
||||
typedef struct st_sort_info
|
||||
{
|
||||
my_off_t filelength,dupp,buff_length;
|
||||
ha_rows max_records;
|
||||
uint current_key, total_keys;
|
||||
myf myf_rw;
|
||||
enum data_file_type new_data_file_type;
|
||||
MI_INFO *info;
|
||||
MI_CHECK *param;
|
||||
char *buff;
|
||||
SORT_KEY_BLOCKS *key_block,*key_block_end;
|
||||
SORT_FT_BUF *ft_buf;
|
||||
/* sync things */
|
||||
uint got_error, threads_running;
|
||||
#ifdef THREAD
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
} SORT_INFO;
|
||||
|
||||
/* functions in mi_check */
|
||||
void myisamchk_init(MI_CHECK *param);
|
||||
int chk_status(MI_CHECK *param, MI_INFO *info);
|
||||
int chk_del(MI_CHECK *param, register MI_INFO *info, uint test_flag);
|
||||
int chk_size(MI_CHECK *param, MI_INFO *info);
|
||||
int chk_key(MI_CHECK *param, MI_INFO *info);
|
||||
int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend);
|
||||
int mi_repair(MI_CHECK *param, register MI_INFO *info,
|
||||
void myisamchk_init(HA_CHECK *param);
|
||||
int chk_status(HA_CHECK *param, MI_INFO *info);
|
||||
int chk_del(HA_CHECK *param, register MI_INFO *info, uint test_flag);
|
||||
int chk_size(HA_CHECK *param, MI_INFO *info);
|
||||
int chk_key(HA_CHECK *param, MI_INFO *info);
|
||||
int chk_data_link(HA_CHECK *param, MI_INFO *info,int extend);
|
||||
int mi_repair(HA_CHECK *param, register MI_INFO *info,
|
||||
my_string name, int rep_quick);
|
||||
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, my_string name);
|
||||
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
|
||||
int mi_sort_index(HA_CHECK *param, register MI_INFO *info, my_string name);
|
||||
int mi_repair_by_sort(HA_CHECK *param, register MI_INFO *info,
|
||||
const char * name, int rep_quick);
|
||||
int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
|
||||
int mi_repair_parallel(HA_CHECK *param, register MI_INFO *info,
|
||||
const char * name, int rep_quick);
|
||||
int change_to_newfile(const char * filename, const char * old_ext,
|
||||
const char * new_ext, uint raid_chunks,
|
||||
myf myflags);
|
||||
int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type,
|
||||
int lock_file(HA_CHECK *param, File file, my_off_t start, int lock_type,
|
||||
const char *filetype, const char *filename);
|
||||
void lock_memory(MI_CHECK *param);
|
||||
void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
|
||||
void lock_memory(HA_CHECK *param);
|
||||
void update_auto_increment_key(HA_CHECK *param, MI_INFO *info,
|
||||
my_bool repair);
|
||||
int update_state_info(MI_CHECK *param, MI_INFO *info,uint update);
|
||||
int update_state_info(HA_CHECK *param, MI_INFO *info,uint update);
|
||||
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
|
||||
ulonglong *unique, ulonglong *notnull,
|
||||
ulonglong records);
|
||||
int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
|
||||
int filecopy(HA_CHECK *param, File to,File from,my_off_t start,
|
||||
my_off_t length, const char *type);
|
||||
int movepoint(MI_INFO *info,byte *record,my_off_t oldpos,
|
||||
my_off_t newpos, uint prot_key);
|
||||
int write_data_suffix(SORT_INFO *sort_info, my_bool fix_datafile);
|
||||
int write_data_suffix(MI_SORT_INFO *sort_info, my_bool fix_datafile);
|
||||
int test_if_almost_full(MI_INFO *info);
|
||||
int recreate_table(MI_CHECK *param, MI_INFO **org_info, char *filename);
|
||||
int recreate_table(HA_CHECK *param, MI_INFO **org_info, char *filename);
|
||||
void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);
|
||||
my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows, ulonglong key_map,
|
||||
my_bool force);
|
||||
@@ -514,6 +408,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
|
||||
|
||||
160
include/myisamchk.h
Normal file
160
include/myisamchk.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/* 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 */
|
||||
{
|
||||
uchar *buff, *end_pos;
|
||||
uchar 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;
|
||||
ha_rows total_records, total_deleted;
|
||||
ha_checksum record_checksum, glob_crc;
|
||||
ha_checksum key_crc[HA_MAX_POSSIBLE_KEY];
|
||||
ulong use_buffers, read_buffer_length, write_buffer_length;
|
||||
ulong sort_buffer_length, sort_key_blocks;
|
||||
ulong rec_per_key_part[HA_MAX_KEY_SEG * HA_MAX_POSSIBLE_KEY];
|
||||
uint out_flag, warning_printed, error_printed, verbose;
|
||||
uint opt_sort_key, total_files, max_level;
|
||||
uint testflag, key_cache_block_size;
|
||||
int tmpfile_createflag;
|
||||
myf myf_rw;
|
||||
uint8 language;
|
||||
my_bool using_global_keycache, opt_lock_memory, opt_follow_links;
|
||||
my_bool retry_repair, force_sort, calc_checksum;
|
||||
char temp_filename[FN_REFLEN];
|
||||
IO_CACHE read_cache;
|
||||
enum_handler_stats_method stats_method;
|
||||
} HA_CHECK;
|
||||
|
||||
|
||||
typedef struct st_sort_ftbuf
|
||||
{
|
||||
uchar *buf, *end;
|
||||
int count;
|
||||
uchar lastkey[HA_MAX_KEY_BUFF];
|
||||
} SORT_FT_BUF;
|
||||
|
||||
|
||||
typedef struct st_buffpek {
|
||||
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;
|
||||
|
||||
#endif /* _myisamchk_h */
|
||||
226
include/pagecache.h
Normal file
226
include/pagecache.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/* Copyright (C) 2006 MySQL AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Page cache variable structures */
|
||||
|
||||
#ifndef _pagecache_h
|
||||
#define _pagecache_h
|
||||
C_MODE_START
|
||||
|
||||
/* Type of the page */
|
||||
enum pagecache_page_type
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
/* used only for control page type chenging during debugging */
|
||||
PAGECACHE_EMPTY_PAGE,
|
||||
#endif
|
||||
/* the page does not contain LSN */
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
/* the page contain LSN (maria tablespace page) */
|
||||
PAGECACHE_LSN_PAGE
|
||||
};
|
||||
|
||||
/*
|
||||
This enum describe lock status changing. every typr of page cache will
|
||||
interpret WRITE/READ lock as it need.
|
||||
*/
|
||||
enum pagecache_page_lock
|
||||
{
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED, /* free -> free */
|
||||
PAGECACHE_LOCK_LEFT_READLOCKED, /* read -> read */
|
||||
PAGECACHE_LOCK_LEFT_WRITELOCKED, /* write -> write */
|
||||
PAGECACHE_LOCK_READ, /* free -> read */
|
||||
PAGECACHE_LOCK_WRITE, /* free -> write */
|
||||
PAGECACHE_LOCK_READ_UNLOCK, /* read -> free */
|
||||
PAGECACHE_LOCK_WRITE_UNLOCK, /* write -> free */
|
||||
PAGECACHE_LOCK_WRITE_TO_READ /* write -> read */
|
||||
};
|
||||
/*
|
||||
This enum describe pin status changing
|
||||
*/
|
||||
enum pagecache_page_pin
|
||||
{
|
||||
PAGECACHE_PIN_LEFT_PINNED, /* pinned -> pinned */
|
||||
PAGECACHE_PIN_LEFT_UNPINNED, /* unpinned -> unpinned */
|
||||
PAGECACHE_PIN, /* unpinned -> pinned */
|
||||
PAGECACHE_UNPIN /* pinned -> unpinned */
|
||||
};
|
||||
/* How to write the page */
|
||||
enum pagecache_write_mode
|
||||
{
|
||||
/* do not write immediately, i.e. it will be dirty page */
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
/* write page to the file and put it to the cache */
|
||||
PAGECACHE_WRITE_NOW,
|
||||
/* page already is in the file. (key cache insert analogue) */
|
||||
PAGECACHE_WRITE_DONE
|
||||
};
|
||||
|
||||
typedef void *PAGECACHE_PAGE_LINK;
|
||||
|
||||
/* TODO: move to loghandler emulator */
|
||||
typedef void LOG_HANDLER;
|
||||
typedef void *LSN;
|
||||
|
||||
/* file descriptor for Maria */
|
||||
typedef struct st_pagecache_file
|
||||
{
|
||||
int file; /* it is for debugging purposes then it will be uint32 file_no */
|
||||
} PAGECACHE_FILE;
|
||||
|
||||
/* page number for maria */
|
||||
typedef uint32 maria_page_no_t;
|
||||
|
||||
/* declare structures that is used by st_pagecache */
|
||||
|
||||
struct st_pagecache_block_link;
|
||||
typedef struct st_pagecache_block_link PAGECACHE_BLOCK_LINK;
|
||||
struct st_pagecache_page;
|
||||
typedef struct st_pagecache_page PAGECACHE_PAGE;
|
||||
struct st_pagecache_hash_link;
|
||||
typedef struct st_pagecache_hash_link PAGECACHE_HASH_LINK;
|
||||
|
||||
/* info about requests in a waiting queue */
|
||||
typedef struct st_pagecache_wqueue
|
||||
{
|
||||
struct st_my_thread_var *last_thread; /* circular list of waiting threads */
|
||||
} PAGECACHE_WQUEUE;
|
||||
|
||||
#define PAGECACHE_CHANGED_BLOCKS_HASH 128 /* must be power of 2 */
|
||||
|
||||
/*
|
||||
The page cache structure
|
||||
It also contains read-only statistics parameters.
|
||||
*/
|
||||
|
||||
typedef struct st_pagecache
|
||||
{
|
||||
my_bool inited;
|
||||
my_bool resize_in_flush; /* true during flush of resize operation */
|
||||
my_bool can_be_used; /* usage of cache for read/write is allowed */
|
||||
uint shift; /* block size = 2 ^ shift */
|
||||
my_size_t mem_size; /* specified size of the cache memory */
|
||||
uint32 block_size; /* size of the page buffer of a cache block */
|
||||
ulong min_warm_blocks; /* min number of warm blocks; */
|
||||
ulong age_threshold; /* age threshold for hot blocks */
|
||||
ulonglong time; /* total number of block link operations */
|
||||
uint hash_entries; /* max number of entries in the hash table */
|
||||
int hash_links; /* max number of hash links */
|
||||
int hash_links_used; /* number of hash links currently used */
|
||||
int disk_blocks; /* max number of blocks in the cache */
|
||||
ulong blocks_used; /* maximum number of concurrently used blocks */
|
||||
ulong blocks_unused; /* number of currently unused blocks */
|
||||
ulong blocks_changed; /* number of currently dirty blocks */
|
||||
ulong warm_blocks; /* number of blocks in warm sub-chain */
|
||||
ulong cnt_for_resize_op; /* counter to block resize operation */
|
||||
long blocks_available; /* number of blocks available in the LRU chain */
|
||||
PAGECACHE_HASH_LINK **hash_root;/* arr. of entries into hash table buckets */
|
||||
PAGECACHE_HASH_LINK *hash_link_root;/* memory for hash table links */
|
||||
PAGECACHE_HASH_LINK *free_hash_list;/* list of free hash links */
|
||||
PAGECACHE_BLOCK_LINK *free_block_list;/* list of free blocks */
|
||||
PAGECACHE_BLOCK_LINK *block_root;/* memory for block links */
|
||||
byte HUGE_PTR *block_mem; /* memory for block buffers */
|
||||
PAGECACHE_BLOCK_LINK *used_last;/* ptr to the last block of the LRU chain */
|
||||
PAGECACHE_BLOCK_LINK *used_ins;/* ptr to the insertion block in LRU chain */
|
||||
pthread_mutex_t cache_lock; /* to lock access to the cache structure */
|
||||
PAGECACHE_WQUEUE resize_queue; /* threads waiting during resize operation */
|
||||
PAGECACHE_WQUEUE waiting_for_hash_link;/* waiting for a free hash link */
|
||||
PAGECACHE_WQUEUE waiting_for_block; /* requests waiting for a free block */
|
||||
/* hash for dirty file bl.*/
|
||||
PAGECACHE_BLOCK_LINK *changed_blocks[PAGECACHE_CHANGED_BLOCKS_HASH];
|
||||
/* hash for other file bl.*/
|
||||
PAGECACHE_BLOCK_LINK *file_blocks[PAGECACHE_CHANGED_BLOCKS_HASH];
|
||||
|
||||
LOG_HANDLER *loghandler; /* loghandler structure */
|
||||
|
||||
/*
|
||||
The following variables are and variables used to hold parameters for
|
||||
initializing the key cache.
|
||||
*/
|
||||
|
||||
ulonglong param_buff_size; /* size the memory allocated for the cache */
|
||||
ulong param_block_size; /* size of the blocks in the key cache */
|
||||
ulong param_division_limit; /* min. percentage of warm blocks */
|
||||
ulong param_age_threshold; /* determines when hot block is downgraded */
|
||||
|
||||
/* Statistics variables. These are reset in reset_key_cache_counters(). */
|
||||
ulong global_blocks_changed; /* number of currently dirty blocks */
|
||||
ulonglong global_cache_w_requests;/* number of write requests (write hits) */
|
||||
ulonglong global_cache_write; /* number of writes from cache to files */
|
||||
ulonglong global_cache_r_requests;/* number of read requests (read hits) */
|
||||
ulonglong global_cache_read; /* number of reads from files to cache */
|
||||
|
||||
int blocks; /* max number of blocks in the cache */
|
||||
my_bool in_init; /* Set to 1 in MySQL during init/resize */
|
||||
} PAGECACHE;
|
||||
|
||||
extern int init_pagecache(PAGECACHE *pagecache, my_size_t use_mem,
|
||||
uint division_limit, uint age_threshold,
|
||||
uint block_size,
|
||||
LOG_HANDLER *loghandler);
|
||||
extern int resize_pagecache(PAGECACHE *pagecache,
|
||||
my_size_t use_mem, uint division_limit,
|
||||
uint age_threshold);
|
||||
extern void change_pagecache_param(PAGECACHE *pagecache, uint division_limit,
|
||||
uint age_threshold);
|
||||
extern byte *pagecache_read(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
maria_page_no_t pageno,
|
||||
uint level,
|
||||
byte *buff,
|
||||
enum pagecache_page_type type,
|
||||
enum pagecache_page_lock lock,
|
||||
PAGECACHE_PAGE_LINK *link);
|
||||
extern my_bool pagecache_write(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
maria_page_no_t pageno,
|
||||
uint level,
|
||||
byte *buff,
|
||||
enum pagecache_page_type type,
|
||||
enum pagecache_page_lock lock,
|
||||
enum pagecache_page_pin pin,
|
||||
enum pagecache_write_mode write_mode,
|
||||
PAGECACHE_PAGE_LINK *link);
|
||||
void pagecache_unlock_page(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
maria_page_no_t pageno,
|
||||
enum pagecache_page_lock lock,
|
||||
enum pagecache_page_pin pin,
|
||||
my_bool stamp_this_page,
|
||||
LSN first_REDO_LSN_for_page);
|
||||
void pagecache_unlock(PAGECACHE *pagecache,
|
||||
PAGECACHE_PAGE_LINK *link,
|
||||
enum pagecache_page_lock lock,
|
||||
enum pagecache_page_pin pin,
|
||||
my_bool stamp_this_page,
|
||||
LSN first_REDO_LSN_for_page);
|
||||
void pagecache_unpin_page(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
maria_page_no_t pageno);
|
||||
void pagecache_unpin(PAGECACHE *pagecache,
|
||||
PAGECACHE_PAGE_LINK *link);
|
||||
extern int flush_pagecache_blocks(PAGECACHE *keycache,
|
||||
PAGECACHE_FILE *file,
|
||||
enum flush_type type);
|
||||
my_bool pagecache_delete_page(PAGECACHE *pagecache,
|
||||
PAGECACHE_FILE *file,
|
||||
maria_page_no_t pageno,
|
||||
enum pagecache_page_lock lock,
|
||||
my_bool flush);
|
||||
extern void end_pagecache(PAGECACHE *keycache, my_bool cleanup);
|
||||
|
||||
C_MODE_END
|
||||
#endif /* _keycache_h */
|
||||
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;
|
||||
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
|
||||
1589
mysql-test/r/maria.result
Normal file
1589
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
910
mysql-test/t/maria.test
Normal file
910
mysql-test/t/maria.test
Normal file
@@ -0,0 +1,910 @@
|
||||
#
|
||||
# Test bugs in the MARIA code
|
||||
#
|
||||
-- source include/have_maria.inc
|
||||
|
||||
let $default=`select @@global.storage_engine`;
|
||||
set global storage_engine=maria;
|
||||
set session storage_engine=maria;
|
||||
|
||||
# Initialise
|
||||
--disable_warnings
|
||||
drop table if exists t1,t2;
|
||||
--enable_warnings
|
||||
SET SQL_WARNINGS=1;
|
||||
|
||||
#
|
||||
# Test problem with CHECK TABLE;
|
||||
#
|
||||
|
||||
CREATE TABLE t1 (
|
||||
STRING_DATA char(255) default NULL,
|
||||
KEY string_data (STRING_DATA)
|
||||
);
|
||||
|
||||
INSERT INTO t1 VALUES ('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
|
||||
INSERT INTO t1 VALUES ('DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD');
|
||||
INSERT INTO t1 VALUES ('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
|
||||
INSERT INTO t1 VALUES ('FGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG');
|
||||
INSERT INTO t1 VALUES ('HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH');
|
||||
INSERT INTO t1 VALUES ('WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW');
|
||||
CHECK TABLE t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test problem with rows that are 65517-65520 bytes long
|
||||
#
|
||||
|
||||
create table t1 (a tinyint not null auto_increment, b blob not null, primary key (a));
|
||||
|
||||
let $1=100;
|
||||
disable_query_log;
|
||||
--disable_warnings
|
||||
SET SQL_WARNINGS=0;
|
||||
while ($1)
|
||||
{
|
||||
eval insert into t1 (b) values(repeat(char(65+$1),65550-$1));
|
||||
dec $1;
|
||||
}
|
||||
SET SQL_WARNINGS=1;
|
||||
--enable_warnings
|
||||
enable_query_log;
|
||||
check table t1;
|
||||
repair table t1;
|
||||
delete from t1 where (a & 1);
|
||||
check table t1;
|
||||
repair table t1;
|
||||
check table t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test bug: Two optimize in a row reset index cardinality
|
||||
#
|
||||
|
||||
create table t1 (a int not null auto_increment, b int not null, primary key (a), index(b));
|
||||
insert into t1 (b) values (1),(2),(2),(2),(2);
|
||||
optimize table t1;
|
||||
show index from t1;
|
||||
optimize table t1;
|
||||
show index from t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test of how ORDER BY works when doing it on the whole table
|
||||
#
|
||||
|
||||
create table t1 (a int not null, b int not null, c int not null, primary key (a),key(b));
|
||||
insert into t1 values (3,3,3),(1,1,1),(2,2,2),(4,4,4);
|
||||
explain select * from t1 order by a;
|
||||
explain select * from t1 order by b;
|
||||
explain select * from t1 order by c;
|
||||
explain select a from t1 order by a;
|
||||
explain select b from t1 order by b;
|
||||
explain select a,b from t1 order by b;
|
||||
explain select a,b from t1;
|
||||
explain select a,b,c from t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test of OPTIMIZE of locked and modified tables
|
||||
#
|
||||
CREATE TABLE t1 (a INT);
|
||||
INSERT INTO t1 VALUES (1), (2), (3);
|
||||
LOCK TABLES t1 WRITE;
|
||||
INSERT INTO t1 VALUES (1), (2), (3);
|
||||
OPTIMIZE TABLE t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# Test of optimize, when only mi_sort_index (but not mi_repair*) is done
|
||||
# in ha_maria::repair, and index size is changed (decreased).
|
||||
#
|
||||
|
||||
create table t1 ( t1 char(255), key(t1(250)));
|
||||
insert t1 values ('137513751375137513751375137513751375137569516951695169516951695169516951695169');
|
||||
insert t1 values ('178417841784178417841784178417841784178403420342034203420342034203420342034203');
|
||||
insert t1 values ('213872387238723872387238723872387238723867376737673767376737673767376737673767');
|
||||
insert t1 values ('242624262426242624262426242624262426242607890789078907890789078907890789078907');
|
||||
insert t1 values ('256025602560256025602560256025602560256011701170117011701170117011701170117011');
|
||||
insert t1 values ('276027602760276027602760276027602760276001610161016101610161016101610161016101');
|
||||
insert t1 values ('281528152815281528152815281528152815281564956495649564956495649564956495649564');
|
||||
insert t1 values ('292129212921292129212921292129212921292102100210021002100210021002100210021002');
|
||||
insert t1 values ('380638063806380638063806380638063806380634483448344834483448344834483448344834');
|
||||
insert t1 values ('411641164116411641164116411641164116411616301630163016301630163016301630163016');
|
||||
insert t1 values ('420842084208420842084208420842084208420899889988998899889988998899889988998899');
|
||||
insert t1 values ('438443844384438443844384438443844384438482448244824482448244824482448244824482');
|
||||
insert t1 values ('443244324432443244324432443244324432443239613961396139613961396139613961396139');
|
||||
insert t1 values ('485448544854485448544854485448544854485477847784778477847784778477847784778477');
|
||||
insert t1 values ('494549454945494549454945494549454945494555275527552755275527552755275527552755');
|
||||
insert t1 values ('538647864786478647864786478647864786478688918891889188918891889188918891889188');
|
||||
insert t1 values ('565556555655565556555655565556555655565554845484548454845484548454845484548454');
|
||||
insert t1 values ('607860786078607860786078607860786078607856665666566656665666566656665666566656');
|
||||
insert t1 values ('640164016401640164016401640164016401640141274127412741274127412741274127412741');
|
||||
insert t1 values ('719471947194719471947194719471947194719478717871787178717871787178717871787178');
|
||||
insert t1 values ('742574257425742574257425742574257425742549604960496049604960496049604960496049');
|
||||
insert t1 values ('887088708870887088708870887088708870887035963596359635963596359635963596359635');
|
||||
insert t1 values ('917791779177917791779177917791779177917773857385738573857385738573857385738573');
|
||||
insert t1 values ('933293329332933293329332933293329332933278987898789878987898789878987898789878');
|
||||
insert t1 values ('963896389638963896389638963896389638963877807780778077807780778077807780778077');
|
||||
delete from t1 where t1>'2';
|
||||
insert t1 values ('70'), ('84'), ('60'), ('20'), ('76'), ('89'), ('49'), ('50'),
|
||||
('88'), ('61'), ('42'), ('98'), ('39'), ('30'), ('25'), ('66'), ('61'), ('48'),
|
||||
('80'), ('84'), ('98'), ('19'), ('91'), ('42'), ('47');
|
||||
optimize table t1;
|
||||
check table t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# test of maria with huge number of packed fields
|
||||
#
|
||||
|
||||
create table t1 (i1 int, i2 int, i3 int, i4 int, i5 int, i6 int, i7 int, i8
|
||||
int, i9 int, i10 int, i11 int, i12 int, i13 int, i14 int, i15 int, i16 int, i17
|
||||
int, i18 int, i19 int, i20 int, i21 int, i22 int, i23 int, i24 int, i25 int,
|
||||
i26 int, i27 int, i28 int, i29 int, i30 int, i31 int, i32 int, i33 int, i34
|
||||
int, i35 int, i36 int, i37 int, i38 int, i39 int, i40 int, i41 int, i42 int,
|
||||
i43 int, i44 int, i45 int, i46 int, i47 int, i48 int, i49 int, i50 int, i51
|
||||
int, i52 int, i53 int, i54 int, i55 int, i56 int, i57 int, i58 int, i59 int,
|
||||
i60 int, i61 int, i62 int, i63 int, i64 int, i65 int, i66 int, i67 int, i68
|
||||
int, i69 int, i70 int, i71 int, i72 int, i73 int, i74 int, i75 int, i76 int,
|
||||
i77 int, i78 int, i79 int, i80 int, i81 int, i82 int, i83 int, i84 int, i85
|
||||
int, i86 int, i87 int, i88 int, i89 int, i90 int, i91 int, i92 int, i93 int,
|
||||
i94 int, i95 int, i96 int, i97 int, i98 int, i99 int, i100 int, i101 int, i102
|
||||
int, i103 int, i104 int, i105 int, i106 int, i107 int, i108 int, i109 int, i110
|
||||
int, i111 int, i112 int, i113 int, i114 int, i115 int, i116 int, i117 int, i118
|
||||
int, i119 int, i120 int, i121 int, i122 int, i123 int, i124 int, i125 int, i126
|
||||
int, i127 int, i128 int, i129 int, i130 int, i131 int, i132 int, i133 int, i134
|
||||
int, i135 int, i136 int, i137 int, i138 int, i139 int, i140 int, i141 int, i142
|
||||
int, i143 int, i144 int, i145 int, i146 int, i147 int, i148 int, i149 int, i150
|
||||
int, i151 int, i152 int, i153 int, i154 int, i155 int, i156 int, i157 int, i158
|
||||
int, i159 int, i160 int, i161 int, i162 int, i163 int, i164 int, i165 int, i166
|
||||
int, i167 int, i168 int, i169 int, i170 int, i171 int, i172 int, i173 int, i174
|
||||
int, i175 int, i176 int, i177 int, i178 int, i179 int, i180 int, i181 int, i182
|
||||
int, i183 int, i184 int, i185 int, i186 int, i187 int, i188 int, i189 int, i190
|
||||
int, i191 int, i192 int, i193 int, i194 int, i195 int, i196 int, i197 int, i198
|
||||
int, i199 int, i200 int, i201 int, i202 int, i203 int, i204 int, i205 int, i206
|
||||
int, i207 int, i208 int, i209 int, i210 int, i211 int, i212 int, i213 int, i214
|
||||
int, i215 int, i216 int, i217 int, i218 int, i219 int, i220 int, i221 int, i222
|
||||
int, i223 int, i224 int, i225 int, i226 int, i227 int, i228 int, i229 int, i230
|
||||
int, i231 int, i232 int, i233 int, i234 int, i235 int, i236 int, i237 int, i238
|
||||
int, i239 int, i240 int, i241 int, i242 int, i243 int, i244 int, i245 int, i246
|
||||
int, i247 int, i248 int, i249 int, i250 int, i251 int, i252 int, i253 int, i254
|
||||
int, i255 int, i256 int, i257 int, i258 int, i259 int, i260 int, i261 int, i262
|
||||
int, i263 int, i264 int, i265 int, i266 int, i267 int, i268 int, i269 int, i270
|
||||
int, i271 int, i272 int, i273 int, i274 int, i275 int, i276 int, i277 int, i278
|
||||
int, i279 int, i280 int, i281 int, i282 int, i283 int, i284 int, i285 int, i286
|
||||
int, i287 int, i288 int, i289 int, i290 int, i291 int, i292 int, i293 int, i294
|
||||
int, i295 int, i296 int, i297 int, i298 int, i299 int, i300 int, i301 int, i302
|
||||
int, i303 int, i304 int, i305 int, i306 int, i307 int, i308 int, i309 int, i310
|
||||
int, i311 int, i312 int, i313 int, i314 int, i315 int, i316 int, i317 int, i318
|
||||
int, i319 int, i320 int, i321 int, i322 int, i323 int, i324 int, i325 int, i326
|
||||
int, i327 int, i328 int, i329 int, i330 int, i331 int, i332 int, i333 int, i334
|
||||
int, i335 int, i336 int, i337 int, i338 int, i339 int, i340 int, i341 int, i342
|
||||
int, i343 int, i344 int, i345 int, i346 int, i347 int, i348 int, i349 int, i350
|
||||
int, i351 int, i352 int, i353 int, i354 int, i355 int, i356 int, i357 int, i358
|
||||
int, i359 int, i360 int, i361 int, i362 int, i363 int, i364 int, i365 int, i366
|
||||
int, i367 int, i368 int, i369 int, i370 int, i371 int, i372 int, i373 int, i374
|
||||
int, i375 int, i376 int, i377 int, i378 int, i379 int, i380 int, i381 int, i382
|
||||
int, i383 int, i384 int, i385 int, i386 int, i387 int, i388 int, i389 int, i390
|
||||
int, i391 int, i392 int, i393 int, i394 int, i395 int, i396 int, i397 int, i398
|
||||
int, i399 int, i400 int, i401 int, i402 int, i403 int, i404 int, i405 int, i406
|
||||
int, i407 int, i408 int, i409 int, i410 int, i411 int, i412 int, i413 int, i414
|
||||
int, i415 int, i416 int, i417 int, i418 int, i419 int, i420 int, i421 int, i422
|
||||
int, i423 int, i424 int, i425 int, i426 int, i427 int, i428 int, i429 int, i430
|
||||
int, i431 int, i432 int, i433 int, i434 int, i435 int, i436 int, i437 int, i438
|
||||
int, i439 int, i440 int, i441 int, i442 int, i443 int, i444 int, i445 int, i446
|
||||
int, i447 int, i448 int, i449 int, i450 int, i451 int, i452 int, i453 int, i454
|
||||
int, i455 int, i456 int, i457 int, i458 int, i459 int, i460 int, i461 int, i462
|
||||
int, i463 int, i464 int, i465 int, i466 int, i467 int, i468 int, i469 int, i470
|
||||
int, i471 int, i472 int, i473 int, i474 int, i475 int, i476 int, i477 int, i478
|
||||
int, i479 int, i480 int, i481 int, i482 int, i483 int, i484 int, i485 int, i486
|
||||
int, i487 int, i488 int, i489 int, i490 int, i491 int, i492 int, i493 int, i494
|
||||
int, i495 int, i496 int, i497 int, i498 int, i499 int, i500 int, i501 int, i502
|
||||
int, i503 int, i504 int, i505 int, i506 int, i507 int, i508 int, i509 int, i510
|
||||
int, i511 int, i512 int, i513 int, i514 int, i515 int, i516 int, i517 int, i518
|
||||
int, i519 int, i520 int, i521 int, i522 int, i523 int, i524 int, i525 int, i526
|
||||
int, i527 int, i528 int, i529 int, i530 int, i531 int, i532 int, i533 int, i534
|
||||
int, i535 int, i536 int, i537 int, i538 int, i539 int, i540 int, i541 int, i542
|
||||
int, i543 int, i544 int, i545 int, i546 int, i547 int, i548 int, i549 int, i550
|
||||
int, i551 int, i552 int, i553 int, i554 int, i555 int, i556 int, i557 int, i558
|
||||
int, i559 int, i560 int, i561 int, i562 int, i563 int, i564 int, i565 int, i566
|
||||
int, i567 int, i568 int, i569 int, i570 int, i571 int, i572 int, i573 int, i574
|
||||
int, i575 int, i576 int, i577 int, i578 int, i579 int, i580 int, i581 int, i582
|
||||
int, i583 int, i584 int, i585 int, i586 int, i587 int, i588 int, i589 int, i590
|
||||
int, i591 int, i592 int, i593 int, i594 int, i595 int, i596 int, i597 int, i598
|
||||
int, i599 int, i600 int, i601 int, i602 int, i603 int, i604 int, i605 int, i606
|
||||
int, i607 int, i608 int, i609 int, i610 int, i611 int, i612 int, i613 int, i614
|
||||
int, i615 int, i616 int, i617 int, i618 int, i619 int, i620 int, i621 int, i622
|
||||
int, i623 int, i624 int, i625 int, i626 int, i627 int, i628 int, i629 int, i630
|
||||
int, i631 int, i632 int, i633 int, i634 int, i635 int, i636 int, i637 int, i638
|
||||
int, i639 int, i640 int, i641 int, i642 int, i643 int, i644 int, i645 int, i646
|
||||
int, i647 int, i648 int, i649 int, i650 int, i651 int, i652 int, i653 int, i654
|
||||
int, i655 int, i656 int, i657 int, i658 int, i659 int, i660 int, i661 int, i662
|
||||
int, i663 int, i664 int, i665 int, i666 int, i667 int, i668 int, i669 int, i670
|
||||
int, i671 int, i672 int, i673 int, i674 int, i675 int, i676 int, i677 int, i678
|
||||
int, i679 int, i680 int, i681 int, i682 int, i683 int, i684 int, i685 int, i686
|
||||
int, i687 int, i688 int, i689 int, i690 int, i691 int, i692 int, i693 int, i694
|
||||
int, i695 int, i696 int, i697 int, i698 int, i699 int, i700 int, i701 int, i702
|
||||
int, i703 int, i704 int, i705 int, i706 int, i707 int, i708 int, i709 int, i710
|
||||
int, i711 int, i712 int, i713 int, i714 int, i715 int, i716 int, i717 int, i718
|
||||
int, i719 int, i720 int, i721 int, i722 int, i723 int, i724 int, i725 int, i726
|
||||
int, i727 int, i728 int, i729 int, i730 int, i731 int, i732 int, i733 int, i734
|
||||
int, i735 int, i736 int, i737 int, i738 int, i739 int, i740 int, i741 int, i742
|
||||
int, i743 int, i744 int, i745 int, i746 int, i747 int, i748 int, i749 int, i750
|
||||
int, i751 int, i752 int, i753 int, i754 int, i755 int, i756 int, i757 int, i758
|
||||
int, i759 int, i760 int, i761 int, i762 int, i763 int, i764 int, i765 int, i766
|
||||
int, i767 int, i768 int, i769 int, i770 int, i771 int, i772 int, i773 int, i774
|
||||
int, i775 int, i776 int, i777 int, i778 int, i779 int, i780 int, i781 int, i782
|
||||
int, i783 int, i784 int, i785 int, i786 int, i787 int, i788 int, i789 int, i790
|
||||
int, i791 int, i792 int, i793 int, i794 int, i795 int, i796 int, i797 int, i798
|
||||
int, i799 int, i800 int, i801 int, i802 int, i803 int, i804 int, i805 int, i806
|
||||
int, i807 int, i808 int, i809 int, i810 int, i811 int, i812 int, i813 int, i814
|
||||
int, i815 int, i816 int, i817 int, i818 int, i819 int, i820 int, i821 int, i822
|
||||
int, i823 int, i824 int, i825 int, i826 int, i827 int, i828 int, i829 int, i830
|
||||
int, i831 int, i832 int, i833 int, i834 int, i835 int, i836 int, i837 int, i838
|
||||
int, i839 int, i840 int, i841 int, i842 int, i843 int, i844 int, i845 int, i846
|
||||
int, i847 int, i848 int, i849 int, i850 int, i851 int, i852 int, i853 int, i854
|
||||
int, i855 int, i856 int, i857 int, i858 int, i859 int, i860 int, i861 int, i862
|
||||
int, i863 int, i864 int, i865 int, i866 int, i867 int, i868 int, i869 int, i870
|
||||
int, i871 int, i872 int, i873 int, i874 int, i875 int, i876 int, i877 int, i878
|
||||
int, i879 int, i880 int, i881 int, i882 int, i883 int, i884 int, i885 int, i886
|
||||
int, i887 int, i888 int, i889 int, i890 int, i891 int, i892 int, i893 int, i894
|
||||
int, i895 int, i896 int, i897 int, i898 int, i899 int, i900 int, i901 int, i902
|
||||
int, i903 int, i904 int, i905 int, i906 int, i907 int, i908 int, i909 int, i910
|
||||
int, i911 int, i912 int, i913 int, i914 int, i915 int, i916 int, i917 int, i918
|
||||
int, i919 int, i920 int, i921 int, i922 int, i923 int, i924 int, i925 int, i926
|
||||
int, i927 int, i928 int, i929 int, i930 int, i931 int, i932 int, i933 int, i934
|
||||
int, i935 int, i936 int, i937 int, i938 int, i939 int, i940 int, i941 int, i942
|
||||
int, i943 int, i944 int, i945 int, i946 int, i947 int, i948 int, i949 int, i950
|
||||
int, i951 int, i952 int, i953 int, i954 int, i955 int, i956 int, i957 int, i958
|
||||
int, i959 int, i960 int, i961 int, i962 int, i963 int, i964 int, i965 int, i966
|
||||
int, i967 int, i968 int, i969 int, i970 int, i971 int, i972 int, i973 int, i974
|
||||
int, i975 int, i976 int, i977 int, i978 int, i979 int, i980 int, i981 int, i982
|
||||
int, i983 int, i984 int, i985 int, i986 int, i987 int, i988 int, i989 int, i990
|
||||
int, i991 int, i992 int, i993 int, i994 int, i995 int, i996 int, i997 int, i998
|
||||
int, i999 int, i1000 int, b blob) row_format=dynamic;
|
||||
insert into t1 values (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, "Sergei");
|
||||
update t1 set b=repeat('a',256);
|
||||
update t1 set i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0;
|
||||
check table t1;
|
||||
delete from t1 where i8=1;
|
||||
select i1,i2 from t1;
|
||||
check table t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test of REPAIR that once failed
|
||||
#
|
||||
CREATE TABLE `t1` (
|
||||
`post_id` mediumint(8) unsigned NOT NULL auto_increment,
|
||||
`topic_id` mediumint(8) unsigned NOT NULL default '0',
|
||||
`post_time` datetime NOT NULL default '0000-00-00 00:00:00',
|
||||
`post_text` text NOT NULL,
|
||||
`icon_url` varchar(10) NOT NULL default '',
|
||||
`sign` tinyint(1) unsigned NOT NULL default '0',
|
||||
`post_edit` varchar(150) NOT NULL default '',
|
||||
`poster_login` varchar(35) NOT NULL default '',
|
||||
`ip` varchar(15) NOT NULL default '',
|
||||
PRIMARY KEY (`post_id`),
|
||||
KEY `post_time` (`post_time`),
|
||||
KEY `ip` (`ip`),
|
||||
KEY `poster_login` (`poster_login`),
|
||||
KEY `topic_id` (`topic_id`),
|
||||
FULLTEXT KEY `post_text` (`post_text`)
|
||||
);
|
||||
|
||||
INSERT INTO t1 (post_text) VALUES ('ceci est un test'),('ceci est un test'),('ceci est un test'),('ceci est un test'),('ceci est un test');
|
||||
|
||||
REPAIR TABLE t1;
|
||||
CHECK TABLE t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test of creating table with too long key
|
||||
#
|
||||
|
||||
--error 1071
|
||||
CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), d varchar(255), e varchar(255), KEY t1 (a, b, c, d, e));
|
||||
CREATE TABLE t1 (a varchar(255), b varchar(255), c varchar(255), d varchar(255), e varchar(255));
|
||||
--error 1071
|
||||
ALTER TABLE t1 ADD INDEX t1 (a, b, c, d, e);
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# Test of cardinality of keys with NULL
|
||||
#
|
||||
|
||||
CREATE TABLE t1 (a int not null, b int, c int, key(b), key(c), key(a,b), key(c,a));
|
||||
INSERT into t1 values (0, null, 0), (0, null, 1), (0, null, 2), (0, null,3), (1,1,4);
|
||||
create table t2 (a int not null, b int, c int, key(b), key(c), key(a));
|
||||
INSERT into t2 values (1,1,1), (2,2,2);
|
||||
optimize table t1;
|
||||
show index from t1;
|
||||
explain select * from t1,t2 where t1.a=t2.a;
|
||||
explain select * from t1,t2 force index(a) where t1.a=t2.a;
|
||||
explain select * from t1 force index(a),t2 force index(a) where t1.a=t2.a;
|
||||
explain select * from t1,t2 where t1.b=t2.b;
|
||||
explain select * from t1,t2 force index(c) where t1.a=t2.a;
|
||||
explain select * from t1 where a=0 or a=2;
|
||||
explain select * from t1 force index (a) where a=0 or a=2;
|
||||
explain select * from t1 where c=1;
|
||||
explain select * from t1 use index() where c=1;
|
||||
drop table t1,t2;
|
||||
|
||||
#
|
||||
# Test bug when updating a split dynamic row where keys are not changed
|
||||
#
|
||||
|
||||
create table t1 (a int not null auto_increment primary key, b varchar(255));
|
||||
insert into t1 (b) values (repeat('a',100)),(repeat('b',100)),(repeat('c',100));
|
||||
update t1 set b=repeat(left(b,1),200) where a=1;
|
||||
delete from t1 where (a & 1)= 0;
|
||||
update t1 set b=repeat('e',200) where a=1;
|
||||
flush tables;
|
||||
check table t1;
|
||||
|
||||
#
|
||||
# check updating with keys
|
||||
#
|
||||
|
||||
disable_query_log;
|
||||
let $1 = 100;
|
||||
while ($1)
|
||||
{
|
||||
eval insert into t1 (b) values (repeat(char(($1 & 32)+65), $1));
|
||||
dec $1;
|
||||
}
|
||||
enable_query_log;
|
||||
update t1 set b=repeat(left(b,1),255) where a between 1 and 5;
|
||||
update t1 set b=repeat(left(b,1),10) where a between 32 and 43;
|
||||
update t1 set b=repeat(left(b,1),2) where a between 64 and 66;
|
||||
update t1 set b=repeat(left(b,1),65) where a between 67 and 70;
|
||||
check table t1;
|
||||
insert into t1 (b) values (repeat('z',100));
|
||||
update t1 set b="test" where left(b,1) > 'n';
|
||||
check table t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# two bugs in maria-space-stripping feature
|
||||
#
|
||||
create table t1 ( a text not null, key a (a(20)));
|
||||
insert into t1 values ('aaa '),('aaa'),('aa');
|
||||
check table t1;
|
||||
repair table t1;
|
||||
select concat(a,'.') from t1 where a='aaa';
|
||||
select concat(a,'.') from t1 where binary a='aaa';
|
||||
update t1 set a='bbb' where a='aaa';
|
||||
select concat(a,'.') from t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Third bug in the same code (BUG#2295)
|
||||
#
|
||||
|
||||
create table t1(a text not null, b text not null, c text not null, index (a(10),b(10),c(10)));
|
||||
insert into t1 values('807780', '477', '165');
|
||||
insert into t1 values('807780', '477', '162');
|
||||
insert into t1 values('807780', '472', '162');
|
||||
select * from t1 where a='807780' and b='477' and c='165';
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# space-stripping in _mi_prefix_search: BUG#5284
|
||||
#
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1 (a varchar(150) NOT NULL, KEY (a));
|
||||
INSERT t1 VALUES ("can \tcan");
|
||||
INSERT t1 VALUES ("can can");
|
||||
INSERT t1 VALUES ("can");
|
||||
SELECT * FROM t1;
|
||||
CHECK TABLE t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# Verify blob handling
|
||||
#
|
||||
create table t1 (a blob);
|
||||
insert into t1 values('a '),('a');
|
||||
select concat(a,'.') from t1 where a='a';
|
||||
select concat(a,'.') from t1 where a='a ';
|
||||
alter table t1 add key(a(2));
|
||||
select concat(a,'.') from t1 where a='a';
|
||||
select concat(a,'.') from t1 where a='a ';
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test text and unique
|
||||
#
|
||||
create table t1 (a int not null auto_increment primary key, b text not null, unique b (b(20)));
|
||||
insert into t1 (b) values ('a'),('b'),('c');
|
||||
select concat(b,'.') from t1;
|
||||
update t1 set b='b ' where a=2;
|
||||
--error 1062
|
||||
update t1 set b='b ' where a > 1;
|
||||
--error 1062
|
||||
insert into t1 (b) values ('b');
|
||||
select * from t1;
|
||||
delete from t1 where b='b';
|
||||
select a,concat(b,'.') from t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test keys with 0 segments. (Bug #3203)
|
||||
#
|
||||
create table t1 (a int not null);
|
||||
create table t2 (a int not null, primary key (a));
|
||||
insert into t1 values (1);
|
||||
insert into t2 values (1),(2);
|
||||
select sql_big_result distinct t1.a from t1,t2 order by t2.a;
|
||||
select distinct t1.a from t1,t2 order by t2.a;
|
||||
select sql_big_result distinct t1.a from t1,t2;
|
||||
explain select sql_big_result distinct t1.a from t1,t2 order by t2.a;
|
||||
explain select distinct t1.a from t1,t2 order by t2.a;
|
||||
drop table t1,t2;
|
||||
|
||||
#
|
||||
# Bug#14616 - Freshly imported table returns error 124 when using LIMIT
|
||||
#
|
||||
create table t1 (
|
||||
c1 varchar(32),
|
||||
key (c1)
|
||||
);
|
||||
alter table t1 disable keys;
|
||||
insert into t1 values ('a'), ('b');
|
||||
select c1 from t1 order by c1 limit 1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test RTREE index
|
||||
#
|
||||
--error 1235, 1289
|
||||
CREATE TABLE t1 (`a` int(11) NOT NULL default '0', `b` int(11) NOT NULL default '0', UNIQUE KEY `a` USING RTREE (`a`,`b`));
|
||||
# INSERT INTO t1 VALUES (1,1),(1,1);
|
||||
# DELETE FROM rt WHERE a<1;
|
||||
# DROP TABLE IF EXISTS t1;
|
||||
|
||||
create table t1 (a int, b varchar(200), c text not null) checksum=1;
|
||||
create table t2 (a int, b varchar(200), c text not null) checksum=0;
|
||||
insert t1 values (1, "aaa", "bbb"), (NULL, "", "ccccc"), (0, NULL, "");
|
||||
insert t2 select * from t1;
|
||||
checksum table t1, t2, t3 quick;
|
||||
checksum table t1, t2, t3;
|
||||
checksum table t1, t2, t3 extended;
|
||||
#show table status;
|
||||
drop table t1,t2;
|
||||
|
||||
create table t1 (a int, key (a));
|
||||
show keys from t1;
|
||||
alter table t1 disable keys;
|
||||
show keys from t1;
|
||||
create table t2 (a int);
|
||||
let $i=1000;
|
||||
set @@rand_seed1=31415926,@@rand_seed2=2718281828;
|
||||
--disable_query_log
|
||||
while ($i)
|
||||
{
|
||||
dec $i;
|
||||
insert t2 values (rand()*100000);
|
||||
}
|
||||
--enable_query_log
|
||||
insert t1 select * from t2;
|
||||
show keys from t1;
|
||||
alter table t1 enable keys;
|
||||
show keys from t1;
|
||||
alter table t1 engine=heap;
|
||||
alter table t1 disable keys;
|
||||
show keys from t1;
|
||||
drop table t1,t2;
|
||||
|
||||
#
|
||||
# index search for NULL in blob. Bug #4816
|
||||
#
|
||||
create table t1 ( a tinytext, b char(1), index idx (a(1),b) );
|
||||
insert into t1 values (null,''), (null,'');
|
||||
explain select count(*) from t1 where a is null;
|
||||
select count(*) from t1 where a is null;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# bug9188 - Corruption Can't open file: 'table.MYI' (errno: 145)
|
||||
#
|
||||
create table t1 (c1 int, c2 varchar(4) not null default '',
|
||||
key(c2(3))) default charset=utf8;
|
||||
insert into t1 values (1,'A'), (2, 'B'), (3, 'A');
|
||||
update t1 set c2='A B' where c1=2;
|
||||
check table t1;
|
||||
drop table t1;
|
||||
|
||||
|
||||
#
|
||||
# Bug#12296 - CHECKSUM TABLE reports 0 for the table
|
||||
# This happened if the first record was marked as deleted.
|
||||
#
|
||||
create table t1 (c1 int);
|
||||
insert into t1 values (1),(2),(3),(4);
|
||||
checksum table t1;
|
||||
delete from t1 where c1 = 1;
|
||||
create table t2 as select * from t1;
|
||||
# The following returns 0 with the bug in place.
|
||||
checksum table t1;
|
||||
# The above should give the same number as the following.
|
||||
checksum table t2;
|
||||
drop table t1, t2;
|
||||
|
||||
#
|
||||
# BUG#12232: New maria_stats_method variable.
|
||||
#
|
||||
|
||||
show variables like 'maria_stats_method';
|
||||
|
||||
create table t1 (a int, key(a));
|
||||
insert into t1 values (0),(1),(2),(3),(4);
|
||||
insert into t1 select NULL from t1;
|
||||
|
||||
# default: NULLs considered inequal
|
||||
analyze table t1;
|
||||
show index from t1;
|
||||
insert into t1 values (11);
|
||||
delete from t1 where a=11;
|
||||
check table t1;
|
||||
show index from t1;
|
||||
|
||||
# Set nulls to be equal:
|
||||
set maria_stats_method=nulls_equal;
|
||||
show variables like 'maria_stats_method';
|
||||
insert into t1 values (11);
|
||||
delete from t1 where a=11;
|
||||
|
||||
analyze table t1;
|
||||
show index from t1;
|
||||
|
||||
insert into t1 values (11);
|
||||
delete from t1 where a=11;
|
||||
|
||||
check table t1;
|
||||
show index from t1;
|
||||
|
||||
# Set nulls back to be equal
|
||||
set maria_stats_method=DEFAULT;
|
||||
show variables like 'maria_stats_method';
|
||||
insert into t1 values (11);
|
||||
delete from t1 where a=11;
|
||||
|
||||
analyze table t1;
|
||||
show index from t1;
|
||||
|
||||
insert into t1 values (11);
|
||||
delete from t1 where a=11;
|
||||
|
||||
check table t1;
|
||||
show index from t1;
|
||||
|
||||
drop table t1;
|
||||
|
||||
# WL#2609, CSC#XXXX: MARIA
|
||||
set maria_stats_method=nulls_ignored;
|
||||
show variables like 'maria_stats_method';
|
||||
|
||||
create table t1 (
|
||||
a char(3), b char(4), c char(5), d char(6),
|
||||
key(a,b,c,d)
|
||||
);
|
||||
insert into t1 values ('bcd','def1', NULL, 'zz');
|
||||
insert into t1 values ('bcd','def2', NULL, 'zz');
|
||||
insert into t1 values ('bce','def1', 'yuu', NULL);
|
||||
insert into t1 values ('bce','def2', NULL, 'quux');
|
||||
analyze table t1;
|
||||
show index from t1;
|
||||
delete from t1;
|
||||
analyze table t1;
|
||||
show index from t1;
|
||||
|
||||
set maria_stats_method=DEFAULT;
|
||||
drop table t1;
|
||||
|
||||
# BUG#13814 - key value packed incorrectly for TINYBLOBs
|
||||
|
||||
create table t1(
|
||||
cip INT NOT NULL,
|
||||
time TIME NOT NULL,
|
||||
score INT NOT NULL DEFAULT 0,
|
||||
bob TINYBLOB
|
||||
);
|
||||
|
||||
insert into t1 (cip, time) VALUES (1, '00:01'), (2, '00:02'), (3,'00:03');
|
||||
insert into t1 (cip, bob, time) VALUES (4, 'a', '00:04'), (5, 'b', '00:05'),
|
||||
(6, 'c', '00:06');
|
||||
select * from t1 where bob is null and cip=1;
|
||||
create index bug on t1 (bob(22), cip, time);
|
||||
select * from t1 where bob is null and cip=1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Bug#14980 - COUNT(*) incorrect on MARIA table with certain INDEX
|
||||
#
|
||||
create table t1 (
|
||||
id1 int not null auto_increment,
|
||||
id2 int not null default '0',
|
||||
t text not null,
|
||||
primary key (id1),
|
||||
key x (id2, t(32))
|
||||
) engine=maria; # engine clause is redundant but it's to test its parsing
|
||||
insert into t1 (id2, t) values
|
||||
(10, 'abc'), (10, 'abc'), (10, 'abc'),
|
||||
(20, 'abc'), (20, 'abc'), (20, 'def'),
|
||||
(10, 'abc'), (10, 'abc');
|
||||
select count(*) from t1 where id2 = 10;
|
||||
select count(id1) from t1 where id2 = 10;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# BUG##20357 - Got error 124 from storage engine using MIN and MAX functions
|
||||
# in queries
|
||||
#
|
||||
CREATE TABLE t1(a TINYINT, KEY(a));
|
||||
INSERT INTO t1 VALUES(1);
|
||||
SELECT MAX(a) FROM t1 IGNORE INDEX(a);
|
||||
ALTER TABLE t1 DISABLE KEYS;
|
||||
SELECT MAX(a) FROM t1;
|
||||
SELECT MAX(a) FROM t1 IGNORE INDEX(a);
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# BUG#18036 - update of table joined to self reports table as crashed
|
||||
#
|
||||
CREATE TABLE t1(a CHAR(9), b VARCHAR(7));
|
||||
INSERT INTO t1(a) VALUES('xxxxxxxxx'),('xxxxxxxxx');
|
||||
UPDATE t1 AS ta1,t1 AS ta2 SET ta1.b='aaaaaa',ta2.b='bbbbbb';
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# Test varchar
|
||||
#
|
||||
|
||||
source include/varchar.inc;
|
||||
|
||||
#
|
||||
# Some errors/warnings on create
|
||||
#
|
||||
|
||||
create table t1 (v varchar(65530), key(v));
|
||||
drop table if exists t1;
|
||||
create table t1 (v varchar(65536));
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
create table t1 (v varchar(65530) character set utf8);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
# MARIA specific varchar tests
|
||||
--error 1118
|
||||
create table t1 (v varchar(65535));
|
||||
|
||||
#
|
||||
# Test concurrent insert
|
||||
# First with static record length
|
||||
#
|
||||
set @save_concurrent_insert=@@concurrent_insert;
|
||||
set global concurrent_insert=1;
|
||||
create table t1 (a int);
|
||||
insert into t1 values (1),(2),(3),(4),(5);
|
||||
lock table t1 read local;
|
||||
connect (con1,localhost,root,,);
|
||||
connection con1;
|
||||
# Insert in table without hole
|
||||
insert into t1 values(6),(7);
|
||||
connection default;
|
||||
unlock tables;
|
||||
delete from t1 where a>=3 and a<=4;
|
||||
lock table t1 read local;
|
||||
connection con1;
|
||||
set global concurrent_insert=2;
|
||||
# Insert in table with hole -> Should insert at end
|
||||
insert into t1 values (8),(9);
|
||||
connection default;
|
||||
unlock tables;
|
||||
# Insert into hole
|
||||
insert into t1 values (10),(11),(12);
|
||||
select * from t1;
|
||||
check table t1;
|
||||
drop table t1;
|
||||
disconnect con1;
|
||||
|
||||
# Same test with dynamic record length
|
||||
create table t1 (a int, b varchar(30) default "hello");
|
||||
insert into t1 (a) values (1),(2),(3),(4),(5);
|
||||
lock table t1 read local;
|
||||
connect (con1,localhost,root,,);
|
||||
connection con1;
|
||||
# Insert in table without hole
|
||||
insert into t1 (a) values(6),(7);
|
||||
connection default;
|
||||
unlock tables;
|
||||
delete from t1 where a>=3 and a<=4;
|
||||
lock table t1 read local;
|
||||
connection con1;
|
||||
set global concurrent_insert=2;
|
||||
# Insert in table with hole -> Should insert at end
|
||||
insert into t1 (a) values (8),(9);
|
||||
connection default;
|
||||
unlock tables;
|
||||
# Insert into hole
|
||||
insert into t1 (a) values (10),(11),(12);
|
||||
select a from t1;
|
||||
check table t1;
|
||||
drop table t1;
|
||||
disconnect con1;
|
||||
set global concurrent_insert=@save_concurrent_insert;
|
||||
|
||||
|
||||
# BUG#9622 - ANALYZE TABLE and ALTER TABLE .. ENABLE INDEX produce
|
||||
# different statistics on the same table with NULL values.
|
||||
create table t1 (a int, key(a));
|
||||
|
||||
insert into t1 values (1),(2),(3),(4),(NULL),(NULL),(NULL),(NULL);
|
||||
analyze table t1;
|
||||
show keys from t1;
|
||||
|
||||
alter table t1 disable keys;
|
||||
alter table t1 enable keys;
|
||||
show keys from t1;
|
||||
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Bug#8706 - temporary table with data directory option fails
|
||||
#
|
||||
connect (session1,localhost,root,,);
|
||||
connect (session2,localhost,root,,);
|
||||
|
||||
connection session1;
|
||||
disable_query_log;
|
||||
eval create temporary table t1 (a int) data directory="$MYSQLTEST_VARDIR/tmp" select 9 a;
|
||||
enable_query_log;
|
||||
disable_result_log;
|
||||
show create table t1;
|
||||
enable_result_log;
|
||||
|
||||
connection session2;
|
||||
disable_query_log;
|
||||
eval create temporary table t1 (a int) data directory="$MYSQLTEST_VARDIR/tmp" select 99 a;
|
||||
enable_query_log;
|
||||
disable_result_log;
|
||||
show create table t1;
|
||||
enable_result_log;
|
||||
|
||||
connection default;
|
||||
create table t1 (a int) select 42 a;
|
||||
|
||||
connection session1;
|
||||
select * from t1;
|
||||
disconnect session1;
|
||||
connection session2;
|
||||
select * from t1;
|
||||
disconnect session2;
|
||||
connection default;
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
|
||||
--echo End of 4.1 tests
|
||||
|
||||
#
|
||||
# Bug#10056 - PACK_KEYS option take values greater than 1 while creating table
|
||||
#
|
||||
create table t1 (c1 int) pack_keys=0;
|
||||
create table t2 (c1 int) pack_keys=1;
|
||||
create table t3 (c1 int) pack_keys=default;
|
||||
--error 1064
|
||||
create table t4 (c1 int) pack_keys=2;
|
||||
drop table t1, t2, t3;
|
||||
|
||||
--echo End of 5.0 tests
|
||||
|
||||
#
|
||||
# Test of key_block_size
|
||||
#
|
||||
|
||||
create table t1 (a int not null, key `a` (a) key_block_size=1024);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a int not null, key `a` (a) key_block_size=2048);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a varchar(2048), key `a` (a));
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a varchar(2048), key `a` (a) key_block_size=1024);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a int not null, b varchar(2048), key (a), key(b)) key_block_size=1024;
|
||||
show create table t1;
|
||||
alter table t1 key_block_size=2048;
|
||||
show create table t1;
|
||||
alter table t1 add c int, add key (c);
|
||||
show create table t1;
|
||||
alter table t1 key_block_size=0;
|
||||
alter table t1 add d int, add key (d);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a int not null, b varchar(2048), key (a), key(b)) key_block_size=8192;
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a int not null, b varchar(2048), key (a) key_block_size=1024, key(b)) key_block_size=8192;
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a int not null, b int, key (a) key_block_size=1024, key(b) key_block_size=8192) key_block_size=16384;
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
|
||||
# Test limits and errors of key_block_size
|
||||
|
||||
create table t1 (a int not null, key `a` (a) key_block_size=512);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a varchar(2048), key `a` (a) key_block_size=1000000000000000000);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a int not null, key `a` (a) key_block_size=1025);
|
||||
show create table t1;
|
||||
drop table t1;
|
||||
|
||||
--error 1064
|
||||
create table t1 (a int not null, key key_block_size=1024 (a));
|
||||
--error 1064
|
||||
create table t1 (a int not null, key `a` key_block_size=1024 (a));
|
||||
|
||||
--echo End of 5.1 tests
|
||||
|
||||
eval set global storage_engine=$default;
|
||||
|
||||
# End of 5.2 tests
|
||||
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
|
||||
@@ -20,7 +20,8 @@ MYSQLBASEdir= $(prefix)
|
||||
INCLUDES = @ZLIB_INCLUDES@ -I$(top_builddir)/include \
|
||||
-I$(top_srcdir)/include -I$(srcdir)
|
||||
pkglib_LIBRARIES = libmysys.a
|
||||
LDADD = libmysys.a $(top_builddir)/strings/libmystrings.a
|
||||
LDADD = libmysys.a $(top_builddir)/strings/libmystrings.a \
|
||||
$(top_builddir)/dbug/libdbug.a
|
||||
noinst_HEADERS = mysys_priv.h my_static.h
|
||||
libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \
|
||||
mf_path.c mf_loadpath.c my_file.c \
|
||||
@@ -53,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 \
|
||||
mf_pagecache.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 \
|
||||
@@ -127,5 +129,61 @@ test_base64$(EXEEXT): base64.c $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN ./test_base64.c $(LDADD) $(LIBS)
|
||||
$(RM) -f ./test_base64.c
|
||||
|
||||
test_mf_pagecache.o: mf_pagecache.c ../include/pagecache.h $(LIBRARIES)
|
||||
$(CP) $(srcdir)/mf_pagecache.c test_mf_pagecache.c
|
||||
$(COMPILE) $(FLAGS) -DPAGECACHE_DEBUG -DEXTRA_DEBUG -c test_mf_pagecache.c
|
||||
|
||||
test_file.o: test_file.c test_file.h
|
||||
$(COMPILE) $(FLAGS) -DPAGECACHE_DEBUG -DEXTRA_DEBUG -c test_file.c
|
||||
|
||||
test_pagecache_single1k$(EXEEXT): test_pagecache_single.c test_mf_pagecache.o ../unittest/mytap/tap.o test_file.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DPAGE_SIZE=1024 -DEXTRA_DEBUG $(srcdir)/test_pagecache_single.c test_mf_pagecache.o ../unittest/mytap/tap.o test_file.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_single8k$(EXEEXT): test_pagecache_single.c test_mf_pagecache.o ../unittest/mytap/tap.o test_file.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DPAGE_SIZE=8192 -DEXTRA_DEBUG $(srcdir)/test_pagecache_single.c test_mf_pagecache.o ../unittest/mytap/tap.o test_file.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_single64k$(EXEEXT): test_pagecache_single.c test_mf_pagecache.o ../unittest/mytap/tap.o test_file.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DPAGE_SIZE=65536 -DEXTRA_DEBUG $(srcdir)/test_pagecache_single.c test_mf_pagecache.o ../unittest/mytap/tap.o test_file.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_single: test_pagecache_single1k$(EXEEXT) test_pagecache_single8k$(EXEEXT) test_pagecache_single64k$(EXEEXT)
|
||||
./test_pagecache_single64k$(EXEEXT)
|
||||
./test_pagecache_single8k$(EXEEXT)
|
||||
./test_pagecache_single1k$(EXEEXT)
|
||||
|
||||
test_pagecache_consist1k$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DPAGE_SIZE=1024 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist64k$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DPAGE_SIZE=65536 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist1kHC$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DTEST_HIGH_CONCURENCY -DPAGE_SIZE=1024 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist64kHC$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DTEST_HIGH_CONCURENCY -DPAGE_SIZE=65536 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist1kRD$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DTEST_READERS -DPAGE_SIZE=1024 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist64kRD$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DTEST_READERS -DPAGE_SIZE=65536 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist1kWR$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DTEST_WRITERS -DPAGE_SIZE=1024 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist64kWR$(EXEEXT): test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LIBRARIES)
|
||||
$(LINK) $(FLAGS) -DMAIN -DPAGECACHE_DEBUG -DTEST_WRITERS -DPAGE_SIZE=65536 -DEXTRA_DEBUG $(srcdir)/test_pagecache_consist.c test_mf_pagecache.o ../unittest/mytap/tap.o $(LDADD) $(LIBS)
|
||||
|
||||
test_pagecache_consist: test_pagecache_consist1k$(EXEEXT) test_pagecache_consist64k$(EXEEXT) test_pagecache_consist1kHC$(EXEEXT) test_pagecache_consist64kHC$(EXEEXT) test_pagecache_consist1kRD$(EXEEXT) test_pagecache_consist64kRD$(EXEEXT) test_pagecache_consist1kWR$(EXEEXT) test_pagecache_consist64kWR$(EXEEXT)
|
||||
./test_pagecache_consist1k$(EXEEXT)
|
||||
./test_pagecache_consist64k$(EXEEXT)
|
||||
./test_pagecache_consist1kHC$(EXEEXT)
|
||||
./test_pagecache_consist64kHC$(EXEEXT)
|
||||
./test_pagecache_consist1kRD$(EXEEXT)
|
||||
./test_pagecache_consist64kRD$(EXEEXT)
|
||||
./test_pagecache_consist1kWR$(EXEEXT)
|
||||
./test_pagecache_consist64kWR$(EXEEXT)
|
||||
|
||||
|
||||
# Don't update the files from bitkeeper
|
||||
%::SCCS/s.%
|
||||
|
||||
@@ -148,7 +148,8 @@ static void safe_hash_free(SAFE_HASH *hash)
|
||||
Return the value stored for a key or default value if no key
|
||||
*/
|
||||
|
||||
static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length)
|
||||
static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length,
|
||||
byte *def)
|
||||
{
|
||||
byte *result;
|
||||
DBUG_ENTER("safe_hash_search");
|
||||
@@ -156,7 +157,7 @@ static byte *safe_hash_search(SAFE_HASH *hash, const byte *key, uint length)
|
||||
result= hash_search(&hash->hash, key, length);
|
||||
rw_unlock(&hash->mutex);
|
||||
if (!result)
|
||||
result= hash->default_value;
|
||||
result= def;
|
||||
else
|
||||
result= ((SAFE_HASH_ENTRY*) result)->data;
|
||||
DBUG_PRINT("exit",("data: 0x%lx", result));
|
||||
@@ -316,6 +317,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
|
||||
@@ -326,11 +328,13 @@ void multi_keycache_free(void)
|
||||
key cache to use
|
||||
*/
|
||||
|
||||
KEY_CACHE *multi_key_cache_search(byte *key, uint length)
|
||||
KEY_CACHE *multi_key_cache_search(byte *key, uint length,
|
||||
KEY_CACHE *def)
|
||||
{
|
||||
if (!key_cache_hash.hash.records)
|
||||
return dflt_key_cache;
|
||||
return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length);
|
||||
return def;
|
||||
return (KEY_CACHE*) safe_hash_search(&key_cache_hash, key, length,
|
||||
(void*) def);
|
||||
}
|
||||
|
||||
|
||||
|
||||
3928
mysys/mf_pagecache.c
Executable file
3928
mysys/mf_pagecache.c
Executable file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -155,7 +155,8 @@ uint my_pwrite(int Filedes, const byte *Buffer, uint Count, my_off_t offset,
|
||||
Count-=writenbytes;
|
||||
offset+=writenbytes;
|
||||
}
|
||||
DBUG_PRINT("error",("Write only %d bytes",writenbytes));
|
||||
DBUG_PRINT("error",("Write only %d bytes, error: %d",
|
||||
writenbytes, my_errno));
|
||||
#ifndef NO_BACKGROUND
|
||||
#ifdef THREAD
|
||||
if (my_thread_var->abort)
|
||||
|
||||
70
mysys/test_file.c
Normal file
70
mysys/test_file.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "mysys_priv.h"
|
||||
#include "my_dir.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pagecache.h>
|
||||
#include "test_file.h"
|
||||
|
||||
|
||||
/*
|
||||
Check that file contance correspond to descriptor
|
||||
|
||||
SYNOPSIS
|
||||
test_file()
|
||||
file File to test
|
||||
file_name Path (and name) of file which is tested
|
||||
size size of file
|
||||
buff_size size of buffer which is enought to check the file
|
||||
desc file descriptor to check with
|
||||
|
||||
RETURN
|
||||
1 file if OK
|
||||
0 error
|
||||
*/
|
||||
|
||||
int test_file(PAGECACHE_FILE file, char *file_name,
|
||||
off_t size, size_t buff_size, struct file_desc *desc)
|
||||
{
|
||||
MY_STAT stat_buff, *stat;
|
||||
unsigned char *buffr= malloc(buff_size);
|
||||
off_t pos= 0;
|
||||
size_t byte;
|
||||
int step= 0;
|
||||
|
||||
if ((stat= my_stat(file_name, &stat_buff, MYF(0))) == NULL)
|
||||
{
|
||||
diag("Can't stat() %s (errno: %d)\n", file_name, errno);
|
||||
return 0;
|
||||
}
|
||||
if (stat->st_size != size)
|
||||
{
|
||||
diag("file %s size is %lu (should be %lu)\n",
|
||||
file_name, (ulong) stat->st_size, (ulong) size);
|
||||
return 0;
|
||||
}
|
||||
/* check content */
|
||||
my_seek(file.file, 0, SEEK_SET, MYF(0));
|
||||
while (desc[step].length != 0)
|
||||
{
|
||||
if (my_read(file.file, (char*)buffr, desc[step].length, MYF(0)) !=
|
||||
desc[step].length)
|
||||
{
|
||||
diag("Can't read %u bytes from %s (errno: %d)\n",
|
||||
(uint)desc[step].length, file_name, errno);
|
||||
return 0;
|
||||
}
|
||||
for (byte= 0; byte < desc[step].length; byte++)
|
||||
{
|
||||
if (buffr[byte] != desc[step].content)
|
||||
{
|
||||
diag("content of %s mismatch 0x%x in position %lu instead of 0x%x\n",
|
||||
file_name, (uint) buffr[byte], (ulong) (pos + byte),
|
||||
desc[step].content);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pos+= desc[step].length;
|
||||
step++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
14
mysys/test_file.h
Normal file
14
mysys/test_file.h
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
#include <pagecache.h>
|
||||
|
||||
/*
|
||||
File content descriptor
|
||||
*/
|
||||
struct file_desc
|
||||
{
|
||||
unsigned int length;
|
||||
unsigned char content;
|
||||
};
|
||||
|
||||
int test_file(PAGECACHE_FILE file, char *file_name,
|
||||
off_t size, size_t buff_size, struct file_desc *desc);
|
||||
447
mysys/test_pagecache_consist.c
Executable file
447
mysys/test_pagecache_consist.c
Executable file
@@ -0,0 +1,447 @@
|
||||
#include "mysys_priv.h"
|
||||
#include "../include/my_pthread.h"
|
||||
#include "../include/pagecache.h"
|
||||
#include <string.h>
|
||||
#include "my_dir.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "../unittest/mytap/tap.h"
|
||||
#include <my_pthread.h>
|
||||
|
||||
/*#define PAGE_SIZE 65536*/
|
||||
#define PCACHE_SIZE (PAGE_SIZE*1024*20)
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
static const char* default_dbug_option;
|
||||
#endif
|
||||
|
||||
static char *file1_name= (char*)"page_cache_test_file_1";
|
||||
static PAGECACHE_FILE file1;
|
||||
static pthread_cond_t COND_thread_count;
|
||||
static pthread_mutex_t LOCK_thread_count;
|
||||
static uint thread_count;
|
||||
static PAGECACHE pagecache;
|
||||
|
||||
#ifdef TEST_HIGH_CONCURENCY
|
||||
static uint number_of_readers= 10;
|
||||
static uint number_of_writers= 20;
|
||||
static uint number_of_tests= 30000;
|
||||
static uint record_length_limit= PAGE_SIZE/200;
|
||||
static uint number_of_pages= 20;
|
||||
static uint flush_divider= 1000;
|
||||
#else /*TEST_HIGH_CONCURENCY*/
|
||||
#ifdef TEST_READERS
|
||||
static uint number_of_readers= 10;
|
||||
static uint number_of_writers= 1;
|
||||
static uint number_of_tests= 30000;
|
||||
static uint record_length_limit= PAGE_SIZE/200;
|
||||
static uint number_of_pages= 20;
|
||||
static uint flush_divider= 1000;
|
||||
#else /*TEST_READERS*/
|
||||
#ifdef TEST_WRITERS
|
||||
static uint number_of_readers= 0;
|
||||
static uint number_of_writers= 10;
|
||||
static uint number_of_tests= 30000;
|
||||
static uint record_length_limit= PAGE_SIZE/200;
|
||||
static uint number_of_pages= 20;
|
||||
static uint flush_divider= 1000;
|
||||
#else /*TEST_WRITERS*/
|
||||
static uint number_of_readers= 10;
|
||||
static uint number_of_writers= 10;
|
||||
static uint number_of_tests= 50000;
|
||||
static uint record_length_limit= PAGE_SIZE/200;
|
||||
static uint number_of_pages= 20000;
|
||||
static uint flush_divider= 1000;
|
||||
#endif /*TEST_WRITERS*/
|
||||
#endif /*TEST_READERS*/
|
||||
#endif /*TEST_HIGH_CONCURENCY*/
|
||||
|
||||
|
||||
/* check page consistemcy */
|
||||
uint check_page(uchar *buff, ulong offset, int page_locked, int page_no,
|
||||
int tag)
|
||||
{
|
||||
uint end= sizeof(uint);
|
||||
uint num= *((uint *)buff);
|
||||
uint i;
|
||||
DBUG_ENTER("check_page");
|
||||
|
||||
for (i= 0; i < num; i++)
|
||||
{
|
||||
uint len= *((uint *)(buff + end));
|
||||
uint j;
|
||||
end+= sizeof(uint)+ sizeof(uint);
|
||||
if (len + end > PAGE_SIZE)
|
||||
{
|
||||
diag("incorrect field header #%u by offset %lu\n", i, offset + end + j);
|
||||
goto err;
|
||||
}
|
||||
for(j= 0; j < len; j++)
|
||||
{
|
||||
if (buff[end + j] != (uchar)((i+1) % 256))
|
||||
{
|
||||
diag("incorrect %lu byte\n", offset + end + j);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
end+= len;
|
||||
}
|
||||
for(i= end; i < PAGE_SIZE; i++)
|
||||
{
|
||||
if (buff[i] != 0)
|
||||
{
|
||||
int h;
|
||||
DBUG_PRINT("err",
|
||||
("byte %lu (%lu + %u), page %u (%s, end: %u, recs: %u, tag: %d) should be 0\n",
|
||||
offset + i, offset, i, page_no,
|
||||
(page_locked ? "locked" : "unlocked"),
|
||||
end, num, tag));
|
||||
diag("byte %lu (%lu + %u), page %u (%s, end: %u, recs: %u, tag: %d) should be 0\n",
|
||||
offset + i, offset, i, page_no,
|
||||
(page_locked ? "locked" : "unlocked"),
|
||||
end, num, tag);
|
||||
h= my_open("wrong_page", O_CREAT | O_TRUNC | O_RDWR, MYF(0));
|
||||
my_pwrite(h, buff, PAGE_SIZE, 0, MYF(0));
|
||||
my_close(h, MYF(0));
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(end);
|
||||
err:
|
||||
DBUG_PRINT("err", ("try to flush"));
|
||||
if (page_locked)
|
||||
{
|
||||
pagecache_delete_page(&pagecache, &file1, page_no,
|
||||
PAGECACHE_LOCK_LEFT_WRITELOCKED, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_RELEASE);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void put_rec(uchar *buff, uint end, uint len, uint tag)
|
||||
{
|
||||
uint i;
|
||||
uint num= *((uint *)buff);
|
||||
if (!len)
|
||||
len= 1;
|
||||
if (end + sizeof(uint)*2 + len > PAGE_SIZE)
|
||||
return;
|
||||
*((uint *)(buff + end))= len;
|
||||
end+= sizeof(uint);
|
||||
*((uint *)(buff + end))= tag;
|
||||
end+= sizeof(uint);
|
||||
num++;
|
||||
*((uint *)buff)= num;
|
||||
*((uint*)(buff + end))= len;
|
||||
for (i= end; i < (len + end); i++)
|
||||
{
|
||||
buff[i]= (uchar) num % 256;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Recreate and reopen a file for test
|
||||
|
||||
SYNOPSIS
|
||||
reset_file()
|
||||
file File to reset
|
||||
file_name Path (and name) of file which should be reset
|
||||
*/
|
||||
|
||||
void reset_file(PAGECACHE_FILE file, char *file_name)
|
||||
{
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_RELEASE);
|
||||
if (my_close(file1.file, MYF(0)) != 0)
|
||||
{
|
||||
diag("Got error during %s closing from close() (errno: %d)\n",
|
||||
file_name, errno);
|
||||
exit(1);
|
||||
}
|
||||
my_delete(file_name, MYF(0));
|
||||
if ((file.file= my_open(file_name,
|
||||
O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1)
|
||||
{
|
||||
diag("Got error during %s creation from open() (errno: %d)\n",
|
||||
file_name, errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void reader(int num)
|
||||
{
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
uint i;
|
||||
|
||||
for (i= 0; i < number_of_tests; i++)
|
||||
{
|
||||
uint page= rand()/(RAND_MAX/number_of_pages);
|
||||
pagecache_read(&pagecache, &file1, page, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
0);
|
||||
check_page(buffr, page * PAGE_SIZE, 0, page, -num);
|
||||
if (i % 500 == 0)
|
||||
printf("reader%d: %d\n", num, i);
|
||||
|
||||
}
|
||||
printf("reader%d: done\n", num);
|
||||
free(buffr);
|
||||
}
|
||||
|
||||
|
||||
void writer(int num)
|
||||
{
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
uint i;
|
||||
|
||||
for (i= 0; i < number_of_tests; i++)
|
||||
{
|
||||
uint end;
|
||||
uint page= rand()/(RAND_MAX/number_of_pages);
|
||||
pagecache_read(&pagecache, &file1, page, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_WRITE,
|
||||
0);
|
||||
end= check_page(buffr, page * PAGE_SIZE, 1, page, num);
|
||||
put_rec(buffr, end, rand()/(RAND_MAX/record_length_limit), num);
|
||||
pagecache_write(&pagecache, &file1, page, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_WRITE_UNLOCK,
|
||||
PAGECACHE_UNPIN,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
|
||||
if (i % flush_divider == 0)
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
if (i % 500 == 0)
|
||||
printf("writer%d: %d\n", num, i);
|
||||
}
|
||||
printf("writer%d: done\n", num);
|
||||
free(buffr);
|
||||
}
|
||||
|
||||
|
||||
static void *test_thread_reader(void *arg)
|
||||
{
|
||||
int param=*((int*) arg);
|
||||
|
||||
my_thread_init();
|
||||
DBUG_ENTER("test_reader");
|
||||
DBUG_PRINT("enter", ("param: %d", param));
|
||||
|
||||
reader(param);
|
||||
|
||||
DBUG_PRINT("info", ("Thread %s ended\n", my_thread_name()));
|
||||
pthread_mutex_lock(&LOCK_thread_count);
|
||||
thread_count--;
|
||||
VOID(pthread_cond_signal(&COND_thread_count)); /* Tell main we are ready */
|
||||
pthread_mutex_unlock(&LOCK_thread_count);
|
||||
free((gptr) arg);
|
||||
my_thread_end();
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
static void *test_thread_writer(void *arg)
|
||||
{
|
||||
int param=*((int*) arg);
|
||||
|
||||
my_thread_init();
|
||||
DBUG_ENTER("test_writer");
|
||||
DBUG_PRINT("enter", ("param: %d", param));
|
||||
|
||||
writer(param);
|
||||
|
||||
DBUG_PRINT("info", ("Thread %s ended\n", my_thread_name()));
|
||||
pthread_mutex_lock(&LOCK_thread_count);
|
||||
thread_count--;
|
||||
VOID(pthread_cond_signal(&COND_thread_count)); /* Tell main we are ready */
|
||||
pthread_mutex_unlock(&LOCK_thread_count);
|
||||
free((gptr) arg);
|
||||
my_thread_end();
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv __attribute__((unused)))
|
||||
{
|
||||
pthread_t tid;
|
||||
pthread_attr_t thr_attr;
|
||||
int *param, error, pagen;
|
||||
|
||||
MY_INIT(argv[0]);
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
#if defined(__WIN__)
|
||||
default_dbug_option= "d:t:i:O,\\test_pagecache_consist.trace";
|
||||
#else
|
||||
default_dbug_option= "d:t:i:o,/tmp/test_pagecache_consist.trace";
|
||||
#endif
|
||||
if (argc > 1)
|
||||
{
|
||||
DBUG_SET(default_dbug_option);
|
||||
DBUG_SET_INITIAL(default_dbug_option);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
DBUG_ENTER("main");
|
||||
DBUG_PRINT("info", ("Main thread: %s\n", my_thread_name()));
|
||||
if ((file1.file= my_open(file1_name,
|
||||
O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1)
|
||||
{
|
||||
fprintf(stderr, "Got error during file1 creation from open() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
DBUG_PRINT("info", ("file1: %d", file1.file));
|
||||
if (chmod(file1_name, S_IRWXU | S_IRWXG | S_IRWXO) != 0)
|
||||
{
|
||||
fprintf(stderr, "Got error during file1 chmod() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
my_pwrite(file1.file, "test file", 9, 0, MYF(0));
|
||||
|
||||
if ((error= pthread_cond_init(&COND_thread_count, NULL)))
|
||||
{
|
||||
fprintf(stderr, "COND_thread_count: %d from pthread_cond_init (errno: %d)\n",
|
||||
error, errno);
|
||||
exit(1);
|
||||
}
|
||||
if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST)))
|
||||
{
|
||||
fprintf(stderr, "LOCK_thread_count: %d from pthread_cond_init (errno: %d)\n",
|
||||
error, errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((error= pthread_attr_init(&thr_attr)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED)))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Got error: %d from pthread_attr_setdetachstate (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifndef pthread_attr_setstacksize /* void return value */
|
||||
if ((error= pthread_attr_setstacksize(&thr_attr, 65536L)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_attr_setstacksize (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_THR_SETCONCURRENCY
|
||||
VOID(thr_setconcurrency(2));
|
||||
#endif
|
||||
|
||||
my_thread_global_init();
|
||||
|
||||
|
||||
if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0,
|
||||
PAGE_SIZE, 0)) == 0)
|
||||
{
|
||||
fprintf(stderr,"Got error: init_pagecache() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
DBUG_PRINT("info", ("Page cache %d pages", pagen));
|
||||
{
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
uint i;
|
||||
memset(buffr, '\0', PAGE_SIZE);
|
||||
for (i= 0; i < number_of_pages; i++)
|
||||
{
|
||||
pagecache_write(&pagecache, &file1, i, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
}
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
free(buffr);
|
||||
}
|
||||
if ((error= pthread_mutex_lock(&LOCK_thread_count)))
|
||||
{
|
||||
fprintf(stderr,"LOCK_thread_count: %d from pthread_mutex_lock (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
while (number_of_readers != 0 || number_of_writers != 0)
|
||||
{
|
||||
if (number_of_readers != 0)
|
||||
{
|
||||
param=(int*) malloc(sizeof(int));
|
||||
*param= number_of_readers;
|
||||
if ((error= pthread_create(&tid, &thr_attr, test_thread_reader,
|
||||
(void*) param)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_create (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
thread_count++;
|
||||
number_of_readers--;
|
||||
}
|
||||
if (number_of_writers != 0)
|
||||
{
|
||||
param=(int*) malloc(sizeof(int));
|
||||
*param= number_of_writers;
|
||||
if ((error= pthread_create(&tid, &thr_attr, test_thread_writer,
|
||||
(void*) param)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_create (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
thread_count++;
|
||||
number_of_writers--;
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("info", ("Thread started"));
|
||||
pthread_mutex_unlock(&LOCK_thread_count);
|
||||
|
||||
pthread_attr_destroy(&thr_attr);
|
||||
|
||||
/* wait finishing */
|
||||
if ((error= pthread_mutex_lock(&LOCK_thread_count)))
|
||||
fprintf(stderr,"LOCK_thread_count: %d from pthread_mutex_lock\n",error);
|
||||
while (thread_count)
|
||||
{
|
||||
if ((error= pthread_cond_wait(&COND_thread_count,&LOCK_thread_count)))
|
||||
fprintf(stderr,"COND_thread_count: %d from pthread_cond_wait\n",error);
|
||||
}
|
||||
if ((error= pthread_mutex_unlock(&LOCK_thread_count)))
|
||||
fprintf(stderr,"LOCK_thread_count: %d from pthread_mutex_unlock\n",error);
|
||||
DBUG_PRINT("info", ("thread ended"));
|
||||
|
||||
end_pagecache(&pagecache, 1);
|
||||
DBUG_PRINT("info", ("Page cache ended"));
|
||||
|
||||
if (my_close(file1.file, MYF(0)) != 0)
|
||||
{
|
||||
fprintf(stderr, "Got error during file1 closing from close() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
/*my_delete(file1_name, MYF(0));*/
|
||||
my_thread_global_end();
|
||||
|
||||
DBUG_PRINT("info", ("file1 (%d) closed", file1.file));
|
||||
|
||||
DBUG_PRINT("info", ("Program end"));
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
589
mysys/test_pagecache_single.c
Normal file
589
mysys/test_pagecache_single.c
Normal file
@@ -0,0 +1,589 @@
|
||||
#include "mysys_priv.h"
|
||||
#include "../include/my_pthread.h"
|
||||
#include "../include/pagecache.h"
|
||||
#include "my_dir.h"
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "../unittest/mytap/tap.h"
|
||||
#include "test_file.h"
|
||||
|
||||
/* #define PAGE_SIZE 1024 */
|
||||
#define PCACHE_SIZE (PAGE_SIZE*1024*10)
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
static const char* default_dbug_option;
|
||||
#endif
|
||||
|
||||
static char *file1_name= (char*)"page_cache_test_file_1";
|
||||
static PAGECACHE_FILE file1;
|
||||
static pthread_cond_t COND_thread_count;
|
||||
static pthread_mutex_t LOCK_thread_count;
|
||||
static uint thread_count;
|
||||
static PAGECACHE pagecache;
|
||||
|
||||
/*
|
||||
File contance descriptors
|
||||
*/
|
||||
static struct file_desc simple_read_write_test_file[]=
|
||||
{
|
||||
{PAGE_SIZE, '\1'},
|
||||
{ 0, 0}
|
||||
};
|
||||
static struct file_desc simple_read_change_write_read_test_file[]=
|
||||
{
|
||||
{PAGE_SIZE/2, '\65'},
|
||||
{PAGE_SIZE/2, '\1'},
|
||||
{ 0, 0}
|
||||
};
|
||||
static struct file_desc simple_pin_test_file1[]=
|
||||
{
|
||||
{PAGE_SIZE*2, '\1'},
|
||||
{ 0, 0}
|
||||
};
|
||||
static struct file_desc simple_pin_test_file2[]=
|
||||
{
|
||||
{PAGE_SIZE/2, '\1'},
|
||||
{PAGE_SIZE/2, (unsigned char)129},
|
||||
{PAGE_SIZE, '\1'},
|
||||
{ 0, 0}
|
||||
};
|
||||
static struct file_desc simple_delete_forget_test_file[]=
|
||||
{
|
||||
{PAGE_SIZE, '\1'},
|
||||
{ 0, 0}
|
||||
};
|
||||
static struct file_desc simple_delete_flush_test_file[]=
|
||||
{
|
||||
{PAGE_SIZE, '\2'},
|
||||
{ 0, 0}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Recreate and reopen a file for test
|
||||
|
||||
SYNOPSIS
|
||||
reset_file()
|
||||
file File to reset
|
||||
file_name Path (and name) of file which should be reset
|
||||
*/
|
||||
|
||||
void reset_file(PAGECACHE_FILE file, char *file_name)
|
||||
{
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_RELEASE);
|
||||
if (my_close(file1.file, MYF(0)) != 0)
|
||||
{
|
||||
diag("Got error during %s closing from close() (errno: %d)\n",
|
||||
file_name, errno);
|
||||
exit(1);
|
||||
}
|
||||
my_delete(file_name, MYF(0));
|
||||
if ((file.file= my_open(file_name,
|
||||
O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1)
|
||||
{
|
||||
diag("Got error during %s creation from open() (errno: %d)\n",
|
||||
file_name, errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Write then read page, check file on disk
|
||||
*/
|
||||
|
||||
int simple_read_write_test()
|
||||
{
|
||||
unsigned char *buffw= malloc(PAGE_SIZE);
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
int res;
|
||||
DBUG_ENTER("simple_read_write_test");
|
||||
memset(buffw, '\1', PAGE_SIZE);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
pagecache_read(&pagecache, &file1, 0, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
0);
|
||||
ok((res= test(memcmp(buffr, buffw, PAGE_SIZE) == 0)),
|
||||
"Simple write-read page ");
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
ok((res&= test(test_file(file1, file1_name, PAGE_SIZE, PAGE_SIZE,
|
||||
simple_read_write_test_file))),
|
||||
"Simple write-read page file");
|
||||
if (res)
|
||||
reset_file(file1, file1_name);
|
||||
free(buffw);
|
||||
free(buffr);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Prepare page, then read (and lock), change (write new value and unlock),
|
||||
then check the page in the cache and on the disk
|
||||
*/
|
||||
int simple_read_change_write_read_test()
|
||||
{
|
||||
unsigned char *buffw= malloc(PAGE_SIZE);
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
int res;
|
||||
DBUG_ENTER("simple_read_change_write_read_test");
|
||||
/* prepare the file */
|
||||
memset(buffw, '\1', PAGE_SIZE);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
/* test */
|
||||
pagecache_read(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_WRITE,
|
||||
0);
|
||||
memset(buffw, '\65', PAGE_SIZE/2);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_WRITE_UNLOCK,
|
||||
PAGECACHE_UNPIN,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
|
||||
pagecache_read(&pagecache, &file1, 0, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
0);
|
||||
ok((res= test(memcmp(buffr, buffw, PAGE_SIZE) == 0)),
|
||||
"Simple read-change-write-read page ");
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
ok((res&= test(test_file(file1, file1_name, PAGE_SIZE, PAGE_SIZE,
|
||||
simple_read_change_write_read_test_file))),
|
||||
"Simple read-change-write-read page file");
|
||||
if (res)
|
||||
reset_file(file1, file1_name);
|
||||
free(buffw);
|
||||
free(buffr);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Prepare page, read page 0 (and pin) then write page 1 and page 0.
|
||||
Flush the file (shold flush only page 1 and return 1 (page 0 is
|
||||
still pinned).
|
||||
Check file on the disk.
|
||||
Unpin and flush.
|
||||
Check file on the disk.
|
||||
*/
|
||||
int simple_pin_test()
|
||||
{
|
||||
unsigned char *buffw= malloc(PAGE_SIZE);
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
int res;
|
||||
DBUG_ENTER("simple_pin_test");
|
||||
/* prepare the file */
|
||||
memset(buffw, '\1', PAGE_SIZE);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
/* test */
|
||||
if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE))
|
||||
{
|
||||
diag("error in flush_pagecache_blocks\n");
|
||||
exit(1);
|
||||
}
|
||||
pagecache_read(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_WRITE,
|
||||
0);
|
||||
pagecache_write(&pagecache, &file1, 1, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
memset(buffw + PAGE_SIZE/2, ((unsigned char) 129), PAGE_SIZE/2);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_WRITE_TO_READ,
|
||||
PAGECACHE_PIN_LEFT_PINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
/*
|
||||
We have to get error because one page of the file is pinned,
|
||||
other page should be flushed
|
||||
*/
|
||||
if (!flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE))
|
||||
{
|
||||
diag("Did not get error in flush_pagecache_blocks\n");
|
||||
res= 0;
|
||||
goto err;
|
||||
}
|
||||
ok((res= test(test_file(file1, file1_name, PAGE_SIZE*2, PAGE_SIZE*2,
|
||||
simple_pin_test_file1))),
|
||||
"Simple pin page file with pin");
|
||||
pagecache_unlock_page(&pagecache,
|
||||
&file1,
|
||||
0,
|
||||
PAGECACHE_LOCK_READ_UNLOCK,
|
||||
PAGECACHE_UNPIN,
|
||||
0, 0);
|
||||
if (flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE))
|
||||
{
|
||||
diag("Got error in flush_pagecache_blocks\n");
|
||||
res= 0;
|
||||
goto err;
|
||||
}
|
||||
ok((res&= test(test_file(file1, file1_name, PAGE_SIZE*2, PAGE_SIZE,
|
||||
simple_pin_test_file2))),
|
||||
"Simple pin page result file");
|
||||
if (res)
|
||||
reset_file(file1, file1_name);
|
||||
err:
|
||||
free(buffw);
|
||||
free(buffr);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
/*
|
||||
Prepare page, write new value, then delete page from cache without flush,
|
||||
on the disk should be page with old content written during preparation
|
||||
*/
|
||||
|
||||
int simple_delete_forget_test()
|
||||
{
|
||||
unsigned char *buffw= malloc(PAGE_SIZE);
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
int res;
|
||||
DBUG_ENTER("simple_delete_forget_test");
|
||||
/* prepare the file */
|
||||
memset(buffw, '\1', PAGE_SIZE);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
/* test */
|
||||
memset(buffw, '\2', PAGE_SIZE);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
pagecache_delete_page(&pagecache, &file1, 0,
|
||||
PAGECACHE_LOCK_WRITE, 0);
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
ok((res= test(test_file(file1, file1_name, PAGE_SIZE, PAGE_SIZE,
|
||||
simple_delete_forget_test_file))),
|
||||
"Simple delete-forget page file");
|
||||
if (res)
|
||||
reset_file(file1, file1_name);
|
||||
free(buffw);
|
||||
free(buffr);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
/*
|
||||
Prepare page with locking, write new content to the page,
|
||||
delete page with flush and on existing lock,
|
||||
check that page on disk contain new value.
|
||||
*/
|
||||
|
||||
int simple_delete_flush_test()
|
||||
{
|
||||
unsigned char *buffw= malloc(PAGE_SIZE);
|
||||
unsigned char *buffr= malloc(PAGE_SIZE);
|
||||
int res;
|
||||
DBUG_ENTER("simple_delete_flush_test");
|
||||
/* prepare the file */
|
||||
memset(buffw, '\1', PAGE_SIZE);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_WRITE,
|
||||
PAGECACHE_PIN,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
/* test */
|
||||
memset(buffw, '\2', PAGE_SIZE);
|
||||
pagecache_write(&pagecache, &file1, 0, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_WRITELOCKED,
|
||||
PAGECACHE_PIN_LEFT_PINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
pagecache_delete_page(&pagecache, &file1, 0,
|
||||
PAGECACHE_LOCK_LEFT_WRITELOCKED, 1);
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
ok((res= test(test_file(file1, file1_name, PAGE_SIZE, PAGE_SIZE,
|
||||
simple_delete_flush_test_file))),
|
||||
"Simple delete-forget page file");
|
||||
if (res)
|
||||
reset_file(file1, file1_name);
|
||||
free(buffw);
|
||||
free(buffr);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
write then read file bigger then cache
|
||||
*/
|
||||
|
||||
int simple_big_test()
|
||||
{
|
||||
unsigned char *buffw= (unsigned char *)malloc(PAGE_SIZE);
|
||||
unsigned char *buffr= (unsigned char *)malloc(PAGE_SIZE);
|
||||
struct file_desc *desc=
|
||||
(struct file_desc *)malloc((PCACHE_SIZE/(PAGE_SIZE/2)) *
|
||||
sizeof(struct file_desc));
|
||||
int res, i;
|
||||
DBUG_ENTER("simple_big_test");
|
||||
/* prepare the file twice larger then cache */
|
||||
for (i= 0; i < PCACHE_SIZE/(PAGE_SIZE/2); i++)
|
||||
{
|
||||
memset(buffw, (unsigned char) (i & 0xff), PAGE_SIZE);
|
||||
desc[i].length= PAGE_SIZE;
|
||||
desc[i].content= (i & 0xff);
|
||||
pagecache_write(&pagecache, &file1, i, 3, (char*)buffw,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
PAGECACHE_PIN_LEFT_UNPINNED,
|
||||
PAGECACHE_WRITE_DELAY,
|
||||
0);
|
||||
}
|
||||
ok(1, "Simple big file write");
|
||||
/* check written pages sequentally read */
|
||||
for (i= 0; i < PCACHE_SIZE/(PAGE_SIZE/2); i++)
|
||||
{
|
||||
int j;
|
||||
pagecache_read(&pagecache, &file1, i, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
0);
|
||||
for(j= 0; j < PAGE_SIZE; j++)
|
||||
{
|
||||
if (buffr[j] != (i & 0xff))
|
||||
{
|
||||
diag("simple_big_test seq: page %u byte %u mismatch\n", i, j);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
ok(1, "simple big file sequentally read");
|
||||
/* chack random reads */
|
||||
for (i= 0; i < PCACHE_SIZE/(PAGE_SIZE); i++)
|
||||
{
|
||||
int j, page;
|
||||
page= rand() % (PCACHE_SIZE/(PAGE_SIZE/2));
|
||||
pagecache_read(&pagecache, &file1, page, 3, (char*)buffr,
|
||||
PAGECACHE_PLAIN_PAGE,
|
||||
PAGECACHE_LOCK_LEFT_UNLOCKED,
|
||||
0);
|
||||
for(j= 0; j < PAGE_SIZE; j++)
|
||||
{
|
||||
if (buffr[j] != (page & 0xff))
|
||||
{
|
||||
diag("simple_big_test rnd: page %u byte %u mismatch\n", page, j);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
ok(1, "simple big file random read");
|
||||
flush_pagecache_blocks(&pagecache, &file1, FLUSH_FORCE_WRITE);
|
||||
|
||||
ok((res= test(test_file(file1, file1_name, PCACHE_SIZE*2, PAGE_SIZE,
|
||||
desc))),
|
||||
"Simple big file");
|
||||
if (res)
|
||||
reset_file(file1, file1_name);
|
||||
free(buffw);
|
||||
free(buffr);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
/*
|
||||
Thread function
|
||||
*/
|
||||
|
||||
static void *test_thread(void *arg)
|
||||
{
|
||||
int param=*((int*) arg);
|
||||
|
||||
my_thread_init();
|
||||
DBUG_ENTER("test_thread");
|
||||
|
||||
DBUG_PRINT("enter", ("param: %d", param));
|
||||
|
||||
if (!simple_read_write_test() ||
|
||||
!simple_read_change_write_read_test() ||
|
||||
!simple_pin_test() ||
|
||||
!simple_delete_forget_test() ||
|
||||
!simple_delete_flush_test() ||
|
||||
!simple_big_test())
|
||||
exit(1);
|
||||
|
||||
DBUG_PRINT("info", ("Thread %s ended\n", my_thread_name()));
|
||||
pthread_mutex_lock(&LOCK_thread_count);
|
||||
thread_count--;
|
||||
VOID(pthread_cond_signal(&COND_thread_count)); /* Tell main we are ready */
|
||||
pthread_mutex_unlock(&LOCK_thread_count);
|
||||
free((gptr) arg);
|
||||
my_thread_end();
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv __attribute__((unused)))
|
||||
{
|
||||
pthread_t tid;
|
||||
pthread_attr_t thr_attr;
|
||||
int *param, error, pagen;
|
||||
|
||||
MY_INIT(argv[0]);
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
#if defined(__WIN__)
|
||||
default_dbug_option= "d:t:i:O,\\test_pagecache_single.trace";
|
||||
#else
|
||||
default_dbug_option= "d:t:i:o,/tmp/test_pagecache_single.trace";
|
||||
#endif
|
||||
if (argc > 1)
|
||||
{
|
||||
DBUG_SET(default_dbug_option);
|
||||
DBUG_SET_INITIAL(default_dbug_option);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
DBUG_ENTER("main");
|
||||
DBUG_PRINT("info", ("Main thread: %s\n", my_thread_name()));
|
||||
if ((file1.file= my_open(file1_name,
|
||||
O_CREAT | O_TRUNC | O_RDWR, MYF(0))) == -1)
|
||||
{
|
||||
fprintf(stderr, "Got error during file1 creation from open() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
DBUG_PRINT("info", ("file1: %d", file1.file));
|
||||
if (chmod(file1_name, S_IRWXU | S_IRWXG | S_IRWXO) != 0)
|
||||
{
|
||||
fprintf(stderr, "Got error during file1 chmod() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
my_pwrite(file1.file, "test file", 9, 0, MYF(0));
|
||||
|
||||
if ((error= pthread_cond_init(&COND_thread_count, NULL)))
|
||||
{
|
||||
fprintf(stderr, "Got error: %d from pthread_cond_init (errno: %d)\n",
|
||||
error, errno);
|
||||
exit(1);
|
||||
}
|
||||
if ((error= pthread_mutex_init(&LOCK_thread_count, MY_MUTEX_INIT_FAST)))
|
||||
{
|
||||
fprintf(stderr, "Got error: %d from pthread_cond_init (errno: %d)\n",
|
||||
error, errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((error= pthread_attr_init(&thr_attr)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
if ((error= pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED)))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Got error: %d from pthread_attr_setdetachstate (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifndef pthread_attr_setstacksize /* void return value */
|
||||
if ((error= pthread_attr_setstacksize(&thr_attr, 65536L)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_attr_setstacksize (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_THR_SETCONCURRENCY
|
||||
VOID(thr_setconcurrency(2));
|
||||
#endif
|
||||
|
||||
my_thread_global_init();
|
||||
|
||||
|
||||
if ((pagen= init_pagecache(&pagecache, PCACHE_SIZE, 0, 0,
|
||||
PAGE_SIZE, 0)) == 0)
|
||||
{
|
||||
fprintf(stderr,"Got error: init_pagecache() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
DBUG_PRINT("info", ("Page cache %d pages", pagen));
|
||||
|
||||
if ((error=pthread_mutex_lock(&LOCK_thread_count)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_mutex_lock (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
param=(int*) malloc(sizeof(int));
|
||||
*param= 1;
|
||||
if ((error= pthread_create(&tid, &thr_attr, test_thread, (void*) param)))
|
||||
{
|
||||
fprintf(stderr,"Got error: %d from pthread_create (errno: %d)\n",
|
||||
error,errno);
|
||||
exit(1);
|
||||
}
|
||||
thread_count++;
|
||||
DBUG_PRINT("info", ("Thread started"));
|
||||
pthread_mutex_unlock(&LOCK_thread_count);
|
||||
|
||||
pthread_attr_destroy(&thr_attr);
|
||||
|
||||
if ((error= pthread_mutex_lock(&LOCK_thread_count)))
|
||||
fprintf(stderr,"Got error: %d from pthread_mutex_lock\n",error);
|
||||
while (thread_count)
|
||||
{
|
||||
if ((error= pthread_cond_wait(&COND_thread_count,&LOCK_thread_count)))
|
||||
fprintf(stderr,"Got error: %d from pthread_cond_wait\n",error);
|
||||
}
|
||||
if ((error= pthread_mutex_unlock(&LOCK_thread_count)))
|
||||
fprintf(stderr,"Got error: %d from pthread_mutex_unlock\n",error);
|
||||
DBUG_PRINT("info", ("thread ended"));
|
||||
|
||||
end_pagecache(&pagecache, 1);
|
||||
DBUG_PRINT("info", ("Page cache ended"));
|
||||
|
||||
if (my_close(file1.file, MYF(0)) != 0)
|
||||
{
|
||||
fprintf(stderr, "Got error during file1 closing from close() (errno: %d)\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
/*my_delete(file1_name, MYF(0));*/
|
||||
my_thread_global_end();
|
||||
|
||||
DBUG_PRINT("info", ("file1 (%d) closed", file1.file));
|
||||
|
||||
DBUG_PRINT("info", ("Program end"));
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#pragma interface /* gcc class implementation */
|
||||
#endif
|
||||
|
||||
#include <my_handler.h>
|
||||
#include <ft_global.h>
|
||||
#include <keycache.h>
|
||||
|
||||
@@ -260,6 +261,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
|
||||
};
|
||||
|
||||
@@ -1324,7 +1324,6 @@ public:
|
||||
|
||||
|
||||
/* for fulltext search */
|
||||
#include <ft_global.h>
|
||||
|
||||
class Item_func_match :public Item_real_func
|
||||
{
|
||||
|
||||
@@ -1650,10 +1650,7 @@ extern SHOW_COMP_OPTION have_blackhole_db;
|
||||
extern SHOW_COMP_OPTION have_ndbcluster;
|
||||
extern SHOW_COMP_OPTION have_partition_db;
|
||||
extern SHOW_COMP_OPTION have_merge_db;
|
||||
|
||||
extern handlerton *partition_hton;
|
||||
extern handlerton *myisam_hton;
|
||||
extern handlerton *heap_hton;
|
||||
extern SHOW_COMP_OPTION have_maria_db;
|
||||
|
||||
extern SHOW_COMP_OPTION have_row_based_replication;
|
||||
extern SHOW_COMP_OPTION have_openssl, have_symlink, have_dlopen;
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#include "events.h"
|
||||
|
||||
#include "../storage/myisam/ha_myisam.h"
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
#include "../storage/maria/ha_maria.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ROW_BASED_REPLICATION
|
||||
#include "rpl_injector.h"
|
||||
@@ -521,6 +524,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>";
|
||||
/* name of additional condition */
|
||||
@@ -4743,10 +4747,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,
|
||||
@@ -4760,7 +4771,7 @@ enum options_mysqld
|
||||
OPT_SORT_BUFFER, OPT_TABLE_OPEN_CACHE, OPT_TABLE_DEF_CACHE,
|
||||
OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE,
|
||||
OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK,
|
||||
OPT_WAIT_TIMEOUT, OPT_MYISAM_REPAIR_THREADS,
|
||||
OPT_WAIT_TIMEOUT,
|
||||
OPT_INNODB_MIRRORED_LOG_GROUPS,
|
||||
OPT_INNODB_LOG_FILES_IN_GROUP,
|
||||
OPT_INNODB_LOG_FILE_SIZE,
|
||||
@@ -5909,6 +5920,49 @@ log and this option does nothing anymore.",
|
||||
0
|
||||
#endif
|
||||
, 0, 2, 0, 1, 0},
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
{"maria_block_size", OPT_MARIA_BLOCK_SIZE,
|
||||
"Block size to be used for MARIA index pages.",
|
||||
(gptr*) &maria_block_size,
|
||||
(gptr*) &maria_block_size, 0, GET_ULONG, REQUIRED_ARG,
|
||||
MARIA_KEY_BLOCK_LENGTH, MARIA_MIN_KEY_BLOCK_LENGTH,
|
||||
MARIA_MAX_KEY_BLOCK_LENGTH,
|
||||
0, MARIA_MIN_KEY_BLOCK_LENGTH, 0},
|
||||
{"maria_key_buffer_size", OPT_KEY_BUFFER_SIZE,
|
||||
"The size of the buffer used for index blocks for Maria tables. Increase "
|
||||
"this to get better index handling (for all reads and multiple writes) to "
|
||||
"as much as you can afford; 64M on a 256M machine that mainly runs MySQL "
|
||||
"is quite common.",
|
||||
(gptr*) &maria_key_cache_var.param_buff_size, (gptr*) 0,
|
||||
0, (GET_ULL | GET_ASK_ADDR),
|
||||
REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, ~(ulong) 0, MALLOC_OVERHEAD,
|
||||
IO_SIZE, 0},
|
||||
{"maria_max_sort_file_size", OPT_MARIA_MAX_SORT_FILE_SIZE,
|
||||
"Don't use the fast sort index method to created index if the temporary "
|
||||
"file would get bigger than this.",
|
||||
(gptr*) &global_system_variables.maria_max_sort_file_size,
|
||||
(gptr*) &max_system_variables.maria_max_sort_file_size, 0,
|
||||
GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, (ulonglong) MAX_FILE_SIZE,
|
||||
0, 1024*1024, 0},
|
||||
{"maria_repair_threads", OPT_MARIA_REPAIR_THREADS,
|
||||
"Number of threads to use when repairing maria tables. The value of 1 "
|
||||
"disables parallel repair.",
|
||||
(gptr*) &global_system_variables.maria_repair_threads,
|
||||
(gptr*) &max_system_variables.maria_repair_threads, 0,
|
||||
GET_ULONG, REQUIRED_ARG, 1, 1, ~0L, 0, 1, 0},
|
||||
{"maria_sort_buffer_size", OPT_MARIA_SORT_BUFFER_SIZE,
|
||||
"The buffer that is allocated when sorting the index when doing a REPAIR "
|
||||
"or when creating indexes with CREATE INDEX or ALTER TABLE.",
|
||||
(gptr*) &global_system_variables.maria_sort_buff_size,
|
||||
(gptr*) &max_system_variables.maria_sort_buff_size, 0,
|
||||
GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0},
|
||||
{"maria_stats_method", OPT_MARIA_STATS_METHOD,
|
||||
"Specifies how maria index statistics collection code should threat NULLs. "
|
||||
"Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
|
||||
"\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
|
||||
(gptr*) &maria_stats_method_str, (gptr*) &maria_stats_method_str, 0,
|
||||
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
#endif
|
||||
{"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
|
||||
"Max packetlength to send/receive from to server.",
|
||||
(gptr*) &global_system_variables.max_allowed_packet,
|
||||
@@ -6009,12 +6063,6 @@ The minimum value for this variable is 4096.",
|
||||
(gptr*) &myisam_data_pointer_size,
|
||||
(gptr*) &myisam_data_pointer_size, 0, GET_ULONG, REQUIRED_ARG,
|
||||
6, 2, 7, 0, 1, 0},
|
||||
{"myisam_max_extra_sort_file_size", OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE,
|
||||
"Deprecated option",
|
||||
(gptr*) &global_system_variables.myisam_max_extra_sort_file_size,
|
||||
(gptr*) &max_system_variables.myisam_max_extra_sort_file_size,
|
||||
0, GET_ULL, REQUIRED_ARG, (ulonglong) MI_MAX_TEMP_LENGTH,
|
||||
0, (ulonglong) MAX_FILE_SIZE, 0, 1, 0},
|
||||
{"myisam_max_sort_file_size", OPT_MYISAM_MAX_SORT_FILE_SIZE,
|
||||
"Don't use the fast sort index method to created index if the temporary file would get bigger than this.",
|
||||
(gptr*) &global_system_variables.myisam_max_sort_file_size,
|
||||
@@ -6946,15 +6994,24 @@ static void mysql_init_variables(void)
|
||||
query_id= thread_id= 1L;
|
||||
strmov(server_version, MYSQL_SERVER_VERSION);
|
||||
myisam_recover_options_str= sql_mode_str= "OFF";
|
||||
myisam_stats_method_str= "nulls_unequal";
|
||||
myisam_stats_method_str= maria_stats_method_str= "nulls_unequal";
|
||||
my_bind_addr = htonl(INADDR_ANY);
|
||||
threads.empty();
|
||||
thread_cache.empty();
|
||||
key_caches.empty();
|
||||
if (!(dflt_key_cache= get_or_create_key_cache(default_key_cache_base.str,
|
||||
default_key_cache_base.length)))
|
||||
default_key_cache_base.length)))
|
||||
exit(1);
|
||||
multi_keycache_init(); /* set key_cache_hash.default_value = dflt_key_cache */
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
if (!(maria_key_cache= get_or_create_key_cache(maria_key_cache_base.str,
|
||||
maria_key_cache_base.length)))
|
||||
exit(1);
|
||||
maria_key_cache->param_buff_size= maria_key_cache_var.param_buff_size;
|
||||
maria_key_cache->param_block_size= maria_block_size;
|
||||
#endif
|
||||
|
||||
/* set key_cache_hash.default_value = dflt_key_cache */
|
||||
multi_keycache_init();
|
||||
|
||||
/* Set directory paths */
|
||||
strmake(language, LANGUAGE, sizeof(language)-1);
|
||||
@@ -7657,7 +7714,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
||||
int method;
|
||||
LINT_INIT(method_conv);
|
||||
|
||||
myisam_stats_method_str= argument;
|
||||
if ((method=find_type(argument, &myisam_stats_method_typelib, 2)) <= 0)
|
||||
{
|
||||
fprintf(stderr, "Invalid value of myisam_stats_method: %s.\n", argument);
|
||||
@@ -8125,6 +8181,7 @@ void refresh_status(THD *thd)
|
||||
#undef have_federated_db
|
||||
#undef have_partition_db
|
||||
#undef have_blackhole_db
|
||||
#undef have_maria_db
|
||||
#undef have_merge_db
|
||||
|
||||
SHOW_COMP_OPTION have_innodb= SHOW_OPTION_NO;
|
||||
@@ -8135,6 +8192,7 @@ SHOW_COMP_OPTION have_csv_db= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_federated_db= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_partition_db= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_blackhole_db= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_maria_db= SHOW_OPTION_NO;
|
||||
SHOW_COMP_OPTION have_merge_db= SHOW_OPTION_NO;
|
||||
|
||||
#ifndef WITH_INNOBASE_STORAGE_ENGINE
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
#include <thr_alarm.h>
|
||||
#include <myisam.h>
|
||||
#include <my_dir.h>
|
||||
#ifdef WITH_MARIA_STORAGE_ENGINE
|
||||
#include <maria.h>
|
||||
#endif
|
||||
|
||||
#include "events.h"
|
||||
|
||||
@@ -145,6 +148,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);
|
||||
@@ -348,6 +352,14 @@ sys_var_thd_enum sys_myisam_stats_method("myisam_stats_method",
|
||||
&myisam_stats_method_typelib,
|
||||
NULL);
|
||||
|
||||
sys_var_thd_ulonglong sys_maria_max_sort_file_size("maria_max_sort_file_size", &SV::maria_max_sort_file_size, fix_maria_max_sort_file_size, 1);
|
||||
sys_var_thd_ulong sys_maria_repair_threads("maria_repair_threads", &SV::maria_repair_threads);
|
||||
sys_var_thd_ulong sys_maria_sort_buffer_size("maria_sort_buffer_size", &SV::maria_sort_buff_size);
|
||||
sys_var_thd_enum sys_maria_stats_method("maria_stats_method",
|
||||
&SV::maria_stats_method,
|
||||
&myisam_stats_method_typelib,
|
||||
NULL);
|
||||
|
||||
sys_var_thd_ulong sys_net_buffer_length("net_buffer_length",
|
||||
&SV::net_buffer_length);
|
||||
sys_var_thd_ulong sys_net_read_timeout("net_read_timeout",
|
||||
@@ -674,6 +686,7 @@ sys_var_have_variable sys_have_federated_db("have_federated_engine",
|
||||
&have_federated_db);
|
||||
sys_var_have_variable sys_have_geometry("have_geometry", &have_geometry);
|
||||
sys_var_have_variable sys_have_innodb("have_innodb", &have_innodb);
|
||||
sys_var_have_variable sys_have_maria("have_maria", &have_maria);
|
||||
sys_var_have_variable sys_have_merge_db("have_merge", &have_merge_db);
|
||||
sys_var_have_variable sys_have_ndbcluster("have_ndbcluster", &have_ndbcluster);
|
||||
sys_var_have_variable sys_have_openssl("have_openssl", &have_openssl);
|
||||
@@ -805,6 +818,7 @@ SHOW_VAR init_vars[]= {
|
||||
{sys_have_federated_db.name,(char*) &have_federated_db, SHOW_HAVE},
|
||||
{sys_have_geometry.name, (char*) &have_geometry, SHOW_HAVE},
|
||||
{sys_have_innodb.name, (char*) &have_innodb, SHOW_HAVE},
|
||||
{sys_have_maria.name, (char*) &have_maria, SHOW_HAVE},
|
||||
{sys_have_merge_db.name, (char*) &have_merge_db, SHOW_HAVE},
|
||||
{sys_have_ndbcluster.name, (char*) &have_ndbcluster, SHOW_HAVE},
|
||||
{sys_have_openssl.name, (char*) &have_openssl, SHOW_HAVE},
|
||||
@@ -886,6 +900,15 @@ SHOW_VAR init_vars[]= {
|
||||
{sys_low_priority_updates.name, (char*) &sys_low_priority_updates, SHOW_SYS},
|
||||
{"lower_case_file_system", (char*) &lower_case_file_system, SHOW_MY_BOOL},
|
||||
{"lower_case_table_names", (char*) &lower_case_table_names, SHOW_INT},
|
||||
|
||||
{sys_maria_max_sort_file_size.name, (char*) &sys_maria_max_sort_file_size,
|
||||
SHOW_SYS},
|
||||
{sys_maria_repair_threads.name, (char*) &sys_maria_repair_threads,
|
||||
SHOW_SYS},
|
||||
{sys_maria_sort_buffer_size.name, (char*) &sys_maria_sort_buffer_size,
|
||||
SHOW_SYS},
|
||||
{sys_maria_stats_method.name, (char*) &sys_maria_stats_method, SHOW_SYS},
|
||||
|
||||
{sys_max_allowed_packet.name,(char*) &sys_max_allowed_packet, SHOW_SYS},
|
||||
{sys_max_binlog_cache_size.name,(char*) &sys_max_binlog_cache_size, SHOW_SYS},
|
||||
{sys_max_binlog_size.name, (char*) &sys_max_binlog_size, SHOW_SYS},
|
||||
@@ -1177,6 +1200,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
|
||||
*/
|
||||
@@ -2383,6 +2416,7 @@ void sys_var_collation_server::set_default(THD *thd, enum_var_type type)
|
||||
|
||||
|
||||
LEX_STRING default_key_cache_base= {(char *) "default", 7 };
|
||||
LEX_STRING maria_key_cache_base= {(char *) "maria", 5 };
|
||||
|
||||
static KEY_CACHE zero_key_cache;
|
||||
|
||||
@@ -2392,7 +2426,7 @@ KEY_CACHE *get_key_cache(LEX_STRING *cache_name)
|
||||
if (!cache_name || ! cache_name->length)
|
||||
cache_name= &default_key_cache_base;
|
||||
return ((KEY_CACHE*) find_named(&key_caches,
|
||||
cache_name->str, cache_name->length, 0));
|
||||
cache_name->str, cache_name->length, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1105,6 +1105,7 @@ public:
|
||||
extern sys_var_thd_bool sys_old_alter_table;
|
||||
extern sys_var_thd_bool sys_old_passwords;
|
||||
extern LEX_STRING default_key_cache_base;
|
||||
extern LEX_STRING maria_key_cache_base;
|
||||
|
||||
/* For sql_yacc */
|
||||
struct sys_var_with_base
|
||||
|
||||
@@ -181,7 +181,7 @@ class Time_zone;
|
||||
|
||||
struct system_variables
|
||||
{
|
||||
ulonglong myisam_max_extra_sort_file_size;
|
||||
ulonglong maria_max_sort_file_size;
|
||||
ulonglong myisam_max_sort_file_size;
|
||||
ha_rows select_limit;
|
||||
ha_rows max_join_size;
|
||||
@@ -196,6 +196,9 @@ struct system_variables
|
||||
ulong max_sort_length;
|
||||
ulong max_tmp_tables;
|
||||
ulong max_insert_delayed_threads;
|
||||
ulong maria_repair_threads;
|
||||
ulong maria_sort_buff_size;
|
||||
ulong maria_stats_method;
|
||||
ulong multi_range_count;
|
||||
ulong myisam_repair_threads;
|
||||
ulong myisam_sort_buff_size;
|
||||
|
||||
@@ -35,7 +35,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 */
|
||||
@@ -43,13 +45,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 */
|
||||
|
||||
@@ -20,7 +20,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
|
||||
160
storage/maria/Makefile.am
Normal file
160
storage/maria/Makefile.am
Normal file
@@ -0,0 +1,160 @@
|
||||
# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
MYSQLDATAdir = $(localstatedir)
|
||||
MYSQLSHAREdir = $(pkgdatadir)
|
||||
MYSQLBASEdir= $(prefix)
|
||||
MYSQLLIBdir= $(pkglibdir)
|
||||
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \
|
||||
-I$(top_srcdir)/regex \
|
||||
-I$(top_srcdir)/sql \
|
||||
-I$(srcdir)
|
||||
WRAPLIBS=
|
||||
|
||||
LDADD =
|
||||
|
||||
DEFS = @DEFS@
|
||||
|
||||
# "." is needed first because tests in unittest need libmaria
|
||||
SUBDIRS = . unittest
|
||||
|
||||
EXTRA_DIST = ma_test_all.sh ma_test_all.res ma_ft_stem.c CMakeLists.txt
|
||||
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 \
|
||||
ma_control_file.h ha_maria.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_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 \
|
||||
ma_rt_index.c ma_rt_key.c ma_rt_mbr.c ma_rt_split.c \
|
||||
ma_sp_key.c ma_control_file.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.%
|
||||
26
storage/maria/cmakelists.txt
Normal file
26
storage/maria/cmakelists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
|
||||
ADD_LIBRARY(myisam ft_boolean_search.c ft_nlq_search.c ft_parser.c ft_static.c ft_stem.c
|
||||
ft_stopwords.c ft_update.c mi_cache.c mi_changed.c mi_check.c
|
||||
mi_checksum.c mi_close.c mi_create.c mi_dbug.c mi_delete.c
|
||||
mi_delete_all.c mi_delete_table.c mi_dynrec.c mi_extra.c mi_info.c
|
||||
mi_key.c mi_keycache.c mi_locking.c mi_log.c mi_open.c
|
||||
mi_packrec.c mi_page.c mi_panic.c mi_preload.c mi_range.c mi_rename.c
|
||||
mi_rfirst.c mi_rlast.c mi_rnext.c mi_rnext_same.c mi_rprev.c mi_rrnd.c
|
||||
mi_rsame.c mi_rsamepos.c mi_scan.c mi_search.c mi_static.c mi_statrec.c
|
||||
mi_unique.c mi_update.c mi_write.c rt_index.c rt_key.c rt_mbr.c
|
||||
rt_split.c sort.c sp_key.c ft_eval.h myisamdef.h rt_index.h mi_rkey.c)
|
||||
|
||||
ADD_EXECUTABLE(myisam_ftdump myisam_ftdump.c)
|
||||
TARGET_LINK_LIBRARIES(myisam_ftdump myisam mysys dbug strings zlib wsock32)
|
||||
|
||||
ADD_EXECUTABLE(myisamchk myisamchk.c)
|
||||
TARGET_LINK_LIBRARIES(myisamchk myisam mysys dbug strings zlib wsock32)
|
||||
|
||||
ADD_EXECUTABLE(myisamlog myisamlog.c)
|
||||
TARGET_LINK_LIBRARIES(myisamlog myisam mysys dbug strings zlib wsock32)
|
||||
|
||||
ADD_EXECUTABLE(myisampack myisampack.c)
|
||||
TARGET_LINK_LIBRARIES(myisampack myisam mysys dbug strings zlib wsock32)
|
||||
49
storage/maria/ft_maria.c
Normal file
49
storage/maria/ft_maria.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
/*
|
||||
This function is for interface functions between fulltext and maria
|
||||
*/
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
|
||||
FT_INFO *maria_ft_init_search(uint flags, void *info, uint keynr,
|
||||
byte *query, uint query_len, CHARSET_INFO *cs,
|
||||
byte *record)
|
||||
{
|
||||
FT_INFO *res;
|
||||
if (flags & FT_BOOL)
|
||||
res= maria_ft_init_boolean_search((MARIA_HA *) info, keynr, query,
|
||||
query_len, cs);
|
||||
else
|
||||
res= maria_ft_init_nlq_search((MARIA_HA *) info, keynr, query, query_len,
|
||||
flags, record);
|
||||
return res;
|
||||
}
|
||||
|
||||
const struct _ft_vft _ma_ft_vft_nlq = {
|
||||
maria_ft_nlq_read_next, maria_ft_nlq_find_relevance,
|
||||
maria_ft_nlq_close_search, maria_ft_nlq_get_relevance,
|
||||
maria_ft_nlq_reinit_search
|
||||
};
|
||||
const struct _ft_vft _ma_ft_vft_boolean = {
|
||||
maria_ft_boolean_read_next, maria_ft_boolean_find_relevance,
|
||||
maria_ft_boolean_close_search, maria_ft_boolean_get_relevance,
|
||||
maria_ft_boolean_reinit_search
|
||||
};
|
||||
|
||||
1848
storage/maria/ha_maria.cc
Normal file
1848
storage/maria/ha_maria.cc
Normal file
File diff suppressed because it is too large
Load Diff
145
storage/maria/ha_maria.h
Normal file
145
storage/maria/ha_maria.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/* Copyright (C) 2006,2004 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
|
||||
#ifdef USE_PRAGMA_INTERFACE
|
||||
#pragma interface /* gcc class implementation */
|
||||
#endif
|
||||
|
||||
/* class for the the maria handler */
|
||||
|
||||
#include <maria.h>
|
||||
|
||||
#define HA_RECOVER_NONE 0 /* No automatic recover */
|
||||
#define HA_RECOVER_DEFAULT 1 /* Automatic recover active */
|
||||
#define HA_RECOVER_BACKUP 2 /* Make a backupfile on recover */
|
||||
#define HA_RECOVER_FORCE 4 /* Recover even if we loose rows */
|
||||
#define HA_RECOVER_QUICK 8 /* Don't check rows in data file */
|
||||
|
||||
extern ulong maria_sort_buffer_size;
|
||||
extern TYPELIB maria_recover_typelib;
|
||||
extern ulong maria_recover_options;
|
||||
|
||||
class ha_maria :public handler
|
||||
{
|
||||
MARIA_HA *file;
|
||||
ulong int_table_flags;
|
||||
char *data_file_name, *index_file_name;
|
||||
bool can_enable_indexes;
|
||||
int repair(THD * thd, HA_CHECK ¶m, bool optimize);
|
||||
|
||||
public:
|
||||
ha_maria(TABLE_SHARE * table_arg);
|
||||
~ha_maria()
|
||||
{}
|
||||
const char *table_type() const
|
||||
{ return "MARIA"; }
|
||||
const char *index_type(uint key_number);
|
||||
const char **bas_ext() const;
|
||||
ulonglong table_flags() const
|
||||
{ return int_table_flags; }
|
||||
ulong index_flags(uint inx, uint part, bool all_parts) const
|
||||
{
|
||||
return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ?
|
||||
0 : HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE |
|
||||
HA_READ_ORDER | HA_KEYREAD_ONLY);
|
||||
}
|
||||
uint max_supported_keys() const
|
||||
{ return MARIA_MAX_KEY; }
|
||||
uint max_supported_key_length() const
|
||||
{ return HA_MAX_KEY_LENGTH; }
|
||||
uint max_supported_key_part_length() const
|
||||
{ return HA_MAX_KEY_LENGTH; }
|
||||
uint checksum() const;
|
||||
|
||||
virtual bool check_if_locking_is_allowed(uint sql_command,
|
||||
ulong type, TABLE * table,
|
||||
uint count,
|
||||
bool called_by_logger_thread);
|
||||
int open(const char *name, int mode, uint test_if_locked);
|
||||
int close(void);
|
||||
int write_row(byte * buf);
|
||||
int update_row(const byte * old_data, byte * new_data);
|
||||
int delete_row(const byte * buf);
|
||||
int index_read(byte * buf, const byte * key,
|
||||
uint key_len, enum ha_rkey_function find_flag);
|
||||
int index_read_idx(byte * buf, uint idx, const byte * key,
|
||||
uint key_len, enum ha_rkey_function find_flag);
|
||||
int index_read_last(byte * buf, const byte * key, uint key_len);
|
||||
int index_next(byte * buf);
|
||||
int index_prev(byte * buf);
|
||||
int index_first(byte * buf);
|
||||
int index_last(byte * buf);
|
||||
int index_next_same(byte * buf, const byte * key, uint keylen);
|
||||
int ft_init()
|
||||
{
|
||||
if (!ft_handler)
|
||||
return 1;
|
||||
ft_handler->please->reinit_search(ft_handler);
|
||||
return 0;
|
||||
}
|
||||
FT_INFO *ft_init_ext(uint flags, uint inx, String * key)
|
||||
{
|
||||
return maria_ft_init_search(flags, file, inx,
|
||||
(byte *) key->ptr(), key->length(),
|
||||
key->charset(), table->record[0]);
|
||||
}
|
||||
int ft_read(byte * buf);
|
||||
int rnd_init(bool scan);
|
||||
int rnd_next(byte * buf);
|
||||
int rnd_pos(byte * buf, byte * pos);
|
||||
int restart_rnd_next(byte * buf, byte * pos);
|
||||
void position(const byte * record);
|
||||
void info(uint);
|
||||
int extra(enum ha_extra_function operation);
|
||||
int extra_opt(enum ha_extra_function operation, ulong cache_size);
|
||||
int reset(void);
|
||||
int external_lock(THD * thd, int lock_type);
|
||||
int delete_all_rows(void);
|
||||
int disable_indexes(uint mode);
|
||||
int enable_indexes(uint mode);
|
||||
int indexes_are_disabled(void);
|
||||
void start_bulk_insert(ha_rows rows);
|
||||
int end_bulk_insert();
|
||||
ha_rows records_in_range(uint inx, key_range * min_key, key_range * max_key);
|
||||
void update_create_info(HA_CREATE_INFO * create_info);
|
||||
int create(const char *name, TABLE * form, HA_CREATE_INFO * create_info);
|
||||
THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to,
|
||||
enum thr_lock_type lock_type);
|
||||
virtual void get_auto_increment(ulonglong offset, ulonglong increment,
|
||||
ulonglong nb_desired_values,
|
||||
ulonglong *first_value,
|
||||
ulonglong *nb_reserved_values);
|
||||
int rename_table(const char *from, const char *to);
|
||||
int delete_table(const char *name);
|
||||
int check(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int analyze(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int repair(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
bool check_and_repair(THD * thd);
|
||||
bool is_crashed() const;
|
||||
bool auto_repair() const
|
||||
{ return maria_recover_options != 0; }
|
||||
int optimize(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int restore(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int backup(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int assign_to_keycache(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
int preload_keys(THD * thd, HA_CHECK_OPT * check_opt);
|
||||
bool check_if_incompatible_data(HA_CREATE_INFO * info, uint table_changes);
|
||||
#ifdef HAVE_REPLICATION
|
||||
int dump(THD * thd, int fd);
|
||||
int net_read_dump(NET * net);
|
||||
#endif
|
||||
};
|
||||
108
storage/maria/ma_cache.c
Normal file
108
storage/maria/ma_cache.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
Functions for read record cacheing with maria
|
||||
Used for reading dynamic/compressed records from datafile.
|
||||
|
||||
Can fetch data directly from file (outside cache),
|
||||
if reading a small chunk straight before the cached part (with possible
|
||||
overlap).
|
||||
|
||||
Can be explicitly asked not to use cache (by not setting READING_NEXT in
|
||||
flag) - useful for occasional out-of-cache reads, when the next read is
|
||||
expected to hit the cache again.
|
||||
|
||||
Allows "partial read" errors in the record header (when READING_HEADER flag
|
||||
is set) - unread part is bzero'ed
|
||||
|
||||
Note: out-of-cache reads are enabled for shared IO_CACHE's too,
|
||||
as these reads will be cached by OS cache (and my_pread is always atomic)
|
||||
*/
|
||||
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
int _ma_read_cache(IO_CACHE *info, byte *buff, my_off_t pos, uint length,
|
||||
int flag)
|
||||
{
|
||||
uint read_length,in_buff_length;
|
||||
my_off_t offset;
|
||||
char *in_buff_pos;
|
||||
DBUG_ENTER("_ma_read_cache");
|
||||
|
||||
if (pos < info->pos_in_file)
|
||||
{
|
||||
read_length=length;
|
||||
if ((my_off_t) read_length > (my_off_t) (info->pos_in_file-pos))
|
||||
read_length=(uint) (info->pos_in_file-pos);
|
||||
info->seek_not_done=1;
|
||||
if (my_pread(info->file,buff,read_length,pos,MYF(MY_NABP)))
|
||||
DBUG_RETURN(1);
|
||||
if (!(length-=read_length))
|
||||
DBUG_RETURN(0);
|
||||
pos+=read_length;
|
||||
buff+=read_length;
|
||||
}
|
||||
if (pos >= info->pos_in_file &&
|
||||
(offset= (my_off_t) (pos - info->pos_in_file)) <
|
||||
(my_off_t) (info->read_end - info->request_pos))
|
||||
{
|
||||
in_buff_pos=info->request_pos+(uint) offset;
|
||||
in_buff_length= min(length,(uint) (info->read_end-in_buff_pos));
|
||||
memcpy(buff,info->request_pos+(uint) offset,(size_t) in_buff_length);
|
||||
if (!(length-=in_buff_length))
|
||||
DBUG_RETURN(0);
|
||||
pos+=in_buff_length;
|
||||
buff+=in_buff_length;
|
||||
}
|
||||
else
|
||||
in_buff_length=0;
|
||||
if (flag & READING_NEXT)
|
||||
{
|
||||
if (pos != (info->pos_in_file +
|
||||
(uint) (info->read_end - info->request_pos)))
|
||||
{
|
||||
info->pos_in_file=pos; /* Force start here */
|
||||
info->read_pos=info->read_end=info->request_pos; /* Everything used */
|
||||
info->seek_not_done=1;
|
||||
}
|
||||
else
|
||||
info->read_pos=info->read_end; /* All block used */
|
||||
if (!(*info->read_function)(info,buff,length))
|
||||
DBUG_RETURN(0);
|
||||
read_length=info->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->seek_not_done=1;
|
||||
if ((read_length=my_pread(info->file,buff,length,pos,MYF(0))) == length)
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
if (!(flag & READING_HEADER) || (int) read_length == -1 ||
|
||||
read_length+in_buff_length < 3)
|
||||
{
|
||||
DBUG_PRINT("error",
|
||||
("Error %d reading next-multi-part block (Got %d bytes)",
|
||||
my_errno, (int) read_length));
|
||||
if (!my_errno || my_errno == -1)
|
||||
my_errno=HA_ERR_WRONG_IN_RECORD;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
bzero(buff+read_length,MARIA_BLOCK_INFO_HEADER_LENGTH - in_buff_length -
|
||||
read_length);
|
||||
DBUG_RETURN(0);
|
||||
} /* _ma_read_cache */
|
||||
34
storage/maria/ma_changed.c
Normal file
34
storage/maria/ma_changed.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Check if somebody has changed table since last check. */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Return 0 if table isn't changed */
|
||||
|
||||
int maria_is_changed(MARIA_HA *info)
|
||||
{
|
||||
int result;
|
||||
DBUG_ENTER("maria_is_changed");
|
||||
if (fast_ma_readinfo(info))
|
||||
DBUG_RETURN(-1);
|
||||
VOID(_ma_writeinfo(info,0));
|
||||
result=(int) info->data_changed;
|
||||
info->data_changed=0;
|
||||
DBUG_PRINT("exit",("result: %d",result));
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
4319
storage/maria/ma_check.c
Normal file
4319
storage/maria/ma_check.c
Normal file
File diff suppressed because it is too large
Load Diff
459
storage/maria/ma_checkpoint.c
Normal file
459
storage/maria/ma_checkpoint.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3071 Maria checkpoint
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/* Here is the implementation of this module */
|
||||
|
||||
/*
|
||||
Summary:
|
||||
- there are asynchronous checkpoints (a writer to the log notices that it's
|
||||
been a long time since we last checkpoint-ed, so posts a request for a
|
||||
background thread to do a checkpoint; does not care about the success of the
|
||||
checkpoint). Then the checkpoint is done by the checkpoint thread, at an
|
||||
unspecified moment ("later") (==soon, of course).
|
||||
- there are synchronous checkpoints: a thread requests a checkpoint to
|
||||
happen now and wants to know when it finishes and if it succeeded; then the
|
||||
checkpoint is done by that same thread.
|
||||
*/
|
||||
|
||||
#include "page_cache.h"
|
||||
#include "least_recently_dirtied.h"
|
||||
#include "transaction.h"
|
||||
#include "share.h"
|
||||
#include "log.h"
|
||||
|
||||
/* could also be called LSN_ERROR */
|
||||
#define LSN_IMPOSSIBLE ((LSN)0)
|
||||
#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;
|
||||
CHECKPOINT_LEVEL next_asynchronous_checkpoint_to_do= NONE;
|
||||
CHECKPOINT_LEVEL synchronous_checkpoint_in_progress= NONE;
|
||||
|
||||
/*
|
||||
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)
|
||||
{
|
||||
DBUG_ENTER("execute_synchronous_checkpoint");
|
||||
DBUG_ASSERT(level > NONE);
|
||||
|
||||
lock(log_mutex);
|
||||
while ((synchronous_checkpoint_in_progress != NONE) ||
|
||||
(next_asynchronous_checkpoint_to_do != NONE))
|
||||
wait_on_checkpoint_done_cond();
|
||||
|
||||
synchronous_checkpoint_in_progress= level;
|
||||
execute_checkpoint(level);
|
||||
safemutex_assert_owner(log_mutex);
|
||||
synchronous_checkpoint_in_progress= NONE;
|
||||
unlock(log_mutex);
|
||||
broadcast(checkpoint_done_cond);
|
||||
}
|
||||
|
||||
/* Picks a checkpoint request, if there is one, and executes it */
|
||||
my_bool execute_asynchronous_checkpoint_if_any()
|
||||
{
|
||||
CHECKPOINT_LEVEL level;
|
||||
DBUG_ENTER("execute_asynchronous_checkpoint");
|
||||
|
||||
lock(log_mutex);
|
||||
if (likely(next_asynchronous_checkpoint_to_do == NONE))
|
||||
{
|
||||
unlock(log_mutex);
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
while (synchronous_checkpoint_in_progress)
|
||||
wait_on_checkpoint_done_cond();
|
||||
|
||||
do_checkpoint:
|
||||
level= next_asynchronous_checkpoint_to_do;
|
||||
DBUG_ASSERT(level > NONE);
|
||||
execute_checkpoint(level);
|
||||
safemutex_assert_owner(log_mutex);
|
||||
if (next_asynchronous_checkpoint_to_do > level)
|
||||
goto do_checkpoint; /* one more request was posted */
|
||||
else
|
||||
{
|
||||
DBUG_ASSERT(next_asynchronous_checkpoint_to_do == level);
|
||||
next_asynchronous_checkpoint_to_do= NONE; /* all work done */
|
||||
}
|
||||
unlock(log_mutex);
|
||||
broadcast(checkpoint_done_cond);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Does the actual checkpointing. Called by
|
||||
execute_synchronous_checkpoint() and
|
||||
execute_asynchronous_checkpoint_if_any().
|
||||
*/
|
||||
my_bool execute_checkpoint(CHECKPOINT_LEVEL level)
|
||||
{
|
||||
LSN candidate_max_rec_lsn_at_last_checkpoint;
|
||||
/* to avoid { lock + no-op + unlock } in the common (==indirect) case */
|
||||
my_bool need_log_mutex;
|
||||
|
||||
DBUG_ENTER("execute_checkpoint");
|
||||
|
||||
safemutex_assert_owner(log_mutex);
|
||||
copy_of_max_rec_lsn_at_last_checkpoint= max_rec_lsn_at_last_checkpoint;
|
||||
|
||||
if (unlikely(need_log_mutex= (level > INDIRECT)))
|
||||
{
|
||||
/* 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).
|
||||
It is max, not min as the WL says (TODO update WL).
|
||||
*/
|
||||
flush_all_LRD_to_lsn(copy_of_max_rec_lsn_at_last_checkpoint);
|
||||
/* this will go full speed (normal scheduling, no sleep) */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
candidate_max_rec_lsn_at_last_checkpoint= checkpoint_indirect(need_log_mutex);
|
||||
|
||||
lock(log_mutex);
|
||||
/*
|
||||
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 */
|
||||
maximum_rec_lsn_last_checkpoint= candidate_max_rec_lsn_at_last_checkpoint;
|
||||
written_since_last_checkpoint= (my_off_t)0;
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
/*
|
||||
keep mutex locked because callers will want to clear mutex-protected
|
||||
status variables
|
||||
*/
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Does an indirect checpoint (collects data from data structures, writes into
|
||||
a checkpoint log record).
|
||||
Returns the largest LSN of the LRD when the checkpoint happened (this is a
|
||||
fuzzy definition), or LSN_IMPOSSIBLE on error. That LSN is used for the
|
||||
"two-checkpoint rule" (MEDIUM checkpoints).
|
||||
*/
|
||||
LSN checkpoint_indirect(my_bool need_log_mutex)
|
||||
{
|
||||
DBUG_ENTER("checkpoint_indirect");
|
||||
|
||||
int error= 0;
|
||||
/* checkpoint record data: */
|
||||
LSN checkpoint_start_lsn;
|
||||
LEX_STRING string1={0,0}, string2={0,0}, string3={0,0};
|
||||
LEX_STRING *string_array[4];
|
||||
char *ptr;
|
||||
LSN checkpoint_lsn;
|
||||
LSN candidate_max_rec_lsn_at_last_checkpoint= 0;
|
||||
list_element *el; /* to scan lists */
|
||||
ulong stored_LRD_size= 0;
|
||||
|
||||
|
||||
DBUG_ASSERT(sizeof(byte *) <= 8);
|
||||
DBUG_ASSERT(sizeof(LSN) <= 8);
|
||||
|
||||
if (need_log_mutex)
|
||||
lock(log_mutex); /* maybe this will clash with log_read_end_lsn() */
|
||||
checkpoint_start_lsn= log_read_end_lsn();
|
||||
unlock(log_mutex);
|
||||
|
||||
DBUG_PRINT("info",("checkpoint_start_lsn %lu", checkpoint_start_lsn));
|
||||
|
||||
/* STEP 1: fetch information about dirty pages */
|
||||
|
||||
/*
|
||||
We lock the entire cache but will be quick, just reading/writing a few MBs
|
||||
of memory at most.
|
||||
*/
|
||||
pagecache_pthread_mutex_lock(&pagecache->cache_lock);
|
||||
|
||||
/*
|
||||
This is an over-estimation, as in theory blocks_changed may contain
|
||||
non-PAGECACHE_LSN_PAGE pages, which we don't want to store into the
|
||||
checkpoint record; the true number of page-LRD-info we'll store into the
|
||||
record is stored_LRD_size.
|
||||
*/
|
||||
string1.length= 8+8+(8+8)*pagecache->blocks_changed;
|
||||
if (NULL == (string1.str= my_malloc(string1.length)))
|
||||
goto err;
|
||||
ptr= string1.str;
|
||||
int8store(ptr, checkpoint_start_lsn);
|
||||
ptr+= 8+8; /* don't store stored_LRD_size now, wait */
|
||||
if (pagecache->blocks_changed > 0)
|
||||
{
|
||||
/*
|
||||
There are different ways to scan the dirty blocks;
|
||||
flush_all_key_blocks() uses a loop over pagecache->used_last->next_used,
|
||||
and for each element of the loop, loops into
|
||||
pagecache->changed_blocks[FILE_HASH(file of the element)].
|
||||
This has the drawback that used_last includes non-dirty blocks, and it's
|
||||
two loops over many elements. Here we try something simpler.
|
||||
If there are no blocks in changed_blocks[file_hash], we should hit
|
||||
zeroes and skip them.
|
||||
*/
|
||||
uint file_hash;
|
||||
for (file_hash= 0; file_hash < PAGECACHE_CHANGED_BLOCKS_HASH; file_hash++)
|
||||
{
|
||||
PAGECACHE_BLOCK_LINK *block;
|
||||
for (block= pagecache->changed_blocks[file_hash] ;
|
||||
block;
|
||||
block= block->next_changed)
|
||||
{
|
||||
DBUG_ASSERT(block->hash_link != NULL);
|
||||
DBUG_ASSERT(block->status & BLOCK_CHANGED);
|
||||
if (block->type != PAGECACHE_LSN_PAGE)
|
||||
{
|
||||
/* no need to store it in the checkpoint record */
|
||||
continue;
|
||||
}
|
||||
/* Q: two "block"s cannot have the same "hash_link", right? */
|
||||
int8store(ptr, block->hash_link->pageno);
|
||||
ptr+= 8;
|
||||
/* I assume rec_lsn will be member of "block", not of "hash_link" */
|
||||
int8store(ptr, block->rec_lsn);
|
||||
ptr+= 8;
|
||||
stored_LRD_size++;
|
||||
set_if_bigger(candidate_max_rec_lsn_at_last_checkpoint,
|
||||
block->rec_lsn);
|
||||
}
|
||||
}
|
||||
pagecache_pthread_mutex_unlock(&pagecache->cache_lock);
|
||||
DBUG_ASSERT(stored_LRD_size <= pagecache->blocks_changed);
|
||||
int8store(string1.str+8, stored_LRD_size);
|
||||
string1.length= 8+8+(8+8)*stored_LRD_size;
|
||||
|
||||
/* STEP 2: fetch information about transactions */
|
||||
|
||||
/*
|
||||
If trx are in more than one list (e.g. three:
|
||||
running transactions, committed transactions, purge queue), we can either
|
||||
take mutexes of all three together or do crabbing.
|
||||
But if an element can move from list 1 to list 3 without passing through
|
||||
list 2, crabbing is dangerous.
|
||||
Hopefully it's ok to take 3 mutexes together...
|
||||
Otherwise I'll have to make sure I miss no important trx and I handle dups.
|
||||
*/
|
||||
lock(global_transactions_list_mutex); /* or 3 mutexes if there are 3 */
|
||||
string2.length= 8+(8+8)*trx_list->count;
|
||||
if (NULL == (string2.str= my_malloc(string2.length)))
|
||||
goto err;
|
||||
ptr= string2.str;
|
||||
int8store(ptr, trx_list->count);
|
||||
ptr+= 8;
|
||||
for (el= trx_list->first; el; el= el->next)
|
||||
{
|
||||
/* possibly latch el.rwlock */
|
||||
*ptr= el->state;
|
||||
ptr++;
|
||||
int7store(ptr, el->long_trans_id);
|
||||
ptr+= 7;
|
||||
int2store(ptr, el->short_trans_id);
|
||||
ptr+= 2;
|
||||
int8store(ptr, el->undo_lsn);
|
||||
ptr+= 8;
|
||||
int8store(ptr, el->undo_purge_lsn);
|
||||
ptr+= 8;
|
||||
/*
|
||||
if no latch, use double variable of type ULONGLONG_CONSISTENT in
|
||||
st_transaction, or even no need if Intel >=486
|
||||
*/
|
||||
int8store(ptr, el->first_undo_lsn);
|
||||
ptr+= 8;
|
||||
/* possibly unlatch el.rwlock */
|
||||
}
|
||||
unlock(global_transactions_list_mutex);
|
||||
|
||||
/* STEP 3: fetch information about table files */
|
||||
|
||||
/* This global mutex is in fact THR_LOCK_maria (see ma_open()) */
|
||||
lock(global_share_list_mutex);
|
||||
string3.length= 8+(8+8)*share_list->count;
|
||||
if (NULL == (string3.str= my_malloc(string3.length)))
|
||||
goto err;
|
||||
ptr= string3.str;
|
||||
/* possibly latch each MARIA_SHARE, one by one, like this: */
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
/*
|
||||
We'll copy the file id (a bit like share->kfile), the file name
|
||||
(like share->unique_file_name[_length]).
|
||||
*/
|
||||
make_copy_of_global_share_list_to_array;
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
unlock(global_share_list_mutex);
|
||||
|
||||
/* work on copy */
|
||||
int8store(ptr, elements_in_array);
|
||||
ptr+= 8;
|
||||
for (el in array)
|
||||
{
|
||||
int8store(ptr, array[...].file_id);
|
||||
ptr+= 8;
|
||||
memcpy(ptr, array[...].file_name, ...);
|
||||
ptr+= ...;
|
||||
/*
|
||||
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 */
|
||||
|
||||
string_array[0]= string1;
|
||||
string_array[1]= string2;
|
||||
string_array[2]= string3;
|
||||
string_array[3]= NULL;
|
||||
|
||||
checkpoint_lsn= log_write_record(LOGREC_CHECKPOINT,
|
||||
&system_trans, string_array);
|
||||
|
||||
/*
|
||||
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;
|
||||
|
||||
goto end;
|
||||
|
||||
err:
|
||||
print_error_to_error_log(the_error_message);
|
||||
candidate_max_rec_lsn_at_last_checkpoint= LSN_IMPOSSIBLE;
|
||||
|
||||
end:
|
||||
my_free(buffer1.str, MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(buffer2.str, MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(buffer3.str, MYF(MY_ALLOW_ZERO_PTR));
|
||||
|
||||
DBUG_RETURN(candidate_max_rec_lsn_at_last_checkpoint);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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);
|
||||
}
|
||||
...;
|
||||
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 (checkpoint_request < level)
|
||||
{
|
||||
/* no equal or stronger running or to run, we post request */
|
||||
/*
|
||||
note that thousands of requests for checkpoints are going to come all
|
||||
at the same time (when the log bound
|
||||
MAX_LOG_BYTES_WRITTEN_BETWEEN_CHECKPOINTS is passed), so it may not be a
|
||||
good idea for each of them to broadcast a cond to wake up the background
|
||||
checkpoint thread. We just don't broacast a cond, the checkpoint thread
|
||||
(see least_recently_dirtied.c) will notice our request in max a few
|
||||
seconds.
|
||||
*/
|
||||
checkpoint_request= 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.
|
||||
*/
|
||||
}
|
||||
35
storage/maria/ma_checkpoint.h
Normal file
35
storage/maria/ma_checkpoint.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3071 Maria checkpoint
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/* This is the interface of this module. */
|
||||
|
||||
typedef enum enum_checkpoint_level {
|
||||
NONE=-1,
|
||||
INDIRECT, /* just write dirty_pages, transactions table and sync files */
|
||||
MEDIUM, /* also flush all dirty pages which were already dirty at prev checkpoint*/
|
||||
FULL /* also flush all dirty pages */
|
||||
} CHECKPOINT_LEVEL;
|
||||
|
||||
void request_asynchronous_checkpoint(CHECKPOINT_LEVEL level);
|
||||
my_bool execute_synchronous_checkpoint(CHECKPOINT_LEVEL level);
|
||||
my_bool execute_asynchronous_checkpoint_if_any();
|
||||
/* that's all that's needed in the interface */
|
||||
65
storage/maria/ma_checksum.c
Normal file
65
storage/maria/ma_checksum.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Calculate a checksum for a row */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
ha_checksum _ma_checksum(MARIA_HA *info, const byte *buf)
|
||||
{
|
||||
uint i;
|
||||
ha_checksum crc=0;
|
||||
MARIA_COLUMNDEF *rec=info->s->rec;
|
||||
|
||||
for (i=info->s->base.fields ; i-- ; buf+=(rec++)->length)
|
||||
{
|
||||
const byte *pos;
|
||||
ulong length;
|
||||
switch (rec->type) {
|
||||
case FIELD_BLOB:
|
||||
{
|
||||
length= _ma_calc_blob_length(rec->length-
|
||||
maria_portable_sizeof_char_ptr,
|
||||
buf);
|
||||
memcpy((char*) &pos, buf+rec->length- maria_portable_sizeof_char_ptr,
|
||||
sizeof(char*));
|
||||
break;
|
||||
}
|
||||
case FIELD_VARCHAR:
|
||||
{
|
||||
uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length-1);
|
||||
if (pack_length == 1)
|
||||
length= (ulong) *(uchar*) buf;
|
||||
else
|
||||
length= uint2korr(buf);
|
||||
pos= buf+pack_length;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
length=rec->length;
|
||||
pos=buf;
|
||||
break;
|
||||
}
|
||||
crc=my_checksum(crc, pos ? pos : "", length);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
ha_checksum _ma_static_checksum(MARIA_HA *info, const byte *pos)
|
||||
{
|
||||
return my_checksum(0, pos, info->s->base.reclength);
|
||||
}
|
||||
123
storage/maria/ma_close.c
Normal file
123
storage/maria/ma_close.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* close a isam-database */
|
||||
/*
|
||||
TODO:
|
||||
We need to have a separate mutex on the closed file to allow other threads
|
||||
to open other files during the time we flush the cache and close this file
|
||||
*/
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
int maria_close(register MARIA_HA *info)
|
||||
{
|
||||
int error=0,flag;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_close");
|
||||
DBUG_PRINT("enter",("base: %lx reopen: %u locks: %u",
|
||||
info,(uint) share->reopen, (uint) share->tot_locks));
|
||||
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
if (info->lock_type == F_EXTRA_LCK)
|
||||
info->lock_type=F_UNLCK; /* HA_EXTRA_NO_USER_CHANGE */
|
||||
|
||||
if (share->reopen == 1 && share->kfile >= 0)
|
||||
_ma_decrement_open_count(info);
|
||||
|
||||
if (info->lock_type != F_UNLCK)
|
||||
{
|
||||
if (maria_lock_database(info,F_UNLCK))
|
||||
error=my_errno;
|
||||
}
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
|
||||
if (share->options & HA_OPTION_READ_ONLY_DATA)
|
||||
{
|
||||
share->r_locks--;
|
||||
share->tot_locks--;
|
||||
}
|
||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||
{
|
||||
if (end_io_cache(&info->rec_cache))
|
||||
error=my_errno;
|
||||
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
|
||||
}
|
||||
flag= !--share->reopen;
|
||||
maria_open_list=list_delete(maria_open_list,&info->open_list);
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
|
||||
my_free(_ma_get_rec_buff_ptr(info, info->rec_buff), MYF(MY_ALLOW_ZERO_PTR));
|
||||
if (flag)
|
||||
{
|
||||
if (share->kfile >= 0 &&
|
||||
flush_key_blocks(share->key_cache, share->kfile,
|
||||
share->temporary ? FLUSH_IGNORE_CHANGED :
|
||||
FLUSH_RELEASE))
|
||||
error=my_errno;
|
||||
if (share->kfile >= 0)
|
||||
{
|
||||
/*
|
||||
If we are crashed, we can safely flush the current state as it will
|
||||
not change the crashed state.
|
||||
We can NOT write the state in other cases as other threads
|
||||
may be using the file at this point
|
||||
*/
|
||||
if (share->mode != O_RDONLY && maria_is_crashed(info))
|
||||
_ma_state_info_write(share->kfile, &share->state, 1);
|
||||
if (my_close(share->kfile,MYF(0)))
|
||||
error = my_errno;
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
if (share->file_map)
|
||||
_ma_unmap_file(info);
|
||||
#endif
|
||||
if (share->decode_trees)
|
||||
{
|
||||
my_free((gptr) share->decode_trees,MYF(0));
|
||||
my_free((gptr) share->decode_tables,MYF(0));
|
||||
}
|
||||
#ifdef THREAD
|
||||
thr_lock_delete(&share->lock);
|
||||
VOID(pthread_mutex_destroy(&share->intern_lock));
|
||||
{
|
||||
int i,keys;
|
||||
keys = share->state.header.keys;
|
||||
VOID(rwlock_destroy(&share->mmap_lock));
|
||||
for(i=0; i<keys; i++) {
|
||||
VOID(rwlock_destroy(&share->key_root_lock[i]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
my_free((gptr) info->s,MYF(0));
|
||||
}
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
if (info->ftparser_param)
|
||||
{
|
||||
my_free((gptr)info->ftparser_param, MYF(0));
|
||||
info->ftparser_param= 0;
|
||||
}
|
||||
if (info->dfile >= 0 && my_close(info->dfile,MYF(0)))
|
||||
error = my_errno;
|
||||
|
||||
my_free((gptr) info,MYF(0));
|
||||
|
||||
if (error)
|
||||
{
|
||||
DBUG_RETURN(my_errno=error);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
} /* maria_close */
|
||||
342
storage/maria/ma_control_file.c
Normal file
342
storage/maria/ma_control_file.c
Normal file
@@ -0,0 +1,342 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3234 Maria control file
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
#include "maria_def.h"
|
||||
#include "ma_control_file.h"
|
||||
|
||||
/* Here is the implementation of this module */
|
||||
|
||||
/*
|
||||
a control file contains 3 objects: magic string, LSN of last checkpoint,
|
||||
number of last log.
|
||||
*/
|
||||
|
||||
/* total size should be < sector size for atomic write operation */
|
||||
#define CONTROL_FILE_MAGIC_STRING "MACF"
|
||||
#define CONTROL_FILE_MAGIC_STRING_OFFSET 0
|
||||
#define CONTROL_FILE_MAGIC_STRING_SIZE (sizeof(CONTROL_FILE_MAGIC_STRING)-1)
|
||||
#define CONTROL_FILE_CHECKSUM_OFFSET (CONTROL_FILE_MAGIC_STRING_OFFSET + CONTROL_FILE_MAGIC_STRING_SIZE)
|
||||
#define CONTROL_FILE_CHECKSUM_SIZE 1
|
||||
#define CONTROL_FILE_LSN_OFFSET (CONTROL_FILE_CHECKSUM_OFFSET + CONTROL_FILE_CHECKSUM_SIZE)
|
||||
#define CONTROL_FILE_LSN_SIZE (4+4)
|
||||
#define CONTROL_FILE_FILENO_OFFSET (CONTROL_FILE_LSN_OFFSET + CONTROL_FILE_LSN_SIZE)
|
||||
#define CONTROL_FILE_FILENO_SIZE 4
|
||||
#define CONTROL_FILE_SIZE (CONTROL_FILE_FILENO_OFFSET + CONTROL_FILE_FILENO_SIZE)
|
||||
|
||||
/*
|
||||
This module owns these two vars.
|
||||
uint32 is always atomically updated, but LSN is 8 bytes, we will need
|
||||
provisions to ensure that it's updated atomically in
|
||||
ma_control_file_write_and_force(). Probably the log mutex could be
|
||||
used. TODO.
|
||||
*/
|
||||
LSN last_checkpoint_lsn;
|
||||
uint32 last_logno;
|
||||
|
||||
|
||||
/*
|
||||
Control file is less then 512 bytes (a disk sector),
|
||||
to be as atomic as possible
|
||||
*/
|
||||
static int control_file_fd= -1;
|
||||
|
||||
static void lsn8store(char *buffer, const LSN *lsn)
|
||||
{
|
||||
int4store(buffer, lsn->file_no);
|
||||
int4store(buffer + CONTROL_FILE_FILENO_SIZE, lsn->rec_offset);
|
||||
}
|
||||
|
||||
static LSN lsn8korr(char *buffer)
|
||||
{
|
||||
LSN tmp;
|
||||
tmp.file_no= uint4korr(buffer);
|
||||
tmp.rec_offset= uint4korr(buffer + CONTROL_FILE_FILENO_SIZE);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static char simple_checksum(char *buffer, uint size)
|
||||
{
|
||||
/* TODO: improve this sum if we want */
|
||||
char s= 0;
|
||||
uint i;
|
||||
for (i= 0; i<size; i++)
|
||||
s+= buffer[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize control file subsystem
|
||||
|
||||
SYNOPSIS
|
||||
ma_control_file_create_or_open()
|
||||
|
||||
Looks for the control file. If absent, it's a fresh start, creates file.
|
||||
If present, reads it to find out last checkpoint's LSN and last log, updates
|
||||
the last_checkpoint_lsn and last_logno global variables.
|
||||
Called at engine's start.
|
||||
|
||||
The format of the control file is:
|
||||
4 bytes: magic string
|
||||
1 byte: checksum of the following bytes
|
||||
4 bytes: number of log where last checkpoint is
|
||||
4 bytes: offset in log where last checkpoint is
|
||||
4 bytes: number of last log
|
||||
|
||||
RETURN
|
||||
0 - OK
|
||||
1 - Error (in which case the file is left closed)
|
||||
*/
|
||||
CONTROL_FILE_ERROR ma_control_file_create_or_open()
|
||||
{
|
||||
char buffer[CONTROL_FILE_SIZE];
|
||||
char name[FN_REFLEN];
|
||||
MY_STAT stat_buff;
|
||||
my_bool create_file;
|
||||
int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR;
|
||||
int error= CONTROL_FILE_UNKNOWN_ERROR;
|
||||
DBUG_ENTER("ma_control_file_create_or_open");
|
||||
|
||||
/*
|
||||
If you change sizes in the #defines, you at least have to change the
|
||||
"*store" and "*korr" calls in this file, and can even create backward
|
||||
compatibility problems. Beware!
|
||||
*/
|
||||
DBUG_ASSERT(CONTROL_FILE_LSN_SIZE == (4+4));
|
||||
DBUG_ASSERT(CONTROL_FILE_FILENO_SIZE == 4);
|
||||
|
||||
if (control_file_fd >= 0) /* already open */
|
||||
DBUG_RETURN(0);
|
||||
|
||||
if (fn_format(name, CONTROL_FILE_BASE_NAME,
|
||||
maria_data_root, "", MYF(MY_WME)) == NullS)
|
||||
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
|
||||
|
||||
create_file= test(my_access(name,F_OK));
|
||||
|
||||
if (create_file)
|
||||
{
|
||||
if ((control_file_fd= my_create(name, 0, open_flags, MYF(0))) < 0)
|
||||
DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
|
||||
/*
|
||||
TODO: from "man fsync" on Linux:
|
||||
"fsync does not necessarily ensure that the entry in the directory
|
||||
containing the file has also reached disk. For that an explicit
|
||||
fsync on the file descriptor of the directory is also needed."
|
||||
So if we just created the file we should sync the directory.
|
||||
Maybe there should be a flag of my_create() to do this.
|
||||
|
||||
To be safer we should make sure that there are no logs or data/index
|
||||
files around (indeed it could be that the control file alone was deleted
|
||||
or not restored, and we should not go on with life at this point).
|
||||
|
||||
TODO: For now we trust (this is alpha version), but for beta if would
|
||||
be great to verify.
|
||||
|
||||
We could have a tool which can rebuild the control file, by reading the
|
||||
directory of logs, finding the newest log, reading it to find last
|
||||
checkpoint... Slow but can save your db. For this to be possible, we
|
||||
must always write to the control file right after writing the checkpoint
|
||||
log record, and do nothing in between (i.e. the checkpoint must be
|
||||
usable as soon as it has been written to the log).
|
||||
*/
|
||||
|
||||
LSN imposs_lsn= CONTROL_FILE_IMPOSSIBLE_LSN;
|
||||
uint32 imposs_logno= CONTROL_FILE_IMPOSSIBLE_FILENO;
|
||||
|
||||
/* init the file with these "undefined" values */
|
||||
DBUG_RETURN(ma_control_file_write_and_force(&imposs_lsn, imposs_logno,
|
||||
CONTROL_FILE_UPDATE_ALL));
|
||||
}
|
||||
|
||||
/* Otherwise, file exists */
|
||||
|
||||
if ((control_file_fd= my_open(name, open_flags, MYF(MY_WME))) < 0)
|
||||
goto err;
|
||||
|
||||
if (my_stat(name, &stat_buff, MYF(MY_WME)) == NULL)
|
||||
goto err;
|
||||
|
||||
if ((uint)stat_buff.st_size < CONTROL_FILE_SIZE)
|
||||
{
|
||||
/*
|
||||
Given that normally we write only a sector and it's atomic, the only
|
||||
possibility for a file to be of too short size is if we crashed at the
|
||||
very first startup, between file creation and file write. Quite unlikely
|
||||
(and can be made even more unlikely by doing this: create a temp file,
|
||||
write it, and then rename it to be the control file).
|
||||
What's more likely is if someone forgot to restore the control file,
|
||||
just did a "touch control" to try to get Maria to start, or if the
|
||||
disk/filesystem has a problem.
|
||||
So let's be rigid.
|
||||
*/
|
||||
/*
|
||||
TODO: store a message "too small file" somewhere, so that it goes to
|
||||
MySQL's error log at startup.
|
||||
*/
|
||||
error= CONTROL_FILE_TOO_SMALL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((uint)stat_buff.st_size > CONTROL_FILE_SIZE)
|
||||
{
|
||||
/* TODO: store "too big file" message */
|
||||
error= CONTROL_FILE_TOO_BIG;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (my_read(control_file_fd, buffer, CONTROL_FILE_SIZE,
|
||||
MYF(MY_FNABP | MY_WME)))
|
||||
goto err;
|
||||
if (memcmp(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
|
||||
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE))
|
||||
{
|
||||
/* TODO: store message "bad magic string" somewhere */
|
||||
error= CONTROL_FILE_BAD_MAGIC_STRING;
|
||||
goto err;
|
||||
}
|
||||
if (simple_checksum(buffer + CONTROL_FILE_LSN_OFFSET,
|
||||
CONTROL_FILE_SIZE - CONTROL_FILE_LSN_OFFSET) !=
|
||||
buffer[CONTROL_FILE_CHECKSUM_OFFSET])
|
||||
{
|
||||
/* TODO: store message "checksum mismatch" somewhere */
|
||||
error= CONTROL_FILE_BAD_CHECKSUM;
|
||||
goto err;
|
||||
}
|
||||
last_checkpoint_lsn= lsn8korr(buffer + CONTROL_FILE_LSN_OFFSET);
|
||||
last_logno= uint4korr(buffer + CONTROL_FILE_FILENO_OFFSET);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
err:
|
||||
ma_control_file_end();
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Write information durably to the control file; stores this information into
|
||||
the last_checkpoint_lsn and last_logno global variables.
|
||||
Called when we have created a new log (after syncing this log's creation)
|
||||
and when we have written a checkpoint (after syncing this log record).
|
||||
|
||||
SYNOPSIS
|
||||
ma_control_file_write_and_force()
|
||||
checkpoint_lsn LSN of last checkpoint
|
||||
logno last log file number
|
||||
objs_to_write which of the arguments should be used as new values
|
||||
(for example, CONTROL_FILE_UPDATE_ONLY_LSN will not
|
||||
write the logno argument to the control file and will
|
||||
not update the last_logno global variable); can be:
|
||||
CONTROL_FILE_UPDATE_ALL
|
||||
CONTROL_FILE_UPDATE_ONLY_LSN
|
||||
CONTROL_FILE_UPDATE_ONLY_LOGNO.
|
||||
|
||||
NOTE
|
||||
We always want to do one single my_pwrite() here to be as atomic as
|
||||
possible.
|
||||
|
||||
RETURN
|
||||
0 - OK
|
||||
1 - Error
|
||||
*/
|
||||
|
||||
int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
|
||||
uint objs_to_write)
|
||||
{
|
||||
char buffer[CONTROL_FILE_SIZE];
|
||||
my_bool update_checkpoint_lsn= FALSE, update_logno= FALSE;
|
||||
DBUG_ENTER("ma_control_file_write_and_force");
|
||||
|
||||
DBUG_ASSERT(control_file_fd >= 0); /* must be open */
|
||||
|
||||
memcpy(buffer + CONTROL_FILE_MAGIC_STRING_OFFSET,
|
||||
CONTROL_FILE_MAGIC_STRING, CONTROL_FILE_MAGIC_STRING_SIZE);
|
||||
|
||||
/* TODO: you need some protection to be able to read last_* global vars */
|
||||
|
||||
if (objs_to_write == CONTROL_FILE_UPDATE_ONLY_LSN)
|
||||
update_checkpoint_lsn= TRUE;
|
||||
else if (objs_to_write == CONTROL_FILE_UPDATE_ONLY_LOGNO)
|
||||
update_logno= TRUE;
|
||||
else if (objs_to_write == CONTROL_FILE_UPDATE_ALL)
|
||||
update_checkpoint_lsn= update_logno= TRUE;
|
||||
else /* incorrect value of objs_to_write */
|
||||
DBUG_ASSERT(0);
|
||||
|
||||
if (update_checkpoint_lsn)
|
||||
lsn8store(buffer + CONTROL_FILE_LSN_OFFSET, checkpoint_lsn);
|
||||
else /* store old value == change nothing */
|
||||
lsn8store(buffer + CONTROL_FILE_LSN_OFFSET, &last_checkpoint_lsn);
|
||||
|
||||
if (update_logno)
|
||||
int4store(buffer + CONTROL_FILE_FILENO_OFFSET, logno);
|
||||
else
|
||||
int4store(buffer + CONTROL_FILE_FILENO_OFFSET, last_logno);
|
||||
|
||||
buffer[CONTROL_FILE_CHECKSUM_OFFSET]=
|
||||
simple_checksum(buffer + CONTROL_FILE_LSN_OFFSET,
|
||||
CONTROL_FILE_SIZE - CONTROL_FILE_LSN_OFFSET);
|
||||
|
||||
if (my_pwrite(control_file_fd, buffer, sizeof(buffer),
|
||||
0, MYF(MY_FNABP | MY_WME)) ||
|
||||
my_sync(control_file_fd, MYF(MY_WME)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/* TODO: you need some protection to be able to write last_* global vars */
|
||||
if (update_checkpoint_lsn)
|
||||
last_checkpoint_lsn= *checkpoint_lsn;
|
||||
if (update_logno)
|
||||
last_logno= logno;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Free resources taken by control file subsystem
|
||||
|
||||
SYNOPSIS
|
||||
ma_control_file_end()
|
||||
*/
|
||||
|
||||
int ma_control_file_end()
|
||||
{
|
||||
int close_error;
|
||||
DBUG_ENTER("ma_control_file_end");
|
||||
|
||||
if (control_file_fd < 0) /* already closed */
|
||||
DBUG_RETURN(0);
|
||||
|
||||
close_error= my_close(control_file_fd, MYF(MY_WME));
|
||||
/*
|
||||
As my_close() frees structures even if close() fails, we do the same,
|
||||
i.e. we mark the file as closed in all cases.
|
||||
*/
|
||||
control_file_fd= -1;
|
||||
/*
|
||||
As this module owns these variables, closing the module forbids access to
|
||||
them (just a safety):
|
||||
*/
|
||||
last_checkpoint_lsn= CONTROL_FILE_IMPOSSIBLE_LSN;
|
||||
last_logno= CONTROL_FILE_IMPOSSIBLE_FILENO;
|
||||
|
||||
DBUG_RETURN(close_error);
|
||||
}
|
||||
100
storage/maria/ma_control_file.h
Normal file
100
storage/maria/ma_control_file.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3234 Maria control file
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
#ifndef _control_file_h
|
||||
#define _control_file_h
|
||||
|
||||
/*
|
||||
Not everybody needs to call the control file that's why control_file.h is
|
||||
not in maria_def.h. However, policy or habit may want to change this.
|
||||
*/
|
||||
|
||||
#ifndef REMOVE_WHEN_SANJA_PUSHES_LOG_HANDLER
|
||||
/*
|
||||
this is to get the control file to compile, until Sanja pushes the log
|
||||
handler which will supersede those definitions.
|
||||
*/
|
||||
typedef struct st_lsn {
|
||||
uint32 file_no;
|
||||
uint32 rec_offset;
|
||||
} LSN;
|
||||
#define maria_data_root "."
|
||||
#endif
|
||||
|
||||
#define CONTROL_FILE_BASE_NAME "maria_control"
|
||||
/*
|
||||
indicate absence of the log file number; first log is always number 1, 0 is
|
||||
impossible.
|
||||
*/
|
||||
#define CONTROL_FILE_IMPOSSIBLE_FILENO 0
|
||||
/* logs always have a header */
|
||||
#define CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET 0
|
||||
/*
|
||||
indicate absence of LSN.
|
||||
*/
|
||||
#define CONTROL_FILE_IMPOSSIBLE_LSN ((LSN){CONTROL_FILE_IMPOSSIBLE_FILENO,CONTROL_FILE_IMPOSSIBLE_LOG_OFFSET})
|
||||
|
||||
/* Here is the interface of this module */
|
||||
|
||||
/*
|
||||
LSN of the last checkoint
|
||||
(if last_checkpoint_lsn.file_no == CONTROL_FILE_IMPOSSIBLE_FILENO
|
||||
then there was never a checkpoint)
|
||||
*/
|
||||
extern LSN last_checkpoint_lsn;
|
||||
/*
|
||||
Last log number (if last_logno ==
|
||||
CONTROL_FILE_IMPOSSIBLE_FILENO then there is no log file yet)
|
||||
*/
|
||||
extern uint32 last_logno;
|
||||
|
||||
/*
|
||||
Looks for the control file. If absent, it's a fresh start, create file.
|
||||
If present, read it to find out last checkpoint's LSN and last log.
|
||||
Called at engine's start.
|
||||
*/
|
||||
typedef enum enum_control_file_error {
|
||||
CONTROL_FILE_OK= 0,
|
||||
CONTROL_FILE_TOO_SMALL,
|
||||
CONTROL_FILE_TOO_BIG,
|
||||
CONTROL_FILE_BAD_MAGIC_STRING,
|
||||
CONTROL_FILE_BAD_CHECKSUM,
|
||||
CONTROL_FILE_UNKNOWN_ERROR /* any other error */
|
||||
} CONTROL_FILE_ERROR;
|
||||
CONTROL_FILE_ERROR ma_control_file_create_or_open();
|
||||
|
||||
/*
|
||||
Write information durably to the control file.
|
||||
Called when we have created a new log (after syncing this log's creation)
|
||||
and when we have written a checkpoint (after syncing this log record).
|
||||
*/
|
||||
#define CONTROL_FILE_UPDATE_ALL 0
|
||||
#define CONTROL_FILE_UPDATE_ONLY_LSN 1
|
||||
#define CONTROL_FILE_UPDATE_ONLY_LOGNO 2
|
||||
int ma_control_file_write_and_force(const LSN *checkpoint_lsn, uint32 logno,
|
||||
uint objs_to_write);
|
||||
|
||||
|
||||
/* Free resources taken by control file subsystem */
|
||||
int ma_control_file_end();
|
||||
|
||||
#endif
|
||||
870
storage/maria/ma_create.c
Normal file
870
storage/maria/ma_create.c
Normal file
@@ -0,0 +1,870 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Create a MARIA table */
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
#include "ma_sp_defs.h"
|
||||
|
||||
#if defined(MSDOS) || defined(__WIN__)
|
||||
#ifdef __WIN__
|
||||
#include <fcntl.h>
|
||||
#else
|
||||
#include <process.h> /* Prototype for getpid */
|
||||
#endif
|
||||
#endif
|
||||
#include <m_ctype.h>
|
||||
|
||||
/*
|
||||
Old options is used when recreating database, from maria_chk
|
||||
*/
|
||||
|
||||
int maria_create(const char *name,uint keys,MARIA_KEYDEF *keydefs,
|
||||
uint columns, MARIA_COLUMNDEF *recinfo,
|
||||
uint uniques, MARIA_UNIQUEDEF *uniquedefs,
|
||||
MARIA_CREATE_INFO *ci,uint flags)
|
||||
{
|
||||
register uint i,j;
|
||||
File dfile,file;
|
||||
int errpos,save_errno, create_mode= O_RDWR | O_TRUNC;
|
||||
myf create_flag;
|
||||
uint fields,length,max_key_length,packed,pointer,real_length_diff,
|
||||
key_length,info_length,key_segs,options,min_key_length_skip,
|
||||
base_pos,long_varchar_count,varchar_length,
|
||||
max_key_block_length,unique_key_parts,fulltext_keys,offset;
|
||||
uint aligned_key_start, block_length;
|
||||
ulong reclength, real_reclength,min_pack_length;
|
||||
char filename[FN_REFLEN],linkname[FN_REFLEN], *linkname_ptr;
|
||||
ulong pack_reclength;
|
||||
ulonglong tot_length,max_rows, tmp;
|
||||
enum en_fieldtype type;
|
||||
MARIA_SHARE share;
|
||||
MARIA_KEYDEF *keydef,tmp_keydef;
|
||||
MARIA_UNIQUEDEF *uniquedef;
|
||||
HA_KEYSEG *keyseg,tmp_keyseg;
|
||||
MARIA_COLUMNDEF *rec;
|
||||
ulong *rec_per_key_part;
|
||||
my_off_t key_root[HA_MAX_POSSIBLE_KEY],key_del[MARIA_MAX_KEY_BLOCK_SIZE];
|
||||
MARIA_CREATE_INFO tmp_create_info;
|
||||
DBUG_ENTER("maria_create");
|
||||
DBUG_PRINT("enter", ("keys: %u columns: %u uniques: %u flags: %u",
|
||||
keys, columns, uniques, flags));
|
||||
|
||||
if (!ci)
|
||||
{
|
||||
bzero((char*) &tmp_create_info,sizeof(tmp_create_info));
|
||||
ci=&tmp_create_info;
|
||||
}
|
||||
|
||||
if (keys + uniques > MARIA_MAX_KEY || columns == 0)
|
||||
{
|
||||
DBUG_RETURN(my_errno=HA_WRONG_CREATE_OPTION);
|
||||
}
|
||||
LINT_INIT(dfile);
|
||||
LINT_INIT(file);
|
||||
errpos=0;
|
||||
options=0;
|
||||
bzero((byte*) &share,sizeof(share));
|
||||
|
||||
if (flags & HA_DONT_TOUCH_DATA)
|
||||
{
|
||||
if (!(ci->old_options & HA_OPTION_TEMP_COMPRESS_RECORD))
|
||||
options=ci->old_options &
|
||||
(HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD |
|
||||
HA_OPTION_READ_ONLY_DATA | HA_OPTION_CHECKSUM |
|
||||
HA_OPTION_TMP_TABLE | HA_OPTION_DELAY_KEY_WRITE);
|
||||
else
|
||||
options=ci->old_options &
|
||||
(HA_OPTION_CHECKSUM | HA_OPTION_TMP_TABLE | HA_OPTION_DELAY_KEY_WRITE);
|
||||
}
|
||||
|
||||
if (ci->reloc_rows > ci->max_rows)
|
||||
ci->reloc_rows=ci->max_rows; /* Check if wrong parameter */
|
||||
|
||||
if (!(rec_per_key_part=
|
||||
(ulong*) my_malloc((keys + uniques)*HA_MAX_KEY_SEG*sizeof(long),
|
||||
MYF(MY_WME | MY_ZEROFILL))))
|
||||
DBUG_RETURN(my_errno);
|
||||
|
||||
/* Start by checking fields and field-types used */
|
||||
|
||||
reclength=varchar_length=long_varchar_count=packed=
|
||||
min_pack_length=pack_reclength=0;
|
||||
for (rec=recinfo, fields=0 ;
|
||||
fields != columns ;
|
||||
rec++,fields++)
|
||||
{
|
||||
reclength+=rec->length;
|
||||
if ((type=(enum en_fieldtype) rec->type) != FIELD_NORMAL &&
|
||||
type != FIELD_CHECK)
|
||||
{
|
||||
packed++;
|
||||
if (type == FIELD_BLOB)
|
||||
{
|
||||
share.base.blobs++;
|
||||
if (pack_reclength != INT_MAX32)
|
||||
{
|
||||
if (rec->length == 4+maria_portable_sizeof_char_ptr)
|
||||
pack_reclength= INT_MAX32;
|
||||
else
|
||||
pack_reclength+=(1 << ((rec->length-maria_portable_sizeof_char_ptr)*8)); /* Max blob length */
|
||||
}
|
||||
}
|
||||
else if (type == FIELD_SKIP_PRESPACE ||
|
||||
type == FIELD_SKIP_ENDSPACE)
|
||||
{
|
||||
if (pack_reclength != INT_MAX32)
|
||||
pack_reclength+= rec->length > 255 ? 2 : 1;
|
||||
min_pack_length++;
|
||||
}
|
||||
else if (type == FIELD_VARCHAR)
|
||||
{
|
||||
varchar_length+= rec->length-1; /* Used for min_pack_length */
|
||||
packed--;
|
||||
pack_reclength++;
|
||||
min_pack_length++;
|
||||
/* We must test for 257 as length includes pack-length */
|
||||
if (test(rec->length >= 257))
|
||||
{
|
||||
long_varchar_count++;
|
||||
pack_reclength+= 2; /* May be packed on 3 bytes */
|
||||
}
|
||||
}
|
||||
else if (type != FIELD_SKIP_ZERO)
|
||||
{
|
||||
min_pack_length+=rec->length;
|
||||
packed--; /* Not a pack record type */
|
||||
}
|
||||
}
|
||||
else /* FIELD_NORMAL */
|
||||
min_pack_length+=rec->length;
|
||||
}
|
||||
if ((packed & 7) == 1)
|
||||
{ /* Bad packing, try to remove a zero-field */
|
||||
while (rec != recinfo)
|
||||
{
|
||||
rec--;
|
||||
if (rec->type == (int) FIELD_SKIP_ZERO && rec->length == 1)
|
||||
{
|
||||
rec->type=(int) FIELD_NORMAL;
|
||||
packed--;
|
||||
min_pack_length++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (packed || (flags & HA_PACK_RECORD))
|
||||
options|=HA_OPTION_PACK_RECORD; /* Must use packed records */
|
||||
/* We can't use checksum with static length rows */
|
||||
if (!(options & HA_OPTION_PACK_RECORD))
|
||||
options&= ~HA_OPTION_CHECKSUM;
|
||||
if (!(options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
|
||||
min_pack_length+= varchar_length;
|
||||
if (flags & HA_CREATE_TMP_TABLE)
|
||||
{
|
||||
options|= HA_OPTION_TMP_TABLE;
|
||||
create_mode|= O_EXCL | O_NOFOLLOW;
|
||||
}
|
||||
if (flags & HA_CREATE_CHECKSUM || (options & HA_OPTION_CHECKSUM))
|
||||
{
|
||||
options|= HA_OPTION_CHECKSUM;
|
||||
min_pack_length++;
|
||||
}
|
||||
if (flags & HA_CREATE_DELAY_KEY_WRITE)
|
||||
options|= HA_OPTION_DELAY_KEY_WRITE;
|
||||
if (flags & HA_CREATE_RELIES_ON_SQL_LAYER)
|
||||
options|= HA_OPTION_RELIES_ON_SQL_LAYER;
|
||||
|
||||
packed=(packed+7)/8;
|
||||
if (pack_reclength != INT_MAX32)
|
||||
pack_reclength+= reclength+packed +
|
||||
test(test_all_bits(options, HA_OPTION_CHECKSUM | HA_PACK_RECORD));
|
||||
min_pack_length+=packed;
|
||||
|
||||
if (!ci->data_file_length && ci->max_rows)
|
||||
{
|
||||
if (pack_reclength == INT_MAX32 ||
|
||||
(~(ulonglong) 0)/ci->max_rows < (ulonglong) pack_reclength)
|
||||
ci->data_file_length= ~(ulonglong) 0;
|
||||
else
|
||||
ci->data_file_length=(ulonglong) ci->max_rows*pack_reclength;
|
||||
}
|
||||
else if (!ci->max_rows)
|
||||
ci->max_rows=(ha_rows) (ci->data_file_length/(min_pack_length +
|
||||
((options & HA_OPTION_PACK_RECORD) ?
|
||||
3 : 0)));
|
||||
|
||||
if (options & (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD))
|
||||
pointer=maria_get_pointer_length(ci->data_file_length,maria_data_pointer_size);
|
||||
else
|
||||
pointer=maria_get_pointer_length(ci->max_rows,maria_data_pointer_size);
|
||||
if (!(max_rows=(ulonglong) ci->max_rows))
|
||||
max_rows= ((((ulonglong) 1 << (pointer*8)) -1) / min_pack_length);
|
||||
|
||||
|
||||
real_reclength=reclength;
|
||||
if (!(options & (HA_OPTION_COMPRESS_RECORD | HA_OPTION_PACK_RECORD)))
|
||||
{
|
||||
if (reclength <= pointer)
|
||||
reclength=pointer+1; /* reserve place for delete link */
|
||||
}
|
||||
else
|
||||
reclength+= long_varchar_count; /* We need space for varchar! */
|
||||
|
||||
max_key_length=0; tot_length=0 ; key_segs=0;
|
||||
fulltext_keys=0;
|
||||
max_key_block_length=0;
|
||||
share.state.rec_per_key_part=rec_per_key_part;
|
||||
share.state.key_root=key_root;
|
||||
share.state.key_del=key_del;
|
||||
if (uniques)
|
||||
{
|
||||
max_key_block_length= maria_block_size;
|
||||
max_key_length= MARIA_UNIQUE_HASH_LENGTH + pointer;
|
||||
}
|
||||
|
||||
for (i=0, keydef=keydefs ; i < keys ; i++ , keydef++)
|
||||
{
|
||||
|
||||
share.state.key_root[i]= HA_OFFSET_ERROR;
|
||||
min_key_length_skip=length=real_length_diff=0;
|
||||
key_length=pointer;
|
||||
if (keydef->flag & HA_SPATIAL)
|
||||
{
|
||||
#ifdef HAVE_SPATIAL
|
||||
/* BAR TODO to support 3D and more dimensions in the future */
|
||||
uint sp_segs=SPDIMS*2;
|
||||
keydef->flag=HA_SPATIAL;
|
||||
|
||||
if (flags & HA_DONT_TOUCH_DATA)
|
||||
{
|
||||
/*
|
||||
called by mariachk - i.e. table structure was taken from
|
||||
MYI file and SPATIAL key *does have* additional sp_segs keysegs.
|
||||
keydef->seg here points right at the GEOMETRY segment,
|
||||
so we only need to decrease keydef->keysegs.
|
||||
(see maria_recreate_table() in _ma_check.c)
|
||||
*/
|
||||
keydef->keysegs-=sp_segs-1;
|
||||
}
|
||||
|
||||
for (j=0, keyseg=keydef->seg ; (int) j < keydef->keysegs ;
|
||||
j++, keyseg++)
|
||||
{
|
||||
if (keyseg->type != HA_KEYTYPE_BINARY &&
|
||||
keyseg->type != HA_KEYTYPE_VARBINARY1 &&
|
||||
keyseg->type != HA_KEYTYPE_VARBINARY2)
|
||||
{
|
||||
my_errno=HA_WRONG_CREATE_OPTION;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
keydef->keysegs+=sp_segs;
|
||||
key_length+=SPLEN*sp_segs;
|
||||
length++; /* At least one length byte */
|
||||
min_key_length_skip+=SPLEN*2*SPDIMS;
|
||||
#else
|
||||
my_errno= HA_ERR_UNSUPPORTED;
|
||||
goto err;
|
||||
#endif /*HAVE_SPATIAL*/
|
||||
}
|
||||
else if (keydef->flag & HA_FULLTEXT)
|
||||
{
|
||||
keydef->flag=HA_FULLTEXT | HA_PACK_KEY | HA_VAR_LENGTH_KEY;
|
||||
options|=HA_OPTION_PACK_KEYS; /* Using packed keys */
|
||||
|
||||
for (j=0, keyseg=keydef->seg ; (int) j < keydef->keysegs ;
|
||||
j++, keyseg++)
|
||||
{
|
||||
if (keyseg->type != HA_KEYTYPE_TEXT &&
|
||||
keyseg->type != HA_KEYTYPE_VARTEXT1 &&
|
||||
keyseg->type != HA_KEYTYPE_VARTEXT2)
|
||||
{
|
||||
my_errno=HA_WRONG_CREATE_OPTION;
|
||||
goto err;
|
||||
}
|
||||
if (!(keyseg->flag & HA_BLOB_PART) &&
|
||||
(keyseg->type == HA_KEYTYPE_VARTEXT1 ||
|
||||
keyseg->type == HA_KEYTYPE_VARTEXT2))
|
||||
{
|
||||
/* Make a flag that this is a VARCHAR */
|
||||
keyseg->flag|= HA_VAR_LENGTH_PART;
|
||||
/* Store in bit_start number of bytes used to pack the length */
|
||||
keyseg->bit_start= ((keyseg->type == HA_KEYTYPE_VARTEXT1)?
|
||||
1 : 2);
|
||||
}
|
||||
}
|
||||
|
||||
fulltext_keys++;
|
||||
key_length+= HA_FT_MAXBYTELEN+HA_FT_WLEN;
|
||||
length++; /* At least one length byte */
|
||||
min_key_length_skip+=HA_FT_MAXBYTELEN;
|
||||
real_length_diff=HA_FT_MAXBYTELEN-FT_MAX_WORD_LEN_FOR_SORT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Test if prefix compression */
|
||||
if (keydef->flag & HA_PACK_KEY)
|
||||
{
|
||||
/* Can't use space_compression on number keys */
|
||||
if ((keydef->seg[0].flag & HA_SPACE_PACK) &&
|
||||
keydef->seg[0].type == (int) HA_KEYTYPE_NUM)
|
||||
keydef->seg[0].flag&= ~HA_SPACE_PACK;
|
||||
|
||||
/* Only use HA_PACK_KEY when first segment is a variable length key */
|
||||
if (!(keydef->seg[0].flag & (HA_SPACE_PACK | HA_BLOB_PART |
|
||||
HA_VAR_LENGTH_PART)))
|
||||
{
|
||||
/* pack relative to previous key */
|
||||
keydef->flag&= ~HA_PACK_KEY;
|
||||
keydef->flag|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
|
||||
}
|
||||
else
|
||||
{
|
||||
keydef->seg[0].flag|=HA_PACK_KEY; /* for easyer intern test */
|
||||
keydef->flag|=HA_VAR_LENGTH_KEY;
|
||||
options|=HA_OPTION_PACK_KEYS; /* Using packed keys */
|
||||
}
|
||||
}
|
||||
if (keydef->flag & HA_BINARY_PACK_KEY)
|
||||
options|=HA_OPTION_PACK_KEYS; /* Using packed keys */
|
||||
|
||||
if (keydef->flag & HA_AUTO_KEY && ci->with_auto_increment)
|
||||
share.base.auto_key=i+1;
|
||||
for (j=0, keyseg=keydef->seg ; j < keydef->keysegs ; j++, keyseg++)
|
||||
{
|
||||
/* numbers are stored with high by first to make compression easier */
|
||||
switch (keyseg->type) {
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
case HA_KEYTYPE_INT24:
|
||||
case HA_KEYTYPE_UINT24:
|
||||
case HA_KEYTYPE_INT8:
|
||||
keyseg->flag|= HA_SWAP_KEY;
|
||||
break;
|
||||
case HA_KEYTYPE_VARTEXT1:
|
||||
case HA_KEYTYPE_VARTEXT2:
|
||||
case HA_KEYTYPE_VARBINARY1:
|
||||
case HA_KEYTYPE_VARBINARY2:
|
||||
if (!(keyseg->flag & HA_BLOB_PART))
|
||||
{
|
||||
/* Make a flag that this is a VARCHAR */
|
||||
keyseg->flag|= HA_VAR_LENGTH_PART;
|
||||
/* Store in bit_start number of bytes used to pack the length */
|
||||
keyseg->bit_start= ((keyseg->type == HA_KEYTYPE_VARTEXT1 ||
|
||||
keyseg->type == HA_KEYTYPE_VARBINARY1) ?
|
||||
1 : 2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (keyseg->flag & HA_SPACE_PACK)
|
||||
{
|
||||
DBUG_ASSERT(!(keyseg->flag & HA_VAR_LENGTH_PART));
|
||||
keydef->flag |= HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY;
|
||||
options|=HA_OPTION_PACK_KEYS; /* Using packed keys */
|
||||
length++; /* At least one length byte */
|
||||
min_key_length_skip+=keyseg->length;
|
||||
if (keyseg->length >= 255)
|
||||
{ /* prefix may be 3 bytes */
|
||||
min_key_length_skip+=2;
|
||||
length+=2;
|
||||
}
|
||||
}
|
||||
if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
|
||||
{
|
||||
DBUG_ASSERT(!test_all_bits(keyseg->flag,
|
||||
(HA_VAR_LENGTH_PART | HA_BLOB_PART)));
|
||||
keydef->flag|=HA_VAR_LENGTH_KEY;
|
||||
length++; /* At least one length byte */
|
||||
options|=HA_OPTION_PACK_KEYS; /* Using packed keys */
|
||||
min_key_length_skip+=keyseg->length;
|
||||
if (keyseg->length >= 255)
|
||||
{ /* prefix may be 3 bytes */
|
||||
min_key_length_skip+=2;
|
||||
length+=2;
|
||||
}
|
||||
}
|
||||
key_length+= keyseg->length;
|
||||
if (keyseg->null_bit)
|
||||
{
|
||||
key_length++;
|
||||
options|=HA_OPTION_PACK_KEYS;
|
||||
keyseg->flag|=HA_NULL_PART;
|
||||
keydef->flag|=HA_VAR_LENGTH_KEY | HA_NULL_PART_KEY;
|
||||
}
|
||||
}
|
||||
} /* if HA_FULLTEXT */
|
||||
key_segs+=keydef->keysegs;
|
||||
if (keydef->keysegs > HA_MAX_KEY_SEG)
|
||||
{
|
||||
my_errno=HA_WRONG_CREATE_OPTION;
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
key_segs may be 0 in the case when we only want to be able to
|
||||
add on row into the table. This can happen with some DISTINCT queries
|
||||
in MySQL
|
||||
*/
|
||||
if ((keydef->flag & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME &&
|
||||
key_segs)
|
||||
share.state.rec_per_key_part[key_segs-1]=1L;
|
||||
length+=key_length;
|
||||
/* Get block length for key, if defined by user */
|
||||
block_length= (keydef->block_length ?
|
||||
my_round_up_to_next_power(keydef->block_length) :
|
||||
maria_block_size);
|
||||
block_length= max(block_length, MARIA_MIN_KEY_BLOCK_LENGTH);
|
||||
block_length= min(block_length, MARIA_MAX_KEY_BLOCK_LENGTH);
|
||||
|
||||
keydef->block_length= MARIA_BLOCK_SIZE(length-real_length_diff,
|
||||
pointer,MARIA_MAX_KEYPTR_SIZE,
|
||||
block_length);
|
||||
if (keydef->block_length > MARIA_MAX_KEY_BLOCK_LENGTH ||
|
||||
length >= HA_MAX_KEY_BUFF)
|
||||
{
|
||||
my_errno=HA_WRONG_CREATE_OPTION;
|
||||
goto err;
|
||||
}
|
||||
set_if_bigger(max_key_block_length,keydef->block_length);
|
||||
keydef->keylength= (uint16) key_length;
|
||||
keydef->minlength= (uint16) (length-min_key_length_skip);
|
||||
keydef->maxlength= (uint16) length;
|
||||
|
||||
if (length > max_key_length)
|
||||
max_key_length= length;
|
||||
tot_length+= (max_rows/(ulong) (((uint) keydef->block_length-5)/
|
||||
(length*2)))*
|
||||
(ulong) keydef->block_length;
|
||||
}
|
||||
for (i=max_key_block_length/MARIA_MIN_KEY_BLOCK_LENGTH ; i-- ; )
|
||||
key_del[i]=HA_OFFSET_ERROR;
|
||||
|
||||
unique_key_parts=0;
|
||||
offset=reclength-uniques*MARIA_UNIQUE_HASH_LENGTH;
|
||||
for (i=0, uniquedef=uniquedefs ; i < uniques ; i++ , uniquedef++)
|
||||
{
|
||||
uniquedef->key=keys+i;
|
||||
unique_key_parts+=uniquedef->keysegs;
|
||||
share.state.key_root[keys+i]= HA_OFFSET_ERROR;
|
||||
tot_length+= (max_rows/(ulong) (((uint) maria_block_size-5)/
|
||||
((MARIA_UNIQUE_HASH_LENGTH + pointer)*2)))*
|
||||
(ulong) maria_block_size;
|
||||
}
|
||||
keys+=uniques; /* Each unique has 1 key */
|
||||
key_segs+=uniques; /* Each unique has 1 key seg */
|
||||
|
||||
base_pos=(MARIA_STATE_INFO_SIZE + keys * MARIA_STATE_KEY_SIZE +
|
||||
max_key_block_length/MARIA_MIN_KEY_BLOCK_LENGTH*
|
||||
MARIA_STATE_KEYBLOCK_SIZE+
|
||||
key_segs*MARIA_STATE_KEYSEG_SIZE);
|
||||
info_length=base_pos+(uint) (MARIA_BASE_INFO_SIZE+
|
||||
keys * MARIA_KEYDEF_SIZE+
|
||||
uniques * MARIA_UNIQUEDEF_SIZE +
|
||||
(key_segs + unique_key_parts)*HA_KEYSEG_SIZE+
|
||||
columns*MARIA_COLUMNDEF_SIZE);
|
||||
|
||||
DBUG_PRINT("info", ("info_length: %u", info_length));
|
||||
/* There are only 16 bits for the total header length. */
|
||||
if (info_length > 65535)
|
||||
{
|
||||
my_printf_error(0, "Maria table '%s' has too many columns and/or "
|
||||
"indexes and/or unique constraints.",
|
||||
MYF(0), name + dirname_length(name));
|
||||
my_errno= HA_WRONG_CREATE_OPTION;
|
||||
goto err;
|
||||
}
|
||||
|
||||
bmove(share.state.header.file_version,(byte*) maria_file_magic,4);
|
||||
ci->old_options=options| (ci->old_options & HA_OPTION_TEMP_COMPRESS_RECORD ?
|
||||
HA_OPTION_COMPRESS_RECORD |
|
||||
HA_OPTION_TEMP_COMPRESS_RECORD: 0);
|
||||
mi_int2store(share.state.header.options,ci->old_options);
|
||||
mi_int2store(share.state.header.header_length,info_length);
|
||||
mi_int2store(share.state.header.state_info_length,MARIA_STATE_INFO_SIZE);
|
||||
mi_int2store(share.state.header.base_info_length,MARIA_BASE_INFO_SIZE);
|
||||
mi_int2store(share.state.header.base_pos,base_pos);
|
||||
share.state.header.language= (ci->language ?
|
||||
ci->language : default_charset_info->number);
|
||||
share.state.header.max_block_size_index= max_key_block_length/MARIA_MIN_KEY_BLOCK_LENGTH;
|
||||
|
||||
share.state.dellink = HA_OFFSET_ERROR;
|
||||
share.state.process= (ulong) getpid();
|
||||
share.state.unique= (ulong) 0;
|
||||
share.state.update_count=(ulong) 0;
|
||||
share.state.version= (ulong) time((time_t*) 0);
|
||||
share.state.sortkey= (ushort) ~0;
|
||||
share.state.auto_increment=ci->auto_increment;
|
||||
share.options=options;
|
||||
share.base.rec_reflength=pointer;
|
||||
/* Get estimate for index file length (this may be wrong for FT keys) */
|
||||
tmp= (tot_length + max_key_block_length * keys *
|
||||
MARIA_INDEX_BLOCK_MARGIN) / MARIA_MIN_KEY_BLOCK_LENGTH;
|
||||
/*
|
||||
use maximum of key_file_length we calculated and key_file_length value we
|
||||
got from MYI file header (see also mariapack.c:save_state)
|
||||
*/
|
||||
share.base.key_reflength=
|
||||
maria_get_pointer_length(max(ci->key_file_length,tmp),3);
|
||||
share.base.keys= share.state.header.keys= keys;
|
||||
share.state.header.uniques= uniques;
|
||||
share.state.header.fulltext_keys= fulltext_keys;
|
||||
mi_int2store(share.state.header.key_parts,key_segs);
|
||||
mi_int2store(share.state.header.unique_key_parts,unique_key_parts);
|
||||
|
||||
maria_set_all_keys_active(share.state.key_map, keys);
|
||||
aligned_key_start= my_round_up_to_next_power(max_key_block_length ?
|
||||
max_key_block_length :
|
||||
maria_block_size);
|
||||
|
||||
share.base.keystart = share.state.state.key_file_length=
|
||||
MY_ALIGN(info_length, aligned_key_start);
|
||||
share.base.max_key_block_length=max_key_block_length;
|
||||
share.base.max_key_length=ALIGN_SIZE(max_key_length+4);
|
||||
share.base.records=ci->max_rows;
|
||||
share.base.reloc= ci->reloc_rows;
|
||||
share.base.reclength=real_reclength;
|
||||
share.base.pack_reclength=reclength+ test(options & HA_OPTION_CHECKSUM);
|
||||
share.base.max_pack_length=pack_reclength;
|
||||
share.base.min_pack_length=min_pack_length;
|
||||
share.base.pack_bits=packed;
|
||||
share.base.fields=fields;
|
||||
share.base.pack_fields=packed;
|
||||
#ifdef USE_RAID
|
||||
share.base.raid_type=ci->raid_type;
|
||||
share.base.raid_chunks=ci->raid_chunks;
|
||||
share.base.raid_chunksize=ci->raid_chunksize;
|
||||
#endif
|
||||
|
||||
/* max_data_file_length and max_key_file_length are recalculated on open */
|
||||
if (options & HA_OPTION_TMP_TABLE)
|
||||
share.base.max_data_file_length=(my_off_t) ci->data_file_length;
|
||||
|
||||
share.base.min_block_length=
|
||||
(share.base.pack_reclength+3 < MARIA_EXTEND_BLOCK_LENGTH &&
|
||||
! share.base.blobs) ?
|
||||
max(share.base.pack_reclength,MARIA_MIN_BLOCK_LENGTH) :
|
||||
MARIA_EXTEND_BLOCK_LENGTH;
|
||||
if (! (flags & HA_DONT_TOUCH_DATA))
|
||||
share.state.create_time= (long) time((time_t*) 0);
|
||||
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
|
||||
if (ci->index_file_name)
|
||||
{
|
||||
char *iext= strrchr(ci->index_file_name, '.');
|
||||
int have_iext= iext && !strcmp(iext, MARIA_NAME_IEXT);
|
||||
if (options & HA_OPTION_TMP_TABLE)
|
||||
{
|
||||
char *path;
|
||||
/* chop off the table name, tempory tables use generated name */
|
||||
if ((path= strrchr(ci->index_file_name, FN_LIBCHAR)))
|
||||
*path= '\0';
|
||||
fn_format(filename, name, ci->index_file_name, MARIA_NAME_IEXT,
|
||||
MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
fn_format(filename, ci->index_file_name, "", MARIA_NAME_IEXT,
|
||||
MY_UNPACK_FILENAME | (have_iext ? MY_REPLACE_EXT :
|
||||
MY_APPEND_EXT));
|
||||
}
|
||||
fn_format(linkname, name, "", MARIA_NAME_IEXT,
|
||||
MY_UNPACK_FILENAME|MY_APPEND_EXT);
|
||||
linkname_ptr=linkname;
|
||||
/*
|
||||
Don't create the table if the link or file exists to ensure that one
|
||||
doesn't accidently destroy another table.
|
||||
*/
|
||||
create_flag=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fn_format(filename, name, "", MARIA_NAME_IEXT,
|
||||
(MY_UNPACK_FILENAME |
|
||||
(flags & HA_DONT_TOUCH_DATA) ? MY_RETURN_REAL_PATH : 0) |
|
||||
MY_APPEND_EXT);
|
||||
linkname_ptr=0;
|
||||
/* Replace the current file */
|
||||
create_flag=MY_DELETE_OLD;
|
||||
}
|
||||
|
||||
/*
|
||||
If a MRG_MARIA table is in use, the mapped MARIA tables are open,
|
||||
but no entry is made in the table cache for them.
|
||||
A TRUNCATE command checks for the table in the cache only and could
|
||||
be fooled to believe, the table is not open.
|
||||
Pull the emergency brake in this situation. (Bug #8306)
|
||||
*/
|
||||
if (_ma_test_if_reopen(filename))
|
||||
{
|
||||
my_printf_error(0, "MARIA table '%s' is in use "
|
||||
"(most likely by a MERGE table). Try FLUSH TABLES.",
|
||||
MYF(0), name + dirname_length(name));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((file= my_create_with_symlink(linkname_ptr, filename, 0, create_mode,
|
||||
MYF(MY_WME | create_flag))) < 0)
|
||||
goto err;
|
||||
errpos=1;
|
||||
|
||||
if (!(flags & HA_DONT_TOUCH_DATA))
|
||||
{
|
||||
#ifdef USE_RAID
|
||||
if (share.base.raid_type)
|
||||
{
|
||||
(void) fn_format(filename, name, "", MARIA_NAME_DEXT,
|
||||
MY_UNPACK_FILENAME | MY_APPEND_EXT);
|
||||
if ((dfile=my_raid_create(filename, 0, create_mode,
|
||||
share.base.raid_type,
|
||||
share.base.raid_chunks,
|
||||
share.base.raid_chunksize,
|
||||
MYF(MY_WME | MY_RAID))) < 0)
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (ci->data_file_name)
|
||||
{
|
||||
char *dext= strrchr(ci->data_file_name, '.');
|
||||
int have_dext= dext && !strcmp(dext, MARIA_NAME_DEXT);
|
||||
|
||||
if (options & HA_OPTION_TMP_TABLE)
|
||||
{
|
||||
char *path;
|
||||
/* chop off the table name, tempory tables use generated name */
|
||||
if ((path= strrchr(ci->data_file_name, FN_LIBCHAR)))
|
||||
*path= '\0';
|
||||
fn_format(filename, name, ci->data_file_name, MARIA_NAME_DEXT,
|
||||
MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
fn_format(filename, ci->data_file_name, "", MARIA_NAME_DEXT,
|
||||
MY_UNPACK_FILENAME |
|
||||
(have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT));
|
||||
}
|
||||
fn_format(linkname, name, "",MARIA_NAME_DEXT,
|
||||
MY_UNPACK_FILENAME | MY_APPEND_EXT);
|
||||
linkname_ptr=linkname;
|
||||
create_flag=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fn_format(filename,name,"", MARIA_NAME_DEXT,
|
||||
MY_UNPACK_FILENAME | MY_APPEND_EXT);
|
||||
linkname_ptr=0;
|
||||
create_flag=MY_DELETE_OLD;
|
||||
}
|
||||
if ((dfile=
|
||||
my_create_with_symlink(linkname_ptr, filename, 0, create_mode,
|
||||
MYF(MY_WME | create_flag))) < 0)
|
||||
goto err;
|
||||
}
|
||||
errpos=3;
|
||||
}
|
||||
DBUG_PRINT("info", ("write state info and base info"));
|
||||
if (_ma_state_info_write(file, &share.state, 2) ||
|
||||
_ma_base_info_write(file, &share.base))
|
||||
goto err;
|
||||
#ifndef DBUG_OFF
|
||||
if ((uint) my_tell(file,MYF(0)) != base_pos+ MARIA_BASE_INFO_SIZE)
|
||||
{
|
||||
uint pos=(uint) my_tell(file,MYF(0));
|
||||
DBUG_PRINT("warning",("base_length: %d != used_length: %d",
|
||||
base_pos+ MARIA_BASE_INFO_SIZE, pos));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write key and keyseg definitions */
|
||||
DBUG_PRINT("info", ("write key and keyseg definitions"));
|
||||
for (i=0 ; i < share.base.keys - uniques; i++)
|
||||
{
|
||||
uint sp_segs=(keydefs[i].flag & HA_SPATIAL) ? 2*SPDIMS : 0;
|
||||
|
||||
if (_ma_keydef_write(file, &keydefs[i]))
|
||||
goto err;
|
||||
for (j=0 ; j < keydefs[i].keysegs-sp_segs ; j++)
|
||||
if (_ma_keyseg_write(file, &keydefs[i].seg[j]))
|
||||
goto err;
|
||||
#ifdef HAVE_SPATIAL
|
||||
for (j=0 ; j < sp_segs ; j++)
|
||||
{
|
||||
HA_KEYSEG sseg;
|
||||
sseg.type=SPTYPE;
|
||||
sseg.language= 7; /* Binary */
|
||||
sseg.null_bit=0;
|
||||
sseg.bit_start=0;
|
||||
sseg.bit_end=0;
|
||||
sseg.bit_length= 0;
|
||||
sseg.bit_pos= 0;
|
||||
sseg.length=SPLEN;
|
||||
sseg.null_pos=0;
|
||||
sseg.start=j*SPLEN;
|
||||
sseg.flag= HA_SWAP_KEY;
|
||||
if (_ma_keyseg_write(file, &sseg))
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* Create extra keys for unique definitions */
|
||||
offset=reclength-uniques*MARIA_UNIQUE_HASH_LENGTH;
|
||||
bzero((char*) &tmp_keydef,sizeof(tmp_keydef));
|
||||
bzero((char*) &tmp_keyseg,sizeof(tmp_keyseg));
|
||||
for (i=0; i < uniques ; i++)
|
||||
{
|
||||
tmp_keydef.keysegs=1;
|
||||
tmp_keydef.flag= HA_UNIQUE_CHECK;
|
||||
tmp_keydef.block_length= (uint16)maria_block_size;
|
||||
tmp_keydef.keylength= MARIA_UNIQUE_HASH_LENGTH + pointer;
|
||||
tmp_keydef.minlength=tmp_keydef.maxlength=tmp_keydef.keylength;
|
||||
tmp_keyseg.type= MARIA_UNIQUE_HASH_TYPE;
|
||||
tmp_keyseg.length= MARIA_UNIQUE_HASH_LENGTH;
|
||||
tmp_keyseg.start= offset;
|
||||
offset+= MARIA_UNIQUE_HASH_LENGTH;
|
||||
if (_ma_keydef_write(file,&tmp_keydef) ||
|
||||
_ma_keyseg_write(file,(&tmp_keyseg)))
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Save unique definition */
|
||||
DBUG_PRINT("info", ("write unique definitions"));
|
||||
for (i=0 ; i < share.state.header.uniques ; i++)
|
||||
{
|
||||
HA_KEYSEG *keyseg_end;
|
||||
keyseg= uniquedefs[i].seg;
|
||||
if (_ma_uniquedef_write(file, &uniquedefs[i]))
|
||||
goto err;
|
||||
for (keyseg= uniquedefs[i].seg, keyseg_end= keyseg+ uniquedefs[i].keysegs;
|
||||
keyseg < keyseg_end;
|
||||
keyseg++)
|
||||
{
|
||||
switch (keyseg->type) {
|
||||
case HA_KEYTYPE_VARTEXT1:
|
||||
case HA_KEYTYPE_VARTEXT2:
|
||||
case HA_KEYTYPE_VARBINARY1:
|
||||
case HA_KEYTYPE_VARBINARY2:
|
||||
if (!(keyseg->flag & HA_BLOB_PART))
|
||||
{
|
||||
keyseg->flag|= HA_VAR_LENGTH_PART;
|
||||
keyseg->bit_start= ((keyseg->type == HA_KEYTYPE_VARTEXT1 ||
|
||||
keyseg->type == HA_KEYTYPE_VARBINARY1) ?
|
||||
1 : 2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (_ma_keyseg_write(file, keyseg))
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("info", ("write field definitions"));
|
||||
for (i=0 ; i < share.base.fields ; i++)
|
||||
if (_ma_recinfo_write(file, &recinfo[i]))
|
||||
goto err;
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
if ((uint) my_tell(file,MYF(0)) != info_length)
|
||||
{
|
||||
uint pos= (uint) my_tell(file,MYF(0));
|
||||
DBUG_PRINT("warning",("info_length: %d != used_length: %d",
|
||||
info_length, pos));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Enlarge files */
|
||||
DBUG_PRINT("info", ("enlarge to keystart: %lu", (ulong) share.base.keystart));
|
||||
if (my_chsize(file,(ulong) share.base.keystart,0,MYF(0)))
|
||||
goto err;
|
||||
|
||||
if (! (flags & HA_DONT_TOUCH_DATA))
|
||||
{
|
||||
#ifdef USE_RELOC
|
||||
if (my_chsize(dfile,share.base.min_pack_length*ci->reloc_rows,0,MYF(0)))
|
||||
goto err;
|
||||
#endif
|
||||
errpos=2;
|
||||
if (my_close(dfile,MYF(0)))
|
||||
goto err;
|
||||
}
|
||||
errpos=0;
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
if (my_close(file,MYF(0)))
|
||||
goto err;
|
||||
my_free((char*) rec_per_key_part,MYF(0));
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
save_errno=my_errno;
|
||||
switch (errpos) {
|
||||
case 3:
|
||||
VOID(my_close(dfile,MYF(0)));
|
||||
/* fall through */
|
||||
case 2:
|
||||
/* QQ: Tõnu should add a call to my_raid_delete() here */
|
||||
if (! (flags & HA_DONT_TOUCH_DATA))
|
||||
my_delete_with_symlink(fn_format(filename,name,"",MARIA_NAME_DEXT,
|
||||
MY_UNPACK_FILENAME | MY_APPEND_EXT),
|
||||
MYF(0));
|
||||
/* fall through */
|
||||
case 1:
|
||||
VOID(my_close(file,MYF(0)));
|
||||
if (! (flags & HA_DONT_TOUCH_DATA))
|
||||
my_delete_with_symlink(fn_format(filename,name,"",MARIA_NAME_IEXT,
|
||||
MY_UNPACK_FILENAME | MY_APPEND_EXT),
|
||||
MYF(0));
|
||||
}
|
||||
my_free((char*) rec_per_key_part, MYF(0));
|
||||
DBUG_RETURN(my_errno=save_errno); /* return the fatal errno */
|
||||
}
|
||||
|
||||
|
||||
uint maria_get_pointer_length(ulonglong file_length, uint def)
|
||||
{
|
||||
DBUG_ASSERT(def >= 2 && def <= 7);
|
||||
if (file_length) /* If not default */
|
||||
{
|
||||
#ifdef NOT_YET_READY_FOR_8_BYTE_POINTERS
|
||||
if (file_length >= (longlong) 1 << 56)
|
||||
def=8;
|
||||
#endif
|
||||
if (file_length >= (longlong) 1 << 48)
|
||||
def=7;
|
||||
if (file_length >= (longlong) 1 << 40)
|
||||
def=6;
|
||||
else if (file_length >= (longlong) 1 << 32)
|
||||
def=5;
|
||||
else if (file_length >= (1L << 24))
|
||||
def=4;
|
||||
else if (file_length >= (1L << 16))
|
||||
def=3;
|
||||
else
|
||||
def=2;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
193
storage/maria/ma_dbug.c
Normal file
193
storage/maria/ma_dbug.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Support rutiner with are using with dbug */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Print a key in user understandable format */
|
||||
|
||||
void _ma_print_key(FILE *stream, register HA_KEYSEG *keyseg,
|
||||
const uchar *key, uint length)
|
||||
{
|
||||
int flag;
|
||||
short int s_1;
|
||||
long int l_1;
|
||||
float f_1;
|
||||
double d_1;
|
||||
const uchar *end;
|
||||
const uchar *key_end=key+length;
|
||||
|
||||
VOID(fputs("Key: \"",stream));
|
||||
flag=0;
|
||||
for (; keyseg->type && key < key_end ;keyseg++)
|
||||
{
|
||||
if (flag++)
|
||||
VOID(putc('-',stream));
|
||||
end= key+ keyseg->length;
|
||||
if (keyseg->flag & HA_NULL_PART)
|
||||
{
|
||||
/* A NULL value is encoded by a 1-byte flag. Zero means NULL. */
|
||||
if (! *(key++))
|
||||
{
|
||||
fprintf(stream,"NULL");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (keyseg->type) {
|
||||
case HA_KEYTYPE_BINARY:
|
||||
if (!(keyseg->flag & HA_SPACE_PACK) && keyseg->length == 1)
|
||||
{ /* packed binary digit */
|
||||
VOID(fprintf(stream,"%d",(uint) *key++));
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case HA_KEYTYPE_TEXT:
|
||||
case HA_KEYTYPE_NUM:
|
||||
if (keyseg->flag & HA_SPACE_PACK)
|
||||
{
|
||||
VOID(fprintf(stream,"%.*s",(int) *key,key+1));
|
||||
key+= (int) *key+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
VOID(fprintf(stream,"%.*s",(int) keyseg->length,key));
|
||||
key=end;
|
||||
}
|
||||
break;
|
||||
case HA_KEYTYPE_INT8:
|
||||
VOID(fprintf(stream,"%d",(int) *((signed char*) key)));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
s_1= mi_sint2korr(key);
|
||||
VOID(fprintf(stream,"%d",(int) s_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
{
|
||||
ushort u_1;
|
||||
u_1= mi_uint2korr(key);
|
||||
VOID(fprintf(stream,"%u",(uint) u_1));
|
||||
key=end;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
l_1=mi_sint4korr(key);
|
||||
VOID(fprintf(stream,"%ld",l_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
l_1=mi_sint4korr(key);
|
||||
VOID(fprintf(stream,"%lu",(ulong) l_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
VOID(fprintf(stream,"%ld",(long) mi_sint3korr(key)));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
VOID(fprintf(stream,"%lu",(ulong) mi_uint3korr(key)));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
mi_float4get(f_1,key);
|
||||
VOID(fprintf(stream,"%g",(double) f_1));
|
||||
key=end;
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
mi_float8get(d_1,key);
|
||||
VOID(fprintf(stream,"%g",d_1));
|
||||
key=end;
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
{
|
||||
char buff[21];
|
||||
longlong2str(mi_sint8korr(key),buff,-10);
|
||||
VOID(fprintf(stream,"%s",buff));
|
||||
key=end;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
{
|
||||
char buff[21];
|
||||
longlong2str(mi_sint8korr(key),buff,10);
|
||||
VOID(fprintf(stream,"%s",buff));
|
||||
key=end;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_BIT:
|
||||
{
|
||||
uint i;
|
||||
fputs("0x",stream);
|
||||
for (i=0 ; i < keyseg->length ; i++)
|
||||
fprintf(stream, "%02x", (uint) *key++);
|
||||
key= end;
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
case HA_KEYTYPE_VARTEXT1: /* VARCHAR and TEXT */
|
||||
case HA_KEYTYPE_VARTEXT2: /* VARCHAR and TEXT */
|
||||
case HA_KEYTYPE_VARBINARY1: /* VARBINARY and BLOB */
|
||||
case HA_KEYTYPE_VARBINARY2: /* VARBINARY and BLOB */
|
||||
{
|
||||
uint tmp_length;
|
||||
get_key_length(tmp_length,key);
|
||||
/*
|
||||
The following command sometimes gives a warning from valgrind.
|
||||
Not yet sure if the bug is in valgrind, glibc or mysqld
|
||||
*/
|
||||
VOID(fprintf(stream,"%.*s",(int) tmp_length,key));
|
||||
key+=tmp_length;
|
||||
break;
|
||||
}
|
||||
default: break; /* This never happens */
|
||||
}
|
||||
}
|
||||
VOID(fputs("\"\n",stream));
|
||||
return;
|
||||
} /* print_key */
|
||||
|
||||
|
||||
#ifdef EXTRA_DEBUG
|
||||
|
||||
my_bool _ma_check_table_is_closed(const char *name, const char *where)
|
||||
{
|
||||
char filename[FN_REFLEN];
|
||||
LIST *pos;
|
||||
DBUG_ENTER("_ma_check_table_is_closed");
|
||||
|
||||
(void) fn_format(filename,name,"",MARIA_NAME_IEXT,4+16+32);
|
||||
for (pos=maria_open_list ; pos ; pos=pos->next)
|
||||
{
|
||||
MARIA_HA *info=(MARIA_HA*) pos->data;
|
||||
MARIA_SHARE *share=info->s;
|
||||
if (!strcmp(share->unique_file_name,filename))
|
||||
{
|
||||
if (share->last_version)
|
||||
{
|
||||
fprintf(stderr,"Warning: Table: %s is open on %s\n", name,where);
|
||||
DBUG_PRINT("warning",("Table: %s is open on %s", name,where));
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
#endif /* EXTRA_DEBUG */
|
||||
888
storage/maria/ma_delete.c
Normal file
888
storage/maria/ma_delete.c
Normal file
@@ -0,0 +1,888 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Remove a row from a MARIA table */
|
||||
|
||||
#include "ma_fulltext.h"
|
||||
#include "ma_rt_index.h"
|
||||
|
||||
static int d_search(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uint comp_flag,
|
||||
uchar *key,uint key_length,my_off_t page,uchar *anc_buff);
|
||||
static int del(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uchar *key,uchar *anc_buff,
|
||||
my_off_t leaf_page,uchar *leaf_buff,uchar *keypos,
|
||||
my_off_t next_block,uchar *ret_key);
|
||||
static int underflow(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uchar *anc_buff,
|
||||
my_off_t leaf_page,uchar *leaf_buff,uchar *keypos);
|
||||
static uint remove_key(MARIA_KEYDEF *keyinfo,uint nod_flag,uchar *keypos,
|
||||
uchar *lastkey,uchar *page_end,
|
||||
my_off_t *next_block);
|
||||
static int _ma_ck_real_delete(register MARIA_HA *info,MARIA_KEYDEF *keyinfo,
|
||||
uchar *key, uint key_length, my_off_t *root);
|
||||
|
||||
|
||||
int maria_delete(MARIA_HA *info,const byte *record)
|
||||
{
|
||||
uint i;
|
||||
uchar *old_key;
|
||||
int save_errno;
|
||||
char lastpos[8];
|
||||
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_delete");
|
||||
|
||||
/* Test if record is in datafile */
|
||||
|
||||
DBUG_EXECUTE_IF("maria_pretend_crashed_table_on_usage",
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
DBUG_RETURN(my_errno= HA_ERR_CRASHED););
|
||||
DBUG_EXECUTE_IF("my_error_test_undefined_error",
|
||||
maria_print_error(info->s, INT_MAX);
|
||||
DBUG_RETURN(my_errno= INT_MAX););
|
||||
if (!(info->update & HA_STATE_AKTIV))
|
||||
{
|
||||
DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); /* No database read */
|
||||
}
|
||||
if (share->options & HA_OPTION_READ_ONLY_DATA)
|
||||
{
|
||||
DBUG_RETURN(my_errno=EACCES);
|
||||
}
|
||||
if (_ma_readinfo(info,F_WRLCK,1))
|
||||
DBUG_RETURN(my_errno);
|
||||
if (info->s->calc_checksum)
|
||||
info->checksum=(*info->s->calc_checksum)(info,record);
|
||||
if ((*share->compare_record)(info,record))
|
||||
goto err; /* Error on read-check */
|
||||
|
||||
if (_ma_mark_file_changed(info))
|
||||
goto err;
|
||||
|
||||
/* Remove all keys from the .ISAM file */
|
||||
|
||||
old_key=info->lastkey2;
|
||||
for (i=0 ; i < share->base.keys ; i++ )
|
||||
{
|
||||
if (maria_is_key_active(info->s->state.key_map, i))
|
||||
{
|
||||
info->s->keyinfo[i].version++;
|
||||
if (info->s->keyinfo[i].flag & HA_FULLTEXT )
|
||||
{
|
||||
if (_ma_ft_del(info,i,(char*) old_key,record,info->lastpos))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info->s->keyinfo[i].ck_delete(info,i,old_key,
|
||||
_ma_make_key(info,i,old_key,record,info->lastpos)))
|
||||
goto err;
|
||||
}
|
||||
/* The above changed info->lastkey2. Inform maria_rnext_same(). */
|
||||
info->update&= ~HA_STATE_RNEXT_SAME;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*share->delete_record)(info))
|
||||
goto err; /* Remove record from database */
|
||||
info->state->checksum-=info->checksum;
|
||||
|
||||
info->update= HA_STATE_CHANGED+HA_STATE_DELETED+HA_STATE_ROW_CHANGED;
|
||||
info->state->records--;
|
||||
|
||||
mi_sizestore(lastpos,info->lastpos);
|
||||
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
||||
allow_break(); /* Allow SIGHUP & SIGINT */
|
||||
if (info->invalidator != 0)
|
||||
{
|
||||
DBUG_PRINT("info", ("invalidator... '%s' (delete)", info->filename));
|
||||
(*info->invalidator)(info->filename);
|
||||
info->invalidator=0;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
save_errno=my_errno;
|
||||
mi_sizestore(lastpos,info->lastpos);
|
||||
if (save_errno != HA_ERR_RECORD_CHANGED)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info); /* mark table crashed */
|
||||
}
|
||||
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
||||
info->update|=HA_STATE_WRITTEN; /* Buffer changed */
|
||||
allow_break(); /* Allow SIGHUP & SIGINT */
|
||||
my_errno=save_errno;
|
||||
if (save_errno == HA_ERR_KEY_NOT_FOUND)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno=HA_ERR_CRASHED;
|
||||
}
|
||||
|
||||
DBUG_RETURN(my_errno);
|
||||
} /* maria_delete */
|
||||
|
||||
|
||||
/* Remove a key from the btree index */
|
||||
|
||||
int _ma_ck_delete(register MARIA_HA *info, uint keynr, uchar *key,
|
||||
uint key_length)
|
||||
{
|
||||
return _ma_ck_real_delete(info, info->s->keyinfo+keynr, key, key_length,
|
||||
&info->s->state.key_root[keynr]);
|
||||
} /* _ma_ck_delete */
|
||||
|
||||
|
||||
static int _ma_ck_real_delete(register MARIA_HA *info, MARIA_KEYDEF *keyinfo,
|
||||
uchar *key, uint key_length, my_off_t *root)
|
||||
{
|
||||
int error;
|
||||
uint nod_flag;
|
||||
my_off_t old_root;
|
||||
uchar *root_buff;
|
||||
DBUG_ENTER("_ma_ck_real_delete");
|
||||
|
||||
if ((old_root=*root) == HA_OFFSET_ERROR)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
DBUG_RETURN(my_errno=HA_ERR_CRASHED);
|
||||
}
|
||||
if (!(root_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
||||
HA_MAX_KEY_BUFF*2)))
|
||||
{
|
||||
DBUG_PRINT("error",("Couldn't allocate memory"));
|
||||
DBUG_RETURN(my_errno=ENOMEM);
|
||||
}
|
||||
DBUG_PRINT("info",("root_page: %ld",old_root));
|
||||
if (!_ma_fetch_keypage(info,keyinfo,old_root,DFLT_INIT_HITS,root_buff,0))
|
||||
{
|
||||
error= -1;
|
||||
goto err;
|
||||
}
|
||||
if ((error=d_search(info,keyinfo,
|
||||
(keyinfo->flag & HA_FULLTEXT ? SEARCH_FIND | SEARCH_UPDATE
|
||||
: SEARCH_SAME),
|
||||
key,key_length,old_root,root_buff)) >0)
|
||||
{
|
||||
if (error == 2)
|
||||
{
|
||||
DBUG_PRINT("test",("Enlarging of root when deleting"));
|
||||
error= _ma_enlarge_root(info,keyinfo,key,root);
|
||||
}
|
||||
else /* error == 1 */
|
||||
{
|
||||
if (maria_getint(root_buff) <= (nod_flag=_ma_test_if_nod(root_buff))+3)
|
||||
{
|
||||
error=0;
|
||||
if (nod_flag)
|
||||
*root= _ma_kpos(nod_flag,root_buff+2+nod_flag);
|
||||
else
|
||||
*root=HA_OFFSET_ERROR;
|
||||
if (_ma_dispose(info,keyinfo,old_root,DFLT_INIT_HITS))
|
||||
error= -1;
|
||||
}
|
||||
else
|
||||
error= _ma_write_keypage(info,keyinfo,old_root,
|
||||
DFLT_INIT_HITS,root_buff);
|
||||
}
|
||||
}
|
||||
err:
|
||||
my_afree((gptr) root_buff);
|
||||
DBUG_PRINT("exit",("Return: %d",error));
|
||||
DBUG_RETURN(error);
|
||||
} /* _ma_ck_real_delete */
|
||||
|
||||
|
||||
/*
|
||||
** Remove key below key root
|
||||
** Return values:
|
||||
** 1 if there are less buffers; In this case anc_buff is not saved
|
||||
** 2 if there are more buffers
|
||||
** -1 on errors
|
||||
*/
|
||||
|
||||
static int d_search(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
||||
uint comp_flag, uchar *key, uint key_length,
|
||||
my_off_t page, uchar *anc_buff)
|
||||
{
|
||||
int flag,ret_value,save_flag;
|
||||
uint length,nod_flag,search_key_length;
|
||||
my_bool last_key;
|
||||
uchar *leaf_buff,*keypos;
|
||||
my_off_t leaf_page,next_block;
|
||||
uchar lastkey[HA_MAX_KEY_BUFF];
|
||||
DBUG_ENTER("d_search");
|
||||
DBUG_DUMP("page",(byte*) anc_buff,maria_getint(anc_buff));
|
||||
|
||||
search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY;
|
||||
flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key, search_key_length,
|
||||
comp_flag, &keypos, lastkey, &last_key);
|
||||
if (flag == MARIA_FOUND_WRONG_KEY)
|
||||
{
|
||||
DBUG_PRINT("error",("Found wrong key"));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
nod_flag=_ma_test_if_nod(anc_buff);
|
||||
|
||||
if (!flag && keyinfo->flag & HA_FULLTEXT)
|
||||
{
|
||||
uint off;
|
||||
int subkeys;
|
||||
|
||||
get_key_full_length_rdonly(off, lastkey);
|
||||
subkeys=ft_sintXkorr(lastkey+off);
|
||||
DBUG_ASSERT(info->ft1_to_ft2==0 || subkeys >=0);
|
||||
comp_flag=SEARCH_SAME;
|
||||
if (subkeys >= 0)
|
||||
{
|
||||
/* normal word, one-level tree structure */
|
||||
if (info->ft1_to_ft2)
|
||||
{
|
||||
/* we're in ft1->ft2 conversion mode. Saving key data */
|
||||
insert_dynamic(info->ft1_to_ft2, (char*) (lastkey+off));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we need exact match only if not in ft1->ft2 conversion mode */
|
||||
flag=(*keyinfo->bin_search)(info,keyinfo,anc_buff,key,USE_WHOLE_KEY,
|
||||
comp_flag, &keypos, lastkey, &last_key);
|
||||
}
|
||||
/* fall through to normal delete */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* popular word. two-level tree. going down */
|
||||
uint tmp_key_length;
|
||||
my_off_t root;
|
||||
uchar *kpos=keypos;
|
||||
|
||||
if (!(tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,lastkey)))
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno= HA_ERR_CRASHED;
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
root= _ma_dpos(info,nod_flag,kpos);
|
||||
if (subkeys == -1)
|
||||
{
|
||||
/* the last entry in sub-tree */
|
||||
if (_ma_dispose(info, keyinfo, root,DFLT_INIT_HITS))
|
||||
DBUG_RETURN(-1);
|
||||
/* fall through to normal delete */
|
||||
}
|
||||
else
|
||||
{
|
||||
keyinfo=&info->s->ft2_keyinfo;
|
||||
kpos-=keyinfo->keylength+nod_flag; /* we'll modify key entry 'in vivo' */
|
||||
get_key_full_length_rdonly(off, key);
|
||||
key+=off;
|
||||
ret_value= _ma_ck_real_delete(info, &info->s->ft2_keyinfo,
|
||||
key, HA_FT_WLEN, &root);
|
||||
_ma_dpointer(info, kpos+HA_FT_WLEN, root);
|
||||
subkeys++;
|
||||
ft_intXstore(kpos, subkeys);
|
||||
if (!ret_value)
|
||||
ret_value= _ma_write_keypage(info,keyinfo,page,
|
||||
DFLT_INIT_HITS,anc_buff);
|
||||
DBUG_PRINT("exit",("Return: %d",ret_value));
|
||||
DBUG_RETURN(ret_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
leaf_buff=0;
|
||||
LINT_INIT(leaf_page);
|
||||
if (nod_flag)
|
||||
{
|
||||
leaf_page= _ma_kpos(nod_flag,keypos);
|
||||
if (!(leaf_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
||||
HA_MAX_KEY_BUFF*2)))
|
||||
{
|
||||
DBUG_PRINT("error",("Couldn't allocate memory"));
|
||||
my_errno=ENOMEM;
|
||||
DBUG_PRINT("exit",("Return: %d",-1));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
if (!_ma_fetch_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff,0))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (flag != 0)
|
||||
{
|
||||
if (!nod_flag)
|
||||
{
|
||||
DBUG_PRINT("error",("Didn't find key"));
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno=HA_ERR_CRASHED; /* This should newer happend */
|
||||
goto err;
|
||||
}
|
||||
save_flag=0;
|
||||
ret_value=d_search(info,keyinfo,comp_flag,key,key_length,
|
||||
leaf_page,leaf_buff);
|
||||
}
|
||||
else
|
||||
{ /* Found key */
|
||||
uint tmp;
|
||||
length=maria_getint(anc_buff);
|
||||
if (!(tmp= remove_key(keyinfo,nod_flag,keypos,lastkey,anc_buff+length,
|
||||
&next_block)))
|
||||
goto err;
|
||||
|
||||
length-= tmp;
|
||||
|
||||
maria_putint(anc_buff,length,nod_flag);
|
||||
if (!nod_flag)
|
||||
{ /* On leaf page */
|
||||
if (_ma_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,anc_buff))
|
||||
{
|
||||
DBUG_PRINT("exit",("Return: %d",-1));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
/* Page will be update later if we return 1 */
|
||||
DBUG_RETURN(test(length <= (info->quick_mode ? MARIA_MIN_KEYBLOCK_LENGTH :
|
||||
(uint) keyinfo->underflow_block_length)));
|
||||
}
|
||||
save_flag=1;
|
||||
ret_value=del(info,keyinfo,key,anc_buff,leaf_page,leaf_buff,keypos,
|
||||
next_block,lastkey);
|
||||
}
|
||||
if (ret_value >0)
|
||||
{
|
||||
save_flag=1;
|
||||
if (ret_value == 1)
|
||||
ret_value= underflow(info,keyinfo,anc_buff,leaf_page,leaf_buff,keypos);
|
||||
else
|
||||
{ /* This happens only with packed keys */
|
||||
DBUG_PRINT("test",("Enlarging of key when deleting"));
|
||||
if (!_ma_get_last_key(info,keyinfo,anc_buff,lastkey,keypos,&length))
|
||||
goto err;
|
||||
ret_value= _ma_insert(info,keyinfo,key,anc_buff,keypos,lastkey,
|
||||
(uchar*) 0,(uchar*) 0,(my_off_t) 0,(my_bool) 0);
|
||||
}
|
||||
}
|
||||
if (ret_value == 0 && maria_getint(anc_buff) > keyinfo->block_length)
|
||||
{
|
||||
save_flag=1;
|
||||
ret_value= _ma_split_page(info,keyinfo,key,anc_buff,lastkey,0) | 2;
|
||||
}
|
||||
if (save_flag && ret_value != 1)
|
||||
ret_value|= _ma_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,anc_buff);
|
||||
else
|
||||
{
|
||||
DBUG_DUMP("page",(byte*) anc_buff,maria_getint(anc_buff));
|
||||
}
|
||||
my_afree((byte*) leaf_buff);
|
||||
DBUG_PRINT("exit",("Return: %d",ret_value));
|
||||
DBUG_RETURN(ret_value);
|
||||
|
||||
err:
|
||||
my_afree((byte*) leaf_buff);
|
||||
DBUG_PRINT("exit",("Error: %d",my_errno));
|
||||
DBUG_RETURN (-1);
|
||||
} /* d_search */
|
||||
|
||||
|
||||
/* Remove a key that has a page-reference */
|
||||
|
||||
static int del(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo, uchar *key,
|
||||
uchar *anc_buff, my_off_t leaf_page, uchar *leaf_buff,
|
||||
uchar *keypos, /* Pos to where deleted key was */
|
||||
my_off_t next_block,
|
||||
uchar *ret_key) /* key before keypos in anc_buff */
|
||||
{
|
||||
int ret_value,length;
|
||||
uint a_length,nod_flag,tmp;
|
||||
my_off_t next_page;
|
||||
uchar keybuff[HA_MAX_KEY_BUFF],*endpos,*next_buff,*key_start, *prev_key;
|
||||
MARIA_SHARE *share=info->s;
|
||||
MARIA_KEY_PARAM s_temp;
|
||||
DBUG_ENTER("del");
|
||||
DBUG_PRINT("enter",("leaf_page: %ld keypos: 0x%lx", leaf_page,
|
||||
(ulong) keypos));
|
||||
DBUG_DUMP("leaf_buff",(byte*) leaf_buff,maria_getint(leaf_buff));
|
||||
|
||||
endpos=leaf_buff+maria_getint(leaf_buff);
|
||||
if (!(key_start= _ma_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
|
||||
&tmp)))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if ((nod_flag=_ma_test_if_nod(leaf_buff)))
|
||||
{
|
||||
next_page= _ma_kpos(nod_flag,endpos);
|
||||
if (!(next_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
|
||||
HA_MAX_KEY_BUFF*2)))
|
||||
DBUG_RETURN(-1);
|
||||
if (!_ma_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,next_buff,0))
|
||||
ret_value= -1;
|
||||
else
|
||||
{
|
||||
DBUG_DUMP("next_page",(byte*) next_buff,maria_getint(next_buff));
|
||||
if ((ret_value=del(info,keyinfo,key,anc_buff,next_page,next_buff,
|
||||
keypos,next_block,ret_key)) >0)
|
||||
{
|
||||
endpos=leaf_buff+maria_getint(leaf_buff);
|
||||
if (ret_value == 1)
|
||||
{
|
||||
ret_value=underflow(info,keyinfo,leaf_buff,next_page,
|
||||
next_buff,endpos);
|
||||
if (ret_value == 0 && maria_getint(leaf_buff) > keyinfo->block_length)
|
||||
{
|
||||
ret_value= _ma_split_page(info,keyinfo,key,leaf_buff,ret_key,0) | 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("test",("Inserting of key when deleting"));
|
||||
if (_ma_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
|
||||
&tmp))
|
||||
goto err;
|
||||
ret_value= _ma_insert(info,keyinfo,key,leaf_buff,endpos,keybuff,
|
||||
(uchar*) 0,(uchar*) 0,(my_off_t) 0,0);
|
||||
}
|
||||
}
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
}
|
||||
my_afree((byte*) next_buff);
|
||||
DBUG_RETURN(ret_value);
|
||||
}
|
||||
|
||||
/* Remove last key from leaf page */
|
||||
|
||||
maria_putint(leaf_buff,key_start-leaf_buff,nod_flag);
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
|
||||
/* Place last key in ancestor page on deleted key position */
|
||||
|
||||
a_length=maria_getint(anc_buff);
|
||||
endpos=anc_buff+a_length;
|
||||
if (keypos != anc_buff+2+share->base.key_reflength &&
|
||||
!_ma_get_last_key(info,keyinfo,anc_buff,ret_key,keypos,&tmp))
|
||||
goto err;
|
||||
prev_key=(keypos == anc_buff+2+share->base.key_reflength ?
|
||||
0 : ret_key);
|
||||
length=(*keyinfo->pack_key)(keyinfo,share->base.key_reflength,
|
||||
keypos == endpos ? (uchar*) 0 : keypos,
|
||||
prev_key, prev_key,
|
||||
keybuff,&s_temp);
|
||||
if (length > 0)
|
||||
bmove_upp((byte*) endpos+length,(byte*) endpos,(uint) (endpos-keypos));
|
||||
else
|
||||
bmove(keypos,keypos-length, (int) (endpos-keypos)+length);
|
||||
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
||||
/* Save pointer to next leaf */
|
||||
if (!(*keyinfo->get_key)(keyinfo,share->base.key_reflength,&keypos,ret_key))
|
||||
goto err;
|
||||
_ma_kpointer(info,keypos - share->base.key_reflength,next_block);
|
||||
maria_putint(anc_buff,a_length+length,share->base.key_reflength);
|
||||
|
||||
DBUG_RETURN( maria_getint(leaf_buff) <=
|
||||
(info->quick_mode ? MARIA_MIN_KEYBLOCK_LENGTH :
|
||||
(uint) keyinfo->underflow_block_length));
|
||||
err:
|
||||
DBUG_RETURN(-1);
|
||||
} /* del */
|
||||
|
||||
|
||||
/* Balances adjacent pages if underflow occours */
|
||||
|
||||
static int underflow(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
||||
uchar *anc_buff,
|
||||
my_off_t leaf_page,/* Ancestor page and underflow page */
|
||||
uchar *leaf_buff,
|
||||
uchar *keypos) /* Position to pos after key */
|
||||
{
|
||||
int t_length;
|
||||
uint length,anc_length,buff_length,leaf_length,p_length,s_length,nod_flag,
|
||||
key_reflength,key_length;
|
||||
my_off_t next_page;
|
||||
uchar anc_key[HA_MAX_KEY_BUFF],leaf_key[HA_MAX_KEY_BUFF],
|
||||
*buff,*endpos,*next_keypos,*anc_pos,*half_pos,*temp_pos,*prev_key,
|
||||
*after_key;
|
||||
MARIA_KEY_PARAM s_temp;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("underflow");
|
||||
DBUG_PRINT("enter",("leaf_page: %ld keypos: 0x%lx",(long) leaf_page,
|
||||
(ulong) keypos));
|
||||
DBUG_DUMP("anc_buff",(byte*) anc_buff,maria_getint(anc_buff));
|
||||
DBUG_DUMP("leaf_buff",(byte*) leaf_buff,maria_getint(leaf_buff));
|
||||
|
||||
buff=info->buff;
|
||||
info->buff_used=1;
|
||||
next_keypos=keypos;
|
||||
nod_flag=_ma_test_if_nod(leaf_buff);
|
||||
p_length=nod_flag+2;
|
||||
anc_length=maria_getint(anc_buff);
|
||||
leaf_length=maria_getint(leaf_buff);
|
||||
key_reflength=share->base.key_reflength;
|
||||
if (info->s->keyinfo+info->lastinx == keyinfo)
|
||||
info->page_changed=1;
|
||||
|
||||
if ((keypos < anc_buff+anc_length && (info->state->records & 1)) ||
|
||||
keypos == anc_buff+2+key_reflength)
|
||||
{ /* Use page right of anc-page */
|
||||
DBUG_PRINT("test",("use right page"));
|
||||
|
||||
if (keyinfo->flag & HA_BINARY_PACK_KEY)
|
||||
{
|
||||
if (!(next_keypos= _ma_get_key(info, keyinfo,
|
||||
anc_buff, buff, keypos, &length)))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Got to end of found key */
|
||||
buff[0]=buff[1]=0; /* Avoid length error check if packed key */
|
||||
if (!(*keyinfo->get_key)(keyinfo,key_reflength,&next_keypos,
|
||||
buff))
|
||||
goto err;
|
||||
}
|
||||
next_page= _ma_kpos(key_reflength,next_keypos);
|
||||
if (!_ma_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
|
||||
goto err;
|
||||
buff_length=maria_getint(buff);
|
||||
DBUG_DUMP("next",(byte*) buff,buff_length);
|
||||
|
||||
/* find keys to make a big key-page */
|
||||
bmove((byte*) next_keypos-key_reflength,(byte*) buff+2,
|
||||
key_reflength);
|
||||
if (!_ma_get_last_key(info,keyinfo,anc_buff,anc_key,next_keypos,&length)
|
||||
|| !_ma_get_last_key(info,keyinfo,leaf_buff,leaf_key,
|
||||
leaf_buff+leaf_length,&length))
|
||||
goto err;
|
||||
|
||||
/* merge pages and put parting key from anc_buff between */
|
||||
prev_key=(leaf_length == p_length ? (uchar*) 0 : leaf_key);
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,buff+p_length,
|
||||
prev_key, prev_key,
|
||||
anc_key, &s_temp);
|
||||
length=buff_length-p_length;
|
||||
endpos=buff+length+leaf_length+t_length;
|
||||
/* buff will always be larger than before !*/
|
||||
bmove_upp((byte*) endpos, (byte*) buff+buff_length,length);
|
||||
memcpy((byte*) buff, (byte*) leaf_buff,(size_t) leaf_length);
|
||||
(*keyinfo->store_key)(keyinfo,buff+leaf_length,&s_temp);
|
||||
buff_length=(uint) (endpos-buff);
|
||||
maria_putint(buff,buff_length,nod_flag);
|
||||
|
||||
/* remove key from anc_buff */
|
||||
|
||||
if (!(s_length=remove_key(keyinfo,key_reflength,keypos,anc_key,
|
||||
anc_buff+anc_length,(my_off_t *) 0)))
|
||||
goto err;
|
||||
|
||||
anc_length-=s_length;
|
||||
maria_putint(anc_buff,anc_length,key_reflength);
|
||||
|
||||
if (buff_length <= keyinfo->block_length)
|
||||
{ /* Keys in one page */
|
||||
memcpy((byte*) leaf_buff,(byte*) buff,(size_t) buff_length);
|
||||
if (_ma_dispose(info,keyinfo,next_page,DFLT_INIT_HITS))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{ /* Page is full */
|
||||
endpos=anc_buff+anc_length;
|
||||
DBUG_PRINT("test",("anc_buff: %lx endpos: %lx",anc_buff,endpos));
|
||||
if (keypos != anc_buff+2+key_reflength &&
|
||||
!_ma_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length))
|
||||
goto err;
|
||||
if (!(half_pos= _ma_find_half_pos(nod_flag, keyinfo, buff, leaf_key,
|
||||
&key_length, &after_key)))
|
||||
goto err;
|
||||
length=(uint) (half_pos-buff);
|
||||
memcpy((byte*) leaf_buff,(byte*) buff,(size_t) length);
|
||||
maria_putint(leaf_buff,length,nod_flag);
|
||||
|
||||
/* Correct new keypointer to leaf_page */
|
||||
half_pos=after_key;
|
||||
_ma_kpointer(info,leaf_key+key_length,next_page);
|
||||
/* Save key in anc_buff */
|
||||
prev_key=(keypos == anc_buff+2+key_reflength ? (uchar*) 0 : anc_key),
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,key_reflength,
|
||||
(keypos == endpos ? (uchar*) 0 :
|
||||
keypos),
|
||||
prev_key, prev_key,
|
||||
leaf_key, &s_temp);
|
||||
if (t_length >= 0)
|
||||
bmove_upp((byte*) endpos+t_length,(byte*) endpos,
|
||||
(uint) (endpos-keypos));
|
||||
else
|
||||
bmove(keypos,keypos-t_length,(uint) (endpos-keypos)+t_length);
|
||||
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
||||
maria_putint(anc_buff,(anc_length+=t_length),key_reflength);
|
||||
|
||||
/* Store key first in new page */
|
||||
if (nod_flag)
|
||||
bmove((byte*) buff+2,(byte*) half_pos-nod_flag,(size_t) nod_flag);
|
||||
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&half_pos,leaf_key))
|
||||
goto err;
|
||||
t_length=(int) (*keyinfo->pack_key)(keyinfo, nod_flag, (uchar*) 0,
|
||||
(uchar*) 0, (uchar *) 0,
|
||||
leaf_key, &s_temp);
|
||||
/* t_length will always be > 0 for a new page !*/
|
||||
length=(uint) ((buff+maria_getint(buff))-half_pos);
|
||||
bmove((byte*) buff+p_length+t_length,(byte*) half_pos,(size_t) length);
|
||||
(*keyinfo->store_key)(keyinfo,buff+p_length,&s_temp);
|
||||
maria_putint(buff,length+t_length+p_length,nod_flag);
|
||||
|
||||
if (_ma_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff))
|
||||
goto err;
|
||||
}
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
DBUG_RETURN(anc_length <= ((info->quick_mode ? MARIA_MIN_BLOCK_LENGTH :
|
||||
(uint) keyinfo->underflow_block_length)));
|
||||
}
|
||||
|
||||
DBUG_PRINT("test",("use left page"));
|
||||
|
||||
keypos= _ma_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length);
|
||||
if (!keypos)
|
||||
goto err;
|
||||
next_page= _ma_kpos(key_reflength,keypos);
|
||||
if (!_ma_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
|
||||
goto err;
|
||||
buff_length=maria_getint(buff);
|
||||
endpos=buff+buff_length;
|
||||
DBUG_DUMP("prev",(byte*) buff,buff_length);
|
||||
|
||||
/* find keys to make a big key-page */
|
||||
bmove((byte*) next_keypos - key_reflength,(byte*) leaf_buff+2,
|
||||
key_reflength);
|
||||
next_keypos=keypos;
|
||||
if (!(*keyinfo->get_key)(keyinfo,key_reflength,&next_keypos,
|
||||
anc_key))
|
||||
goto err;
|
||||
if (!_ma_get_last_key(info,keyinfo,buff,leaf_key,endpos,&length))
|
||||
goto err;
|
||||
|
||||
/* merge pages and put parting key from anc_buff between */
|
||||
prev_key=(leaf_length == p_length ? (uchar*) 0 : leaf_key);
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
|
||||
(leaf_length == p_length ?
|
||||
(uchar*) 0 : leaf_buff+p_length),
|
||||
prev_key, prev_key,
|
||||
anc_key, &s_temp);
|
||||
if (t_length >= 0)
|
||||
bmove((byte*) endpos+t_length,(byte*) leaf_buff+p_length,
|
||||
(size_t) (leaf_length-p_length));
|
||||
else /* We gained space */
|
||||
bmove((byte*) endpos,(byte*) leaf_buff+((int) p_length-t_length),
|
||||
(size_t) (leaf_length-p_length+t_length));
|
||||
|
||||
(*keyinfo->store_key)(keyinfo,endpos,&s_temp);
|
||||
buff_length=buff_length+leaf_length-p_length+t_length;
|
||||
maria_putint(buff,buff_length,nod_flag);
|
||||
|
||||
/* remove key from anc_buff */
|
||||
if (!(s_length= remove_key(keyinfo,key_reflength,keypos,anc_key,
|
||||
anc_buff+anc_length,(my_off_t *) 0)))
|
||||
goto err;
|
||||
|
||||
anc_length-=s_length;
|
||||
maria_putint(anc_buff,anc_length,key_reflength);
|
||||
|
||||
if (buff_length <= keyinfo->block_length)
|
||||
{ /* Keys in one page */
|
||||
if (_ma_dispose(info,keyinfo,leaf_page,DFLT_INIT_HITS))
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{ /* Page is full */
|
||||
if (keypos == anc_buff+2+key_reflength)
|
||||
anc_pos=0; /* First key */
|
||||
else if (!_ma_get_last_key(info,keyinfo,anc_buff,anc_pos=anc_key,keypos,
|
||||
&length))
|
||||
goto err;
|
||||
endpos= _ma_find_half_pos(nod_flag,keyinfo,buff,leaf_key,
|
||||
&key_length, &half_pos);
|
||||
if (!endpos)
|
||||
goto err;
|
||||
_ma_kpointer(info,leaf_key+key_length,leaf_page);
|
||||
/* Save key in anc_buff */
|
||||
DBUG_DUMP("anc_buff",(byte*) anc_buff,anc_length);
|
||||
DBUG_DUMP("key_to_anc",(byte*) leaf_key,key_length);
|
||||
|
||||
temp_pos=anc_buff+anc_length;
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,key_reflength,
|
||||
keypos == temp_pos ? (uchar*) 0
|
||||
: keypos,
|
||||
anc_pos, anc_pos,
|
||||
leaf_key,&s_temp);
|
||||
if (t_length > 0)
|
||||
bmove_upp((byte*) temp_pos+t_length,(byte*) temp_pos,
|
||||
(uint) (temp_pos-keypos));
|
||||
else
|
||||
bmove(keypos,keypos-t_length,(uint) (temp_pos-keypos)+t_length);
|
||||
(*keyinfo->store_key)(keyinfo,keypos,&s_temp);
|
||||
maria_putint(anc_buff,(anc_length+=t_length),key_reflength);
|
||||
|
||||
/* Store first key on new page */
|
||||
if (nod_flag)
|
||||
bmove((byte*) leaf_buff+2,(byte*) half_pos-nod_flag,(size_t) nod_flag);
|
||||
if (!(length=(*keyinfo->get_key)(keyinfo,nod_flag,&half_pos,leaf_key)))
|
||||
goto err;
|
||||
DBUG_DUMP("key_to_leaf",(byte*) leaf_key,length);
|
||||
t_length=(*keyinfo->pack_key)(keyinfo,nod_flag, (uchar*) 0,
|
||||
(uchar*) 0, (uchar*) 0, leaf_key, &s_temp);
|
||||
length=(uint) ((buff+buff_length)-half_pos);
|
||||
DBUG_PRINT("info",("t_length: %d length: %d",t_length,(int) length));
|
||||
bmove((byte*) leaf_buff+p_length+t_length,(byte*) half_pos,
|
||||
(size_t) length);
|
||||
(*keyinfo->store_key)(keyinfo,leaf_buff+p_length,&s_temp);
|
||||
maria_putint(leaf_buff,length+t_length+p_length,nod_flag);
|
||||
if (_ma_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
|
||||
goto err;
|
||||
maria_putint(buff,endpos-buff,nod_flag);
|
||||
}
|
||||
if (_ma_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff))
|
||||
goto err;
|
||||
DBUG_RETURN(anc_length <= (uint) keyinfo->block_length/2);
|
||||
|
||||
err:
|
||||
DBUG_RETURN(-1);
|
||||
} /* underflow */
|
||||
|
||||
|
||||
/*
|
||||
remove a key from packed buffert
|
||||
The current code doesn't handle the case that the next key may be
|
||||
packed better against the previous key if there is a case difference
|
||||
returns how many chars was removed or 0 on error
|
||||
*/
|
||||
|
||||
static uint remove_key(MARIA_KEYDEF *keyinfo, uint nod_flag,
|
||||
uchar *keypos, /* Where key starts */
|
||||
uchar *lastkey, /* key to be removed */
|
||||
uchar *page_end, /* End of page */
|
||||
my_off_t *next_block) /* ptr to next block */
|
||||
{
|
||||
int s_length;
|
||||
uchar *start;
|
||||
DBUG_ENTER("remove_key");
|
||||
DBUG_PRINT("enter",("keypos: %lx page_end: %lx",keypos,page_end));
|
||||
|
||||
start=keypos;
|
||||
if (!(keyinfo->flag &
|
||||
(HA_PACK_KEY | HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY |
|
||||
HA_BINARY_PACK_KEY)))
|
||||
{
|
||||
s_length=(int) (keyinfo->keylength+nod_flag);
|
||||
if (next_block && nod_flag)
|
||||
*next_block= _ma_kpos(nod_flag,keypos+s_length);
|
||||
}
|
||||
else
|
||||
{ /* Let keypos point at next key */
|
||||
/* Calculate length of key */
|
||||
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey))
|
||||
DBUG_RETURN(0); /* Error */
|
||||
|
||||
if (next_block && nod_flag)
|
||||
*next_block= _ma_kpos(nod_flag,keypos);
|
||||
s_length=(int) (keypos-start);
|
||||
if (keypos != page_end)
|
||||
{
|
||||
if (keyinfo->flag & HA_BINARY_PACK_KEY)
|
||||
{
|
||||
uchar *old_key=start;
|
||||
uint next_length,prev_length,prev_pack_length;
|
||||
get_key_length(next_length,keypos);
|
||||
get_key_pack_length(prev_length,prev_pack_length,old_key);
|
||||
if (next_length > prev_length)
|
||||
{
|
||||
/* We have to copy data from the current key to the next key */
|
||||
bmove_upp((char*) keypos,(char*) (lastkey+next_length),
|
||||
(next_length-prev_length));
|
||||
keypos-=(next_length-prev_length)+prev_pack_length;
|
||||
store_key_length(keypos,prev_length);
|
||||
s_length=(int) (keypos-start);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check if a variable length first key part */
|
||||
if ((keyinfo->seg->flag & HA_PACK_KEY) && *keypos & 128)
|
||||
{
|
||||
/* Next key is packed against the current one */
|
||||
uint next_length,prev_length,prev_pack_length,lastkey_length,
|
||||
rest_length;
|
||||
if (keyinfo->seg[0].length >= 127)
|
||||
{
|
||||
if (!(prev_length=mi_uint2korr(start) & 32767))
|
||||
goto end;
|
||||
next_length=mi_uint2korr(keypos) & 32767;
|
||||
keypos+=2;
|
||||
prev_pack_length=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(prev_length= *start & 127))
|
||||
goto end; /* Same key as previous*/
|
||||
next_length= *keypos & 127;
|
||||
keypos++;
|
||||
prev_pack_length=1;
|
||||
}
|
||||
if (!(*start & 128))
|
||||
prev_length=0; /* prev key not packed */
|
||||
if (keyinfo->seg[0].flag & HA_NULL_PART)
|
||||
lastkey++; /* Skip null marker */
|
||||
get_key_length(lastkey_length,lastkey);
|
||||
if (!next_length) /* Same key after */
|
||||
{
|
||||
next_length=lastkey_length;
|
||||
rest_length=0;
|
||||
}
|
||||
else
|
||||
get_key_length(rest_length,keypos);
|
||||
|
||||
if (next_length >= prev_length)
|
||||
{ /* Key after is based on deleted key */
|
||||
uint pack_length,tmp;
|
||||
bmove_upp((char*) keypos,(char*) (lastkey+next_length),
|
||||
tmp=(next_length-prev_length));
|
||||
rest_length+=tmp;
|
||||
pack_length= prev_length ? get_pack_length(rest_length): 0;
|
||||
keypos-=tmp+pack_length+prev_pack_length;
|
||||
s_length=(int) (keypos-start);
|
||||
if (prev_length) /* Pack against prev key */
|
||||
{
|
||||
*keypos++= start[0];
|
||||
if (prev_pack_length == 2)
|
||||
*keypos++= start[1];
|
||||
store_key_length(keypos,rest_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Next key is not packed anymore */
|
||||
if (keyinfo->seg[0].flag & HA_NULL_PART)
|
||||
{
|
||||
rest_length++; /* Mark not null */
|
||||
}
|
||||
if (prev_pack_length == 2)
|
||||
{
|
||||
mi_int2store(keypos,rest_length);
|
||||
}
|
||||
else
|
||||
*keypos= rest_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end:
|
||||
bmove((byte*) start,(byte*) start+s_length,
|
||||
(uint) (page_end-start-s_length));
|
||||
DBUG_RETURN((uint) s_length);
|
||||
} /* remove_key */
|
||||
77
storage/maria/ma_delete_all.c
Normal file
77
storage/maria/ma_delete_all.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Remove all rows from a MARIA table */
|
||||
/* This clears the status information and truncates files */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
int maria_delete_all_rows(MARIA_HA *info)
|
||||
{
|
||||
uint i;
|
||||
MARIA_SHARE *share=info->s;
|
||||
MARIA_STATE_INFO *state=&share->state;
|
||||
DBUG_ENTER("maria_delete_all_rows");
|
||||
|
||||
if (share->options & HA_OPTION_READ_ONLY_DATA)
|
||||
{
|
||||
DBUG_RETURN(my_errno=EACCES);
|
||||
}
|
||||
if (_ma_readinfo(info,F_WRLCK,1))
|
||||
DBUG_RETURN(my_errno);
|
||||
if (_ma_mark_file_changed(info))
|
||||
goto err;
|
||||
|
||||
info->state->records=info->state->del=state->split=0;
|
||||
state->dellink = HA_OFFSET_ERROR;
|
||||
state->sortkey= (ushort) ~0;
|
||||
info->state->key_file_length=share->base.keystart;
|
||||
info->state->data_file_length=0;
|
||||
info->state->empty=info->state->key_empty=0;
|
||||
info->state->checksum=0;
|
||||
|
||||
for (i=share->base.max_key_block_length/MARIA_MIN_KEY_BLOCK_LENGTH ; i-- ; )
|
||||
state->key_del[i]= HA_OFFSET_ERROR;
|
||||
for (i=0 ; i < share->base.keys ; i++)
|
||||
state->key_root[i]= HA_OFFSET_ERROR;
|
||||
|
||||
/*
|
||||
If we are using delayed keys or if the user has done changes to the tables
|
||||
since it was locked then there may be key blocks in the key cache
|
||||
*/
|
||||
flush_key_blocks(share->key_cache, share->kfile, FLUSH_IGNORE_CHANGED);
|
||||
if (my_chsize(info->dfile, 0, 0, MYF(MY_WME)) ||
|
||||
my_chsize(share->kfile, share->base.keystart, 0, MYF(MY_WME)) )
|
||||
goto err;
|
||||
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
||||
#ifdef HAVE_MMAP
|
||||
/* Resize mmaped area */
|
||||
rw_wrlock(&info->s->mmap_lock);
|
||||
_ma_remap_file(info, (my_off_t)0);
|
||||
rw_unlock(&info->s->mmap_lock);
|
||||
#endif
|
||||
allow_break(); /* Allow SIGHUP & SIGINT */
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
{
|
||||
int save_errno=my_errno;
|
||||
VOID(_ma_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
|
||||
info->update|=HA_STATE_WRITTEN; /* Buffer changed */
|
||||
allow_break(); /* Allow SIGHUP & SIGINT */
|
||||
DBUG_RETURN(my_errno=save_errno);
|
||||
}
|
||||
} /* maria_delete */
|
||||
70
storage/maria/ma_delete_table.c
Normal file
70
storage/maria/ma_delete_table.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
deletes a table
|
||||
*/
|
||||
|
||||
#include "ma_fulltext.h"
|
||||
|
||||
int maria_delete_table(const char *name)
|
||||
{
|
||||
char from[FN_REFLEN];
|
||||
#ifdef USE_RAID
|
||||
uint raid_type=0,raid_chunks=0;
|
||||
#endif
|
||||
DBUG_ENTER("maria_delete_table");
|
||||
|
||||
#ifdef EXTRA_DEBUG
|
||||
_ma_check_table_is_closed(name,"delete");
|
||||
#endif
|
||||
#ifdef USE_RAID
|
||||
{
|
||||
MARIA_HA *info;
|
||||
/*
|
||||
When built with RAID support, we need to determine if this table
|
||||
makes use of the raid feature. If yes, we need to remove all raid
|
||||
chunks. This is done with my_raid_delete(). Unfortunately it is
|
||||
necessary to open the table just to check this. We use
|
||||
'open_for_repair' to be able to open even a crashed table. If even
|
||||
this open fails, we assume no raid configuration for this table
|
||||
and try to remove the normal data file only. This may however
|
||||
leave the raid chunks behind.
|
||||
*/
|
||||
if (!(info= maria_open(name, O_RDONLY, HA_OPEN_FOR_REPAIR)))
|
||||
raid_type= 0;
|
||||
else
|
||||
{
|
||||
raid_type= info->s->base.raid_type;
|
||||
raid_chunks= info->s->base.raid_chunks;
|
||||
maria_close(info);
|
||||
}
|
||||
}
|
||||
#ifdef EXTRA_DEBUG
|
||||
_ma_check_table_is_closed(name,"delete");
|
||||
#endif
|
||||
#endif /* USE_RAID */
|
||||
|
||||
fn_format(from,name,"",MARIA_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
|
||||
if (my_delete_with_symlink(from, MYF(MY_WME)))
|
||||
DBUG_RETURN(my_errno);
|
||||
fn_format(from,name,"",MARIA_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
|
||||
#ifdef USE_RAID
|
||||
if (raid_type)
|
||||
DBUG_RETURN(my_raid_delete(from, raid_chunks, MYF(MY_WME)) ? my_errno : 0);
|
||||
#endif
|
||||
DBUG_RETURN(my_delete_with_symlink(from, MYF(MY_WME)) ? my_errno : 0);
|
||||
}
|
||||
1920
storage/maria/ma_dynrec.c
Normal file
1920
storage/maria/ma_dynrec.c
Normal file
File diff suppressed because it is too large
Load Diff
441
storage/maria/ma_extra.c
Normal file
441
storage/maria/ma_extra.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
static void maria_extra_keyflag(MARIA_HA *info, enum ha_extra_function function);
|
||||
|
||||
|
||||
/*
|
||||
Set options and buffers to optimize table handling
|
||||
|
||||
SYNOPSIS
|
||||
maria_extra()
|
||||
info open table
|
||||
function operation
|
||||
extra_arg Pointer to extra argument (normally pointer to ulong)
|
||||
Used when function is one of:
|
||||
HA_EXTRA_WRITE_CACHE
|
||||
HA_EXTRA_CACHE
|
||||
RETURN VALUES
|
||||
0 ok
|
||||
# error
|
||||
*/
|
||||
|
||||
int maria_extra(MARIA_HA *info, enum ha_extra_function function, void *extra_arg)
|
||||
{
|
||||
int error=0;
|
||||
ulong cache_size;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_extra");
|
||||
DBUG_PRINT("enter",("function: %d",(int) function));
|
||||
|
||||
switch (function) {
|
||||
case HA_EXTRA_RESET_STATE: /* Reset state (don't free buffers) */
|
||||
info->lastinx= 0; /* Use first index as def */
|
||||
info->last_search_keypage=info->lastpos= HA_OFFSET_ERROR;
|
||||
info->page_changed=1;
|
||||
/* Next/prev gives first/last */
|
||||
if (info->opt_flag & READ_CACHE_USED)
|
||||
{
|
||||
reinit_io_cache(&info->rec_cache,READ_CACHE,0,
|
||||
(pbool) (info->lock_type != F_UNLCK),
|
||||
(pbool) test(info->update & HA_STATE_ROW_CHANGED)
|
||||
);
|
||||
}
|
||||
info->update= ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND |
|
||||
HA_STATE_PREV_FOUND);
|
||||
break;
|
||||
case HA_EXTRA_CACHE:
|
||||
if (info->lock_type == F_UNLCK &&
|
||||
(share->options & HA_OPTION_PACK_RECORD))
|
||||
{
|
||||
error=1; /* Not possibly if not locked */
|
||||
my_errno=EACCES;
|
||||
break;
|
||||
}
|
||||
if (info->s->file_map) /* Don't use cache if mmap */
|
||||
break;
|
||||
#if defined(HAVE_MMAP) && defined(HAVE_MADVISE)
|
||||
if ((share->options & HA_OPTION_COMPRESS_RECORD))
|
||||
{
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
if (_ma_memmap_file(info))
|
||||
{
|
||||
/* We don't nead MADV_SEQUENTIAL if small file */
|
||||
madvise(share->file_map,share->state.state.data_file_length,
|
||||
share->state.state.data_file_length <= RECORD_CACHE_SIZE*16 ?
|
||||
MADV_RANDOM : MADV_SEQUENTIAL);
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
}
|
||||
#endif
|
||||
if (info->opt_flag & WRITE_CACHE_USED)
|
||||
{
|
||||
info->opt_flag&= ~WRITE_CACHE_USED;
|
||||
if ((error=end_io_cache(&info->rec_cache)))
|
||||
break;
|
||||
}
|
||||
if (!(info->opt_flag &
|
||||
(READ_CACHE_USED | WRITE_CACHE_USED | MEMMAP_USED)))
|
||||
{
|
||||
cache_size= (extra_arg ? *(ulong*) extra_arg :
|
||||
my_default_record_cache_size);
|
||||
if (!(init_io_cache(&info->rec_cache,info->dfile,
|
||||
(uint) min(info->state->data_file_length+1,
|
||||
cache_size),
|
||||
READ_CACHE,0L,(pbool) (info->lock_type != F_UNLCK),
|
||||
MYF(share->write_flag & MY_WAIT_IF_FULL))))
|
||||
{
|
||||
info->opt_flag|=READ_CACHE_USED;
|
||||
info->update&= ~HA_STATE_ROW_CHANGED;
|
||||
}
|
||||
if (share->concurrent_insert)
|
||||
info->rec_cache.end_of_file=info->state->data_file_length;
|
||||
}
|
||||
break;
|
||||
case HA_EXTRA_REINIT_CACHE:
|
||||
if (info->opt_flag & READ_CACHE_USED)
|
||||
{
|
||||
reinit_io_cache(&info->rec_cache,READ_CACHE,info->nextpos,
|
||||
(pbool) (info->lock_type != F_UNLCK),
|
||||
(pbool) test(info->update & HA_STATE_ROW_CHANGED));
|
||||
info->update&= ~HA_STATE_ROW_CHANGED;
|
||||
if (share->concurrent_insert)
|
||||
info->rec_cache.end_of_file=info->state->data_file_length;
|
||||
}
|
||||
break;
|
||||
case HA_EXTRA_WRITE_CACHE:
|
||||
if (info->lock_type == F_UNLCK)
|
||||
{
|
||||
error=1; /* Not possibly if not locked */
|
||||
break;
|
||||
}
|
||||
|
||||
cache_size= (extra_arg ? *(ulong*) extra_arg :
|
||||
my_default_record_cache_size);
|
||||
if (!(info->opt_flag &
|
||||
(READ_CACHE_USED | WRITE_CACHE_USED | OPT_NO_ROWS)) &&
|
||||
!share->state.header.uniques)
|
||||
if (!(init_io_cache(&info->rec_cache,info->dfile, cache_size,
|
||||
WRITE_CACHE,info->state->data_file_length,
|
||||
(pbool) (info->lock_type != F_UNLCK),
|
||||
MYF(share->write_flag & MY_WAIT_IF_FULL))))
|
||||
{
|
||||
info->opt_flag|=WRITE_CACHE_USED;
|
||||
info->update&= ~(HA_STATE_ROW_CHANGED |
|
||||
HA_STATE_WRITE_AT_END |
|
||||
HA_STATE_EXTEND_BLOCK);
|
||||
}
|
||||
break;
|
||||
case HA_EXTRA_PREPARE_FOR_UPDATE:
|
||||
if (info->s->data_file_type != DYNAMIC_RECORD)
|
||||
break;
|
||||
/* Remove read/write cache if dynamic rows */
|
||||
case HA_EXTRA_NO_CACHE:
|
||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||
{
|
||||
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
|
||||
error=end_io_cache(&info->rec_cache);
|
||||
/* Sergei will insert full text index caching here */
|
||||
}
|
||||
#if defined(HAVE_MMAP) && defined(HAVE_MADVISE)
|
||||
if (info->opt_flag & MEMMAP_USED)
|
||||
madvise(share->file_map,share->state.state.data_file_length,MADV_RANDOM);
|
||||
#endif
|
||||
break;
|
||||
case HA_EXTRA_FLUSH_CACHE:
|
||||
if (info->opt_flag & WRITE_CACHE_USED)
|
||||
{
|
||||
if ((error=flush_io_cache(&info->rec_cache)))
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info); /* Fatal error found */
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HA_EXTRA_NO_READCHECK:
|
||||
info->opt_flag&= ~READ_CHECK_USED; /* No readcheck */
|
||||
break;
|
||||
case HA_EXTRA_READCHECK:
|
||||
info->opt_flag|= READ_CHECK_USED;
|
||||
break;
|
||||
case HA_EXTRA_KEYREAD: /* Read only keys to record */
|
||||
case HA_EXTRA_REMEMBER_POS:
|
||||
info->opt_flag |= REMEMBER_OLD_POS;
|
||||
bmove((byte*) info->lastkey+share->base.max_key_length*2,
|
||||
(byte*) info->lastkey,info->lastkey_length);
|
||||
info->save_update= info->update;
|
||||
info->save_lastinx= info->lastinx;
|
||||
info->save_lastpos= info->lastpos;
|
||||
info->save_lastkey_length=info->lastkey_length;
|
||||
if (function == HA_EXTRA_REMEMBER_POS)
|
||||
break;
|
||||
/* fall through */
|
||||
case HA_EXTRA_KEYREAD_CHANGE_POS:
|
||||
info->opt_flag |= KEY_READ_USED;
|
||||
info->read_record= _ma_read_key_record;
|
||||
break;
|
||||
case HA_EXTRA_NO_KEYREAD:
|
||||
case HA_EXTRA_RESTORE_POS:
|
||||
if (info->opt_flag & REMEMBER_OLD_POS)
|
||||
{
|
||||
bmove((byte*) info->lastkey,
|
||||
(byte*) info->lastkey+share->base.max_key_length*2,
|
||||
info->save_lastkey_length);
|
||||
info->update= info->save_update | HA_STATE_WRITTEN;
|
||||
info->lastinx= info->save_lastinx;
|
||||
info->lastpos= info->save_lastpos;
|
||||
info->lastkey_length=info->save_lastkey_length;
|
||||
}
|
||||
info->read_record= share->read_record;
|
||||
info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS);
|
||||
break;
|
||||
case HA_EXTRA_NO_USER_CHANGE: /* Database is somehow locked agains changes */
|
||||
info->lock_type= F_EXTRA_LCK; /* Simulate as locked */
|
||||
break;
|
||||
case HA_EXTRA_WAIT_LOCK:
|
||||
info->lock_wait=0;
|
||||
break;
|
||||
case HA_EXTRA_NO_WAIT_LOCK:
|
||||
info->lock_wait=MY_DONT_WAIT;
|
||||
break;
|
||||
case HA_EXTRA_NO_KEYS:
|
||||
if (info->lock_type == F_UNLCK)
|
||||
{
|
||||
error=1; /* Not possibly if not lock */
|
||||
break;
|
||||
}
|
||||
if (maria_is_any_key_active(share->state.key_map))
|
||||
{
|
||||
MARIA_KEYDEF *key=share->keyinfo;
|
||||
uint i;
|
||||
for (i=0 ; i < share->base.keys ; i++,key++)
|
||||
{
|
||||
if (!(key->flag & HA_NOSAME) && info->s->base.auto_key != i+1)
|
||||
{
|
||||
maria_clear_key_active(share->state.key_map, i);
|
||||
info->update|= HA_STATE_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!share->changed)
|
||||
{
|
||||
share->state.changed|= STATE_CHANGED | STATE_NOT_ANALYZED;
|
||||
share->changed=1; /* Update on close */
|
||||
if (!share->global_changed)
|
||||
{
|
||||
share->global_changed=1;
|
||||
share->state.open_count++;
|
||||
}
|
||||
}
|
||||
share->state.state= *info->state;
|
||||
error=_ma_state_info_write(share->kfile,&share->state,1 | 2);
|
||||
}
|
||||
break;
|
||||
case HA_EXTRA_FORCE_REOPEN:
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
share->last_version= 0L; /* Impossible version */
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
break;
|
||||
case HA_EXTRA_PREPARE_FOR_DELETE:
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
share->last_version= 0L; /* Impossible version */
|
||||
#ifdef __WIN__
|
||||
/* Close the isam and data files as Win32 can't drop an open table */
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
if (flush_key_blocks(share->key_cache, share->kfile,
|
||||
(function == HA_EXTRA_FORCE_REOPEN ?
|
||||
FLUSH_RELEASE : FLUSH_IGNORE_CHANGED)))
|
||||
{
|
||||
error=my_errno;
|
||||
share->changed=1;
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info); /* Fatal error found */
|
||||
}
|
||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||
{
|
||||
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
|
||||
error=end_io_cache(&info->rec_cache);
|
||||
}
|
||||
if (info->lock_type != F_UNLCK && ! info->was_locked)
|
||||
{
|
||||
info->was_locked=info->lock_type;
|
||||
if (maria_lock_database(info,F_UNLCK))
|
||||
error=my_errno;
|
||||
info->lock_type = F_UNLCK;
|
||||
}
|
||||
if (share->kfile >= 0)
|
||||
_ma_decrement_open_count(info);
|
||||
if (share->kfile >= 0 && my_close(share->kfile,MYF(0)))
|
||||
error=my_errno;
|
||||
{
|
||||
LIST *list_element ;
|
||||
for (list_element=maria_open_list ;
|
||||
list_element ;
|
||||
list_element=list_element->next)
|
||||
{
|
||||
MARIA_HA *tmpinfo=(MARIA_HA*) list_element->data;
|
||||
if (tmpinfo->s == info->s)
|
||||
{
|
||||
if (tmpinfo->dfile >= 0 && my_close(tmpinfo->dfile,MYF(0)))
|
||||
error = my_errno;
|
||||
tmpinfo->dfile= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
share->kfile= -1; /* Files aren't open anymore */
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
#endif
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
break;
|
||||
case HA_EXTRA_FLUSH:
|
||||
if (!share->temporary)
|
||||
flush_key_blocks(share->key_cache, share->kfile, FLUSH_KEEP);
|
||||
#ifdef HAVE_PWRITE
|
||||
_ma_decrement_open_count(info);
|
||||
#endif
|
||||
if (share->not_flushed)
|
||||
{
|
||||
share->not_flushed=0;
|
||||
if (my_sync(share->kfile, MYF(0)))
|
||||
error= my_errno;
|
||||
if (my_sync(info->dfile, MYF(0)))
|
||||
error= my_errno;
|
||||
if (error)
|
||||
{
|
||||
share->changed=1;
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info); /* Fatal error found */
|
||||
}
|
||||
}
|
||||
if (share->base.blobs)
|
||||
_ma_alloc_rec_buff(info, -1, &info->rec_buff);
|
||||
break;
|
||||
case HA_EXTRA_NORMAL: /* Theese isn't in use */
|
||||
info->quick_mode=0;
|
||||
break;
|
||||
case HA_EXTRA_QUICK:
|
||||
info->quick_mode=1;
|
||||
break;
|
||||
case HA_EXTRA_NO_ROWS:
|
||||
if (!share->state.header.uniques)
|
||||
info->opt_flag|= OPT_NO_ROWS;
|
||||
break;
|
||||
case HA_EXTRA_PRELOAD_BUFFER_SIZE:
|
||||
info->preload_buff_size= *((ulong *) extra_arg);
|
||||
break;
|
||||
case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
|
||||
case HA_EXTRA_CHANGE_KEY_TO_DUP:
|
||||
maria_extra_keyflag(info, function);
|
||||
break;
|
||||
case HA_EXTRA_MMAP:
|
||||
#ifdef HAVE_MMAP
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
if (!share->file_map)
|
||||
{
|
||||
if (_ma_dynmap_file(info, share->state.state.data_file_length))
|
||||
{
|
||||
DBUG_PRINT("warning",("mmap failed: errno: %d",errno));
|
||||
error= my_errno= errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
share->file_read= _ma_mmap_pread;
|
||||
share->file_write= _ma_mmap_pwrite;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
#endif
|
||||
break;
|
||||
case HA_EXTRA_MARK_AS_LOG_TABLE:
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
share->is_log_table= TRUE;
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
break;
|
||||
case HA_EXTRA_KEY_CACHE:
|
||||
case HA_EXTRA_NO_KEY_CACHE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
{
|
||||
char tmp[1];
|
||||
tmp[0]=function;
|
||||
}
|
||||
DBUG_RETURN(error);
|
||||
} /* maria_extra */
|
||||
|
||||
|
||||
/*
|
||||
Start/Stop Inserting Duplicates Into a Table, WL#1648.
|
||||
*/
|
||||
static void maria_extra_keyflag(MARIA_HA *info, enum ha_extra_function function)
|
||||
{
|
||||
uint idx;
|
||||
|
||||
for (idx= 0; idx< info->s->base.keys; idx++)
|
||||
{
|
||||
switch (function) {
|
||||
case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
|
||||
info->s->keyinfo[idx].flag|= HA_NOSAME;
|
||||
break;
|
||||
case HA_EXTRA_CHANGE_KEY_TO_DUP:
|
||||
info->s->keyinfo[idx].flag&= ~(HA_NOSAME);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int maria_reset(MARIA_HA *info)
|
||||
{
|
||||
int error= 0;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_reset");
|
||||
/*
|
||||
Free buffers and reset the following flags:
|
||||
EXTRA_CACHE, EXTRA_WRITE_CACHE, EXTRA_KEYREAD, EXTRA_QUICK
|
||||
|
||||
If the row buffer cache is large (for dynamic tables), reduce it
|
||||
to save memory.
|
||||
*/
|
||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||
{
|
||||
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
|
||||
error= end_io_cache(&info->rec_cache);
|
||||
}
|
||||
if (share->base.blobs)
|
||||
_ma_alloc_rec_buff(info, -1, &info->rec_buff);
|
||||
#if defined(HAVE_MMAP) && defined(HAVE_MADVISE)
|
||||
if (info->opt_flag & MEMMAP_USED)
|
||||
madvise(share->file_map,share->state.state.data_file_length,MADV_RANDOM);
|
||||
#endif
|
||||
info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS);
|
||||
info->quick_mode=0;
|
||||
info->lastinx= 0; /* Use first index as def */
|
||||
info->last_search_keypage= info->lastpos= HA_OFFSET_ERROR;
|
||||
info->page_changed= 1;
|
||||
info->update= ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND |
|
||||
HA_STATE_PREV_FOUND);
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
965
storage/maria/ma_ft_boolean_search.c
Normal file
965
storage/maria/ma_ft_boolean_search.c
Normal file
@@ -0,0 +1,965 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
/* TODO: add caching - pre-read several index entries at once */
|
||||
|
||||
/*
|
||||
Added optimization for full-text queries with plus-words. It was
|
||||
implemented by sharing maximal document id (max_docid) variable
|
||||
inside plus subtree. max_docid could be used by any word in plus
|
||||
subtree, but it could be updated by plus-word only.
|
||||
|
||||
The idea is: there is no need to search for docid smaller than
|
||||
biggest docid inside current plus subtree.
|
||||
|
||||
Examples:
|
||||
+word1 word2
|
||||
share same max_docid
|
||||
max_docid updated by word1
|
||||
+word1 +(word2 word3)
|
||||
share same max_docid
|
||||
max_docid updated by word1
|
||||
+(word1 -word2) +(+word3 word4)
|
||||
share same max_docid
|
||||
max_docid updated by word3
|
||||
*/
|
||||
|
||||
#define FT_CORE
|
||||
#include "ma_ftdefs.h"
|
||||
|
||||
/* search with boolean queries */
|
||||
|
||||
static double _wghts[11]=
|
||||
{
|
||||
0.131687242798354,
|
||||
0.197530864197531,
|
||||
0.296296296296296,
|
||||
0.444444444444444,
|
||||
0.666666666666667,
|
||||
1.000000000000000,
|
||||
1.500000000000000,
|
||||
2.250000000000000,
|
||||
3.375000000000000,
|
||||
5.062500000000000,
|
||||
7.593750000000000};
|
||||
static double *wghts=_wghts+5; /* wghts[i] = 1.5**i */
|
||||
|
||||
static double _nwghts[11]=
|
||||
{
|
||||
-0.065843621399177,
|
||||
-0.098765432098766,
|
||||
-0.148148148148148,
|
||||
-0.222222222222222,
|
||||
-0.333333333333334,
|
||||
-0.500000000000000,
|
||||
-0.750000000000000,
|
||||
-1.125000000000000,
|
||||
-1.687500000000000,
|
||||
-2.531250000000000,
|
||||
-3.796875000000000};
|
||||
static double *nwghts=_nwghts+5; /* nwghts[i] = -0.5*1.5**i */
|
||||
|
||||
#define FTB_FLAG_TRUNC 1
|
||||
/* At most one of the following flags can be set */
|
||||
#define FTB_FLAG_YES 2
|
||||
#define FTB_FLAG_NO 4
|
||||
#define FTB_FLAG_WONLY 8
|
||||
|
||||
typedef struct st_ftb_expr FTB_EXPR;
|
||||
struct st_ftb_expr
|
||||
{
|
||||
FTB_EXPR *up;
|
||||
uint flags;
|
||||
/* ^^^^^^^^^^^^^^^^^^ FTB_{EXPR,WORD} common section */
|
||||
my_off_t docid[2];
|
||||
my_off_t max_docid;
|
||||
float weight;
|
||||
float cur_weight;
|
||||
LIST *phrase; /* phrase words */
|
||||
LIST *document; /* for phrase search */
|
||||
uint yesses; /* number of "yes" words matched */
|
||||
uint nos; /* number of "no" words matched */
|
||||
uint ythresh; /* number of "yes" words in expr */
|
||||
uint yweaks; /* number of "yes" words for scan only */
|
||||
};
|
||||
|
||||
typedef struct st_ftb_word
|
||||
{
|
||||
FTB_EXPR *up;
|
||||
uint flags;
|
||||
/* ^^^^^^^^^^^^^^^^^^ FTB_{EXPR,WORD} common section */
|
||||
my_off_t docid[2]; /* for index search and for scan */
|
||||
my_off_t key_root;
|
||||
my_off_t *max_docid;
|
||||
MARIA_KEYDEF *keyinfo;
|
||||
struct st_ftb_word *prev;
|
||||
float weight;
|
||||
uint ndepth;
|
||||
uint len;
|
||||
uchar off;
|
||||
byte word[1];
|
||||
} FTB_WORD;
|
||||
|
||||
typedef struct st_ft_info
|
||||
{
|
||||
struct _ft_vft *please;
|
||||
MARIA_HA *info;
|
||||
CHARSET_INFO *charset;
|
||||
FTB_EXPR *root;
|
||||
FTB_WORD **list;
|
||||
FTB_WORD *last_word;
|
||||
MEM_ROOT mem_root;
|
||||
QUEUE queue;
|
||||
TREE no_dupes;
|
||||
my_off_t lastpos;
|
||||
uint keynr;
|
||||
uchar with_scan;
|
||||
enum { UNINITIALIZED, READY, INDEX_SEARCH, INDEX_DONE } state;
|
||||
} FTB;
|
||||
|
||||
static int FTB_WORD_cmp(my_off_t *v, FTB_WORD *a, FTB_WORD *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* if a==curdoc, take it as a < b */
|
||||
if (v && a->docid[0] == *v)
|
||||
return -1;
|
||||
|
||||
/* ORDER BY docid, ndepth DESC */
|
||||
i=CMP_NUM(a->docid[0], b->docid[0]);
|
||||
if (!i)
|
||||
i=CMP_NUM(b->ndepth,a->ndepth);
|
||||
return i;
|
||||
}
|
||||
|
||||
static int FTB_WORD_cmp_list(CHARSET_INFO *cs, FTB_WORD **a, FTB_WORD **b)
|
||||
{
|
||||
/* ORDER BY word DESC, ndepth DESC */
|
||||
int i= ha_compare_text(cs, (uchar*) (*b)->word+1,(*b)->len-1,
|
||||
(uchar*) (*a)->word+1,(*a)->len-1,0,0);
|
||||
if (!i)
|
||||
i=CMP_NUM((*b)->ndepth,(*a)->ndepth);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
typedef struct st_my_ftb_param
|
||||
{
|
||||
FTB *ftb;
|
||||
FTB_EXPR *ftbe;
|
||||
byte *up_quot;
|
||||
uint depth;
|
||||
} MY_FTB_PARAM;
|
||||
|
||||
|
||||
static int ftb_query_add_word(MYSQL_FTPARSER_PARAM *param,
|
||||
char *word, int word_len,
|
||||
MYSQL_FTPARSER_BOOLEAN_INFO *info)
|
||||
{
|
||||
MY_FTB_PARAM *ftb_param= param->mysql_ftparam;
|
||||
FTB_WORD *ftbw;
|
||||
FTB_EXPR *ftbe, *tmp_expr;
|
||||
FT_WORD *phrase_word;
|
||||
LIST *tmp_element;
|
||||
int r= info->weight_adjust;
|
||||
float weight= (float)
|
||||
(info->wasign ? nwghts : wghts)[(r>5)?5:((r<-5)?-5:r)];
|
||||
|
||||
switch (info->type) {
|
||||
case FT_TOKEN_WORD:
|
||||
ftbw= (FTB_WORD *)alloc_root(&ftb_param->ftb->mem_root,
|
||||
sizeof(FTB_WORD) +
|
||||
(info->trunc ? HA_MAX_KEY_BUFF :
|
||||
word_len * ftb_param->ftb->charset->mbmaxlen +
|
||||
HA_FT_WLEN +
|
||||
ftb_param->ftb->info->s->rec_reflength));
|
||||
ftbw->len= word_len + 1;
|
||||
ftbw->flags= 0;
|
||||
ftbw->off= 0;
|
||||
if (info->yesno > 0) ftbw->flags|= FTB_FLAG_YES;
|
||||
if (info->yesno < 0) ftbw->flags|= FTB_FLAG_NO;
|
||||
if (info->trunc) ftbw->flags|= FTB_FLAG_TRUNC;
|
||||
ftbw->weight= weight;
|
||||
ftbw->up= ftb_param->ftbe;
|
||||
ftbw->docid[0]= ftbw->docid[1]= HA_OFFSET_ERROR;
|
||||
ftbw->ndepth= (info->yesno < 0) + ftb_param->depth;
|
||||
ftbw->key_root= HA_OFFSET_ERROR;
|
||||
memcpy(ftbw->word + 1, word, word_len);
|
||||
ftbw->word[0]= word_len;
|
||||
if (info->yesno > 0) ftbw->up->ythresh++;
|
||||
ftb_param->ftb->queue.max_elements++;
|
||||
ftbw->prev= ftb_param->ftb->last_word;
|
||||
ftb_param->ftb->last_word= ftbw;
|
||||
ftb_param->ftb->with_scan|= (info->trunc & FTB_FLAG_TRUNC);
|
||||
for (tmp_expr= ftb_param->ftbe; tmp_expr->up; tmp_expr= tmp_expr->up)
|
||||
if (! (tmp_expr->flags & FTB_FLAG_YES))
|
||||
break;
|
||||
ftbw->max_docid= &tmp_expr->max_docid;
|
||||
/* fall through */
|
||||
case FT_TOKEN_STOPWORD:
|
||||
if (! ftb_param->up_quot) break;
|
||||
phrase_word= (FT_WORD *)alloc_root(&ftb_param->ftb->mem_root, sizeof(FT_WORD));
|
||||
tmp_element= (LIST *)alloc_root(&ftb_param->ftb->mem_root, sizeof(LIST));
|
||||
phrase_word->pos= word;
|
||||
phrase_word->len= word_len;
|
||||
tmp_element->data= (void *)phrase_word;
|
||||
ftb_param->ftbe->phrase= list_add(ftb_param->ftbe->phrase, tmp_element);
|
||||
/* Allocate document list at this point.
|
||||
It allows to avoid huge amount of allocs/frees for each row.*/
|
||||
tmp_element= (LIST *)alloc_root(&ftb_param->ftb->mem_root, sizeof(LIST));
|
||||
tmp_element->data= alloc_root(&ftb_param->ftb->mem_root, sizeof(FT_WORD));
|
||||
ftb_param->ftbe->document=
|
||||
list_add(ftb_param->ftbe->document, tmp_element);
|
||||
break;
|
||||
case FT_TOKEN_LEFT_PAREN:
|
||||
ftbe=(FTB_EXPR *)alloc_root(&ftb_param->ftb->mem_root, sizeof(FTB_EXPR));
|
||||
ftbe->flags= 0;
|
||||
if (info->yesno > 0) ftbe->flags|= FTB_FLAG_YES;
|
||||
if (info->yesno < 0) ftbe->flags|= FTB_FLAG_NO;
|
||||
ftbe->weight= weight;
|
||||
ftbe->up= ftb_param->ftbe;
|
||||
ftbe->max_docid= ftbe->ythresh= ftbe->yweaks= 0;
|
||||
ftbe->docid[0]= ftbe->docid[1]= HA_OFFSET_ERROR;
|
||||
ftbe->phrase= NULL;
|
||||
ftbe->document= 0;
|
||||
if (info->quot) ftb_param->ftb->with_scan|= 2;
|
||||
if (info->yesno > 0) ftbe->up->ythresh++;
|
||||
ftb_param->ftbe= ftbe;
|
||||
ftb_param->depth++;
|
||||
ftb_param->up_quot= info->quot;
|
||||
break;
|
||||
case FT_TOKEN_RIGHT_PAREN:
|
||||
if (ftb_param->ftbe->document)
|
||||
{
|
||||
/* Circuit document list */
|
||||
for (tmp_element= ftb_param->ftbe->document;
|
||||
tmp_element->next; tmp_element= tmp_element->next) /* no-op */;
|
||||
tmp_element->next= ftb_param->ftbe->document;
|
||||
ftb_param->ftbe->document->prev= tmp_element;
|
||||
}
|
||||
info->quot= 0;
|
||||
if (ftb_param->ftbe->up)
|
||||
{
|
||||
DBUG_ASSERT(ftb_param->depth);
|
||||
ftb_param->ftbe= ftb_param->ftbe->up;
|
||||
ftb_param->depth--;
|
||||
ftb_param->up_quot= 0;
|
||||
}
|
||||
break;
|
||||
case FT_TOKEN_EOF:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static int ftb_parse_query_internal(MYSQL_FTPARSER_PARAM *param,
|
||||
char *query, int len)
|
||||
{
|
||||
MY_FTB_PARAM *ftb_param= param->mysql_ftparam;
|
||||
MYSQL_FTPARSER_BOOLEAN_INFO info;
|
||||
CHARSET_INFO *cs= ftb_param->ftb->charset;
|
||||
char **start= &query;
|
||||
char *end= query + len;
|
||||
FT_WORD w;
|
||||
|
||||
info.prev= ' ';
|
||||
info.quot= 0;
|
||||
while (maria_ft_get_word(cs, start, end, &w, &info))
|
||||
param->mysql_add_word(param, w.pos, w.len, &info);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static void _ftb_parse_query(FTB *ftb, byte *query, uint len,
|
||||
struct st_mysql_ftparser *parser)
|
||||
{
|
||||
MYSQL_FTPARSER_PARAM *param;
|
||||
MY_FTB_PARAM ftb_param;
|
||||
DBUG_ENTER("_ftb_parse_query");
|
||||
DBUG_ASSERT(parser);
|
||||
|
||||
if (ftb->state != UNINITIALIZED)
|
||||
DBUG_VOID_RETURN;
|
||||
if (! (param= maria_ftparser_call_initializer(ftb->info, ftb->keynr, 0)))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
ftb_param.ftb= ftb;
|
||||
ftb_param.depth= 0;
|
||||
ftb_param.ftbe= ftb->root;
|
||||
ftb_param.up_quot= 0;
|
||||
|
||||
param->mysql_parse= ftb_parse_query_internal;
|
||||
param->mysql_add_word= ftb_query_add_word;
|
||||
param->mysql_ftparam= (void *)&ftb_param;
|
||||
param->cs= ftb->charset;
|
||||
param->doc= query;
|
||||
param->length= len;
|
||||
param->flags= 0;
|
||||
param->mode= MYSQL_FTPARSER_FULL_BOOLEAN_INFO;
|
||||
parser->parse(param);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
static int _ftb_no_dupes_cmp(void* not_used __attribute__((unused)),
|
||||
const void *a,const void *b)
|
||||
{
|
||||
return CMP_NUM((*((my_off_t*)a)), (*((my_off_t*)b)));
|
||||
}
|
||||
|
||||
/* returns 1 if the search was finished (must-word wasn't found) */
|
||||
static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search)
|
||||
{
|
||||
int r;
|
||||
int subkeys=1;
|
||||
my_bool can_go_down;
|
||||
MARIA_HA *info=ftb->info;
|
||||
uint off, extra=HA_FT_WLEN+info->s->base.rec_reflength;
|
||||
byte *lastkey_buf=ftbw->word+ftbw->off;
|
||||
LINT_INIT(off);
|
||||
|
||||
if (ftbw->flags & FTB_FLAG_TRUNC)
|
||||
lastkey_buf+=ftbw->len;
|
||||
|
||||
if (init_search)
|
||||
{
|
||||
ftbw->key_root=info->s->state.key_root[ftb->keynr];
|
||||
ftbw->keyinfo=info->s->keyinfo+ftb->keynr;
|
||||
|
||||
r= _ma_search(info, ftbw->keyinfo, (uchar*) ftbw->word, ftbw->len,
|
||||
SEARCH_FIND | SEARCH_BIGGER, ftbw->key_root);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint sflag= SEARCH_BIGGER;
|
||||
if (ftbw->docid[0] < *ftbw->max_docid)
|
||||
{
|
||||
sflag|= SEARCH_SAME;
|
||||
_ma_dpointer(info, (uchar *)(ftbw->word + ftbw->len + HA_FT_WLEN),
|
||||
*ftbw->max_docid);
|
||||
}
|
||||
r= _ma_search(info, ftbw->keyinfo, (uchar*) lastkey_buf,
|
||||
USE_WHOLE_KEY, sflag, ftbw->key_root);
|
||||
}
|
||||
|
||||
can_go_down=(!ftbw->off && (init_search || (ftbw->flags & FTB_FLAG_TRUNC)));
|
||||
/* Skip rows inserted by concurrent insert */
|
||||
while (!r)
|
||||
{
|
||||
if (can_go_down)
|
||||
{
|
||||
/* going down ? */
|
||||
off=info->lastkey_length-extra;
|
||||
subkeys=ft_sintXkorr(info->lastkey+off);
|
||||
}
|
||||
if (subkeys<0 || info->lastpos < info->state->data_file_length)
|
||||
break;
|
||||
r= _ma_search_next(info, ftbw->keyinfo, info->lastkey,
|
||||
info->lastkey_length,
|
||||
SEARCH_BIGGER, ftbw->key_root);
|
||||
}
|
||||
|
||||
if (!r && !ftbw->off)
|
||||
{
|
||||
r= ha_compare_text(ftb->charset,
|
||||
info->lastkey+1,
|
||||
info->lastkey_length-extra-1,
|
||||
(uchar*) ftbw->word+1,
|
||||
ftbw->len-1,
|
||||
(my_bool) (ftbw->flags & FTB_FLAG_TRUNC),0);
|
||||
}
|
||||
|
||||
if (r) /* not found */
|
||||
{
|
||||
if (!ftbw->off || !(ftbw->flags & FTB_FLAG_TRUNC))
|
||||
{
|
||||
ftbw->docid[0]=HA_OFFSET_ERROR;
|
||||
if ((ftbw->flags & FTB_FLAG_YES) && ftbw->up->up==0)
|
||||
{
|
||||
/*
|
||||
This word MUST BE present in every document returned,
|
||||
so we can stop the search right now
|
||||
*/
|
||||
ftb->state=INDEX_DONE;
|
||||
return 1; /* search is done */
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* going up to the first-level tree to continue search there */
|
||||
_ma_dpointer(info, (uchar*) (lastkey_buf+HA_FT_WLEN), ftbw->key_root);
|
||||
ftbw->key_root=info->s->state.key_root[ftb->keynr];
|
||||
ftbw->keyinfo=info->s->keyinfo+ftb->keynr;
|
||||
ftbw->off=0;
|
||||
return _ft2_search(ftb, ftbw, 0);
|
||||
}
|
||||
|
||||
/* matching key found */
|
||||
memcpy(lastkey_buf, info->lastkey, info->lastkey_length);
|
||||
if (lastkey_buf == ftbw->word)
|
||||
ftbw->len=info->lastkey_length-extra;
|
||||
|
||||
/* going down ? */
|
||||
if (subkeys<0)
|
||||
{
|
||||
/*
|
||||
yep, going down, to the second-level tree
|
||||
TODO here: subkey-based optimization
|
||||
*/
|
||||
ftbw->off=off;
|
||||
ftbw->key_root=info->lastpos;
|
||||
ftbw->keyinfo=& info->s->ft2_keyinfo;
|
||||
r= _ma_search_first(info, ftbw->keyinfo, ftbw->key_root);
|
||||
DBUG_ASSERT(r==0); /* found something */
|
||||
memcpy(lastkey_buf+off, info->lastkey, info->lastkey_length);
|
||||
}
|
||||
ftbw->docid[0]=info->lastpos;
|
||||
if (ftbw->flags & FTB_FLAG_YES)
|
||||
*ftbw->max_docid= info->lastpos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _ftb_init_index_search(FT_INFO *ftb)
|
||||
{
|
||||
int i;
|
||||
FTB_WORD *ftbw;
|
||||
|
||||
if ((ftb->state != READY && ftb->state !=INDEX_DONE) ||
|
||||
ftb->keynr == NO_SUCH_KEY)
|
||||
return;
|
||||
ftb->state=INDEX_SEARCH;
|
||||
|
||||
for (i=ftb->queue.elements; i; i--)
|
||||
{
|
||||
ftbw=(FTB_WORD *)(ftb->queue.root[i]);
|
||||
|
||||
if (ftbw->flags & FTB_FLAG_TRUNC)
|
||||
{
|
||||
/*
|
||||
special treatment for truncation operator
|
||||
1. there are some (besides this) +words
|
||||
| no need to search in the index, it can never ADD new rows
|
||||
| to the result, and to remove half-matched rows we do scan anyway
|
||||
2. -trunc*
|
||||
| same as 1.
|
||||
3. in 1 and 2, +/- need not be on the same expr. level,
|
||||
but can be on any upper level, as in +word +(trunc1* trunc2*)
|
||||
4. otherwise
|
||||
| We have to index-search for this prefix.
|
||||
| It may cause duplicates, as in the index (sorted by <word,docid>)
|
||||
| <aaaa,row1>
|
||||
| <aabb,row2>
|
||||
| <aacc,row1>
|
||||
| Searching for "aa*" will find row1 twice...
|
||||
*/
|
||||
FTB_EXPR *ftbe;
|
||||
for (ftbe=(FTB_EXPR*)ftbw;
|
||||
ftbe->up && !(ftbe->up->flags & FTB_FLAG_TRUNC);
|
||||
ftbe->up->flags|= FTB_FLAG_TRUNC, ftbe=ftbe->up)
|
||||
{
|
||||
if (ftbe->flags & FTB_FLAG_NO || /* 2 */
|
||||
ftbe->up->ythresh - ftbe->up->yweaks >1) /* 1 */
|
||||
{
|
||||
FTB_EXPR *top_ftbe=ftbe->up;
|
||||
ftbw->docid[0]=HA_OFFSET_ERROR;
|
||||
for (ftbe=(FTB_EXPR *)ftbw;
|
||||
ftbe != top_ftbe && !(ftbe->flags & FTB_FLAG_NO);
|
||||
ftbe=ftbe->up)
|
||||
ftbe->up->yweaks++;
|
||||
ftbe=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ftbe)
|
||||
continue;
|
||||
/* 4 */
|
||||
if (!is_tree_inited(& ftb->no_dupes))
|
||||
init_tree(& ftb->no_dupes,0,0,sizeof(my_off_t),
|
||||
_ftb_no_dupes_cmp,0,0,0);
|
||||
else
|
||||
reset_tree(& ftb->no_dupes);
|
||||
}
|
||||
|
||||
ftbw->off=0; /* in case of reinit */
|
||||
if (_ft2_search(ftb, ftbw, 1))
|
||||
return;
|
||||
}
|
||||
queue_fix(& ftb->queue);
|
||||
}
|
||||
|
||||
|
||||
FT_INFO * maria_ft_init_boolean_search(MARIA_HA *info, uint keynr, byte *query,
|
||||
uint query_len, CHARSET_INFO *cs)
|
||||
{
|
||||
FTB *ftb;
|
||||
FTB_EXPR *ftbe;
|
||||
FTB_WORD *ftbw;
|
||||
|
||||
if (!(ftb=(FTB *)my_malloc(sizeof(FTB), MYF(MY_WME))))
|
||||
return 0;
|
||||
ftb->please= (struct _ft_vft *) & _ma_ft_vft_boolean;
|
||||
ftb->state=UNINITIALIZED;
|
||||
ftb->info=info;
|
||||
ftb->keynr=keynr;
|
||||
ftb->charset=cs;
|
||||
DBUG_ASSERT(keynr==NO_SUCH_KEY || cs == info->s->keyinfo[keynr].seg->charset);
|
||||
ftb->with_scan=0;
|
||||
ftb->lastpos=HA_OFFSET_ERROR;
|
||||
bzero(& ftb->no_dupes, sizeof(TREE));
|
||||
ftb->last_word= 0;
|
||||
|
||||
init_alloc_root(&ftb->mem_root, 1024, 1024);
|
||||
ftb->queue.max_elements= 0;
|
||||
if (!(ftbe=(FTB_EXPR *)alloc_root(&ftb->mem_root, sizeof(FTB_EXPR))))
|
||||
goto err;
|
||||
ftbe->weight=1;
|
||||
ftbe->flags=FTB_FLAG_YES;
|
||||
ftbe->nos=1;
|
||||
ftbe->up=0;
|
||||
ftbe->max_docid= ftbe->ythresh= ftbe->yweaks= 0;
|
||||
ftbe->docid[0]=ftbe->docid[1]=HA_OFFSET_ERROR;
|
||||
ftbe->phrase= NULL;
|
||||
ftbe->document= 0;
|
||||
ftb->root=ftbe;
|
||||
_ftb_parse_query(ftb, query, query_len, keynr == NO_SUCH_KEY ?
|
||||
&ft_default_parser :
|
||||
info->s->keyinfo[keynr].parser);
|
||||
/*
|
||||
Hack: instead of init_queue, we'll use reinit queue to be able
|
||||
to alloc queue with alloc_root()
|
||||
*/
|
||||
if (! (ftb->queue.root= (byte **)alloc_root(&ftb->mem_root,
|
||||
(ftb->queue.max_elements + 1) *
|
||||
sizeof(void *))))
|
||||
goto err;
|
||||
reinit_queue(&ftb->queue, ftb->queue.max_elements, 0, 0,
|
||||
(int (*)(void*, byte*, byte*))FTB_WORD_cmp, 0);
|
||||
for (ftbw= ftb->last_word; ftbw; ftbw= ftbw->prev)
|
||||
queue_insert(&ftb->queue, (byte *)ftbw);
|
||||
ftb->list=(FTB_WORD **)alloc_root(&ftb->mem_root,
|
||||
sizeof(FTB_WORD *)*ftb->queue.elements);
|
||||
memcpy(ftb->list, ftb->queue.root+1, sizeof(FTB_WORD *)*ftb->queue.elements);
|
||||
qsort2(ftb->list, ftb->queue.elements, sizeof(FTB_WORD *),
|
||||
(qsort2_cmp)FTB_WORD_cmp_list, ftb->charset);
|
||||
if (ftb->queue.elements<2) ftb->with_scan &= ~FTB_FLAG_TRUNC;
|
||||
ftb->state=READY;
|
||||
return ftb;
|
||||
err:
|
||||
free_root(& ftb->mem_root, MYF(0));
|
||||
my_free((gptr)ftb,MYF(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct st_my_ftb_phrase_param
|
||||
{
|
||||
LIST *phrase;
|
||||
LIST *document;
|
||||
CHARSET_INFO *cs;
|
||||
uint phrase_length;
|
||||
uint document_length;
|
||||
uint match;
|
||||
} MY_FTB_PHRASE_PARAM;
|
||||
|
||||
|
||||
static int ftb_phrase_add_word(MYSQL_FTPARSER_PARAM *param,
|
||||
char *word, int word_len,
|
||||
MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info __attribute__((unused)))
|
||||
{
|
||||
MY_FTB_PHRASE_PARAM *phrase_param= param->mysql_ftparam;
|
||||
FT_WORD *w= (FT_WORD *)phrase_param->document->data;
|
||||
LIST *phrase, *document;
|
||||
w->pos= word;
|
||||
w->len= word_len;
|
||||
phrase_param->document= phrase_param->document->prev;
|
||||
if (phrase_param->phrase_length > phrase_param->document_length)
|
||||
{
|
||||
phrase_param->document_length++;
|
||||
return 0;
|
||||
}
|
||||
/* TODO: rewrite phrase search to avoid
|
||||
comparing the same word twice. */
|
||||
for (phrase= phrase_param->phrase, document= phrase_param->document->next;
|
||||
phrase; phrase= phrase->next, document= document->next)
|
||||
{
|
||||
FT_WORD *phrase_word= (FT_WORD *)phrase->data;
|
||||
FT_WORD *document_word= (FT_WORD *)document->data;
|
||||
if (my_strnncoll(phrase_param->cs,
|
||||
(uchar*) phrase_word->pos, phrase_word->len,
|
||||
(uchar*) document_word->pos, document_word->len))
|
||||
return 0;
|
||||
}
|
||||
phrase_param->match++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ftb_check_phrase_internal(MYSQL_FTPARSER_PARAM *param,
|
||||
char *document, int len)
|
||||
{
|
||||
FT_WORD word;
|
||||
MY_FTB_PHRASE_PARAM *phrase_param= param->mysql_ftparam;
|
||||
const char *docend= document + len;
|
||||
while (maria_ft_simple_get_word(phrase_param->cs, &document, docend, &word, FALSE))
|
||||
{
|
||||
param->mysql_add_word(param, word.pos, word.len, 0);
|
||||
if (phrase_param->match)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Checks if given buffer matches phrase list.
|
||||
|
||||
SYNOPSIS
|
||||
_ftb_check_phrase()
|
||||
s0 start of buffer
|
||||
e0 end of buffer
|
||||
phrase broken into list phrase
|
||||
cs charset info
|
||||
|
||||
RETURN VALUE
|
||||
1 is returned if phrase found, 0 else.
|
||||
*/
|
||||
|
||||
static int _ftb_check_phrase(FTB *ftb, const byte *document, uint len,
|
||||
FTB_EXPR *ftbe, struct st_mysql_ftparser *parser)
|
||||
{
|
||||
MY_FTB_PHRASE_PARAM ftb_param;
|
||||
MYSQL_FTPARSER_PARAM *param;
|
||||
DBUG_ENTER("_ftb_check_phrase");
|
||||
DBUG_ASSERT(parser);
|
||||
|
||||
if (! (param= maria_ftparser_call_initializer(ftb->info, ftb->keynr, 1)))
|
||||
DBUG_RETURN(0);
|
||||
ftb_param.phrase= ftbe->phrase;
|
||||
ftb_param.document= ftbe->document;
|
||||
ftb_param.cs= ftb->charset;
|
||||
ftb_param.phrase_length= list_length(ftbe->phrase);
|
||||
ftb_param.document_length= 1;
|
||||
ftb_param.match= 0;
|
||||
|
||||
param->mysql_parse= ftb_check_phrase_internal;
|
||||
param->mysql_add_word= ftb_phrase_add_word;
|
||||
param->mysql_ftparam= (void *)&ftb_param;
|
||||
param->cs= ftb->charset;
|
||||
param->doc= (byte *)document;
|
||||
param->length= len;
|
||||
param->flags= 0;
|
||||
param->mode= MYSQL_FTPARSER_WITH_STOPWORDS;
|
||||
parser->parse(param);
|
||||
DBUG_RETURN(ftb_param.match ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
static void _ftb_climb_the_tree(FTB *ftb, FTB_WORD *ftbw, FT_SEG_ITERATOR *ftsi_orig)
|
||||
{
|
||||
FT_SEG_ITERATOR ftsi;
|
||||
FTB_EXPR *ftbe;
|
||||
float weight=ftbw->weight;
|
||||
int yn=ftbw->flags, ythresh, mode=(ftsi_orig != 0);
|
||||
my_off_t curdoc=ftbw->docid[mode];
|
||||
struct st_mysql_ftparser *parser= ftb->keynr == NO_SUCH_KEY ?
|
||||
&ft_default_parser :
|
||||
ftb->info->s->keyinfo[ftb->keynr].parser;
|
||||
|
||||
for (ftbe=ftbw->up; ftbe; ftbe=ftbe->up)
|
||||
{
|
||||
ythresh = ftbe->ythresh - (mode ? 0 : ftbe->yweaks);
|
||||
if (ftbe->docid[mode] != curdoc)
|
||||
{
|
||||
ftbe->cur_weight=0;
|
||||
ftbe->yesses=ftbe->nos=0;
|
||||
ftbe->docid[mode]=curdoc;
|
||||
}
|
||||
if (ftbe->nos)
|
||||
break;
|
||||
if (yn & FTB_FLAG_YES)
|
||||
{
|
||||
weight /= ftbe->ythresh;
|
||||
ftbe->cur_weight += weight;
|
||||
if ((int) ++ftbe->yesses == ythresh)
|
||||
{
|
||||
yn=ftbe->flags;
|
||||
weight=ftbe->cur_weight*ftbe->weight;
|
||||
if (mode && ftbe->phrase)
|
||||
{
|
||||
int not_found=1;
|
||||
|
||||
memcpy(&ftsi, ftsi_orig, sizeof(ftsi));
|
||||
while (_ma_ft_segiterator(&ftsi) && not_found)
|
||||
{
|
||||
if (!ftsi.pos)
|
||||
continue;
|
||||
not_found = ! _ftb_check_phrase(ftb, ftsi.pos, ftsi.len,
|
||||
ftbe, parser);
|
||||
}
|
||||
if (not_found) break;
|
||||
} /* ftbe->quot */
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (yn & FTB_FLAG_NO)
|
||||
{
|
||||
/*
|
||||
NOTE: special sort function of queue assures that all
|
||||
(yn & FTB_FLAG_NO) != 0
|
||||
events for every particular subexpression will
|
||||
"auto-magically" happen BEFORE all the
|
||||
(yn & FTB_FLAG_YES) != 0 events. So no
|
||||
already matched expression can become not-matched again.
|
||||
*/
|
||||
++ftbe->nos;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ftbe->ythresh)
|
||||
weight/=3;
|
||||
ftbe->cur_weight += weight;
|
||||
if ((int) ftbe->yesses < ythresh)
|
||||
break;
|
||||
if (!(yn & FTB_FLAG_WONLY))
|
||||
yn= ((int) ftbe->yesses++ == ythresh) ? ftbe->flags : FTB_FLAG_WONLY ;
|
||||
weight*= ftbe->weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int maria_ft_boolean_read_next(FT_INFO *ftb, char *record)
|
||||
{
|
||||
FTB_EXPR *ftbe;
|
||||
FTB_WORD *ftbw;
|
||||
MARIA_HA *info=ftb->info;
|
||||
my_off_t curdoc;
|
||||
|
||||
if (ftb->state != INDEX_SEARCH && ftb->state != INDEX_DONE)
|
||||
return -1;
|
||||
|
||||
/* black magic ON */
|
||||
if ((int) _ma_check_index(info, ftb->keynr) < 0)
|
||||
return my_errno;
|
||||
if (_ma_readinfo(info, F_RDLCK, 1))
|
||||
return my_errno;
|
||||
/* black magic OFF */
|
||||
|
||||
if (!ftb->queue.elements)
|
||||
return my_errno=HA_ERR_END_OF_FILE;
|
||||
|
||||
/* Attention!!! Address of a local variable is used here! See err: label */
|
||||
ftb->queue.first_cmp_arg=(void *)&curdoc;
|
||||
|
||||
while (ftb->state == INDEX_SEARCH &&
|
||||
(curdoc=((FTB_WORD *)queue_top(& ftb->queue))->docid[0]) !=
|
||||
HA_OFFSET_ERROR)
|
||||
{
|
||||
while (curdoc == (ftbw=(FTB_WORD *)queue_top(& ftb->queue))->docid[0])
|
||||
{
|
||||
_ftb_climb_the_tree(ftb, ftbw, 0);
|
||||
|
||||
/* update queue */
|
||||
_ft2_search(ftb, ftbw, 0);
|
||||
queue_replaced(& ftb->queue);
|
||||
}
|
||||
|
||||
ftbe=ftb->root;
|
||||
if (ftbe->docid[0]==curdoc && ftbe->cur_weight>0 &&
|
||||
ftbe->yesses>=(ftbe->ythresh-ftbe->yweaks) && !ftbe->nos)
|
||||
{
|
||||
/* curdoc matched ! */
|
||||
if (is_tree_inited(&ftb->no_dupes) &&
|
||||
tree_insert(&ftb->no_dupes, &curdoc, 0,
|
||||
ftb->no_dupes.custom_arg)->count >1)
|
||||
/* but it managed already to get past this line once */
|
||||
continue;
|
||||
|
||||
info->lastpos=curdoc;
|
||||
/* Clear all states, except that the table was updated */
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
|
||||
if (!(*info->read_record)(info,curdoc,record))
|
||||
{
|
||||
info->update|= HA_STATE_AKTIV; /* Record is read */
|
||||
if (ftb->with_scan && maria_ft_boolean_find_relevance(ftb,record,0)==0)
|
||||
continue; /* no match */
|
||||
my_errno=0;
|
||||
goto err;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
ftb->state=INDEX_DONE;
|
||||
my_errno=HA_ERR_END_OF_FILE;
|
||||
err:
|
||||
ftb->queue.first_cmp_arg=(void *)0;
|
||||
return my_errno;
|
||||
}
|
||||
|
||||
|
||||
typedef struct st_my_ftb_find_param
|
||||
{
|
||||
FT_INFO *ftb;
|
||||
FT_SEG_ITERATOR *ftsi;
|
||||
} MY_FTB_FIND_PARAM;
|
||||
|
||||
|
||||
static int ftb_find_relevance_add_word(MYSQL_FTPARSER_PARAM *param,
|
||||
char *word, int len,
|
||||
MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info __attribute__((unused)))
|
||||
{
|
||||
MY_FTB_FIND_PARAM *ftb_param= param->mysql_ftparam;
|
||||
FT_INFO *ftb= ftb_param->ftb;
|
||||
FTB_WORD *ftbw;
|
||||
int a, b, c;
|
||||
for (a= 0, b= ftb->queue.elements, c= (a+b)/2; b-a>1; c= (a+b)/2)
|
||||
{
|
||||
ftbw= ftb->list[c];
|
||||
if (ha_compare_text(ftb->charset, (uchar*)word, len,
|
||||
(uchar*)ftbw->word+1, ftbw->len-1,
|
||||
(my_bool)(ftbw->flags&FTB_FLAG_TRUNC), 0) > 0)
|
||||
b= c;
|
||||
else
|
||||
a= c;
|
||||
}
|
||||
for (; c >= 0; c--)
|
||||
{
|
||||
ftbw= ftb->list[c];
|
||||
if (ha_compare_text(ftb->charset, (uchar*)word, len,
|
||||
(uchar*)ftbw->word + 1,ftbw->len - 1,
|
||||
(my_bool)(ftbw->flags & FTB_FLAG_TRUNC), 0))
|
||||
break;
|
||||
if (ftbw->docid[1] == ftb->info->lastpos)
|
||||
continue;
|
||||
ftbw->docid[1]= ftb->info->lastpos;
|
||||
_ftb_climb_the_tree(ftb, ftbw, ftb_param->ftsi);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static int ftb_find_relevance_parse(MYSQL_FTPARSER_PARAM *param,
|
||||
char *doc, int len)
|
||||
{
|
||||
MY_FTB_FIND_PARAM *ftb_param= param->mysql_ftparam;
|
||||
FT_INFO *ftb= ftb_param->ftb;
|
||||
char *end= doc + len;
|
||||
FT_WORD w;
|
||||
while (maria_ft_simple_get_word(ftb->charset, &doc, end, &w, TRUE))
|
||||
param->mysql_add_word(param, w.pos, w.len, 0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
float maria_ft_boolean_find_relevance(FT_INFO *ftb, byte *record, uint length)
|
||||
{
|
||||
FTB_EXPR *ftbe;
|
||||
FT_SEG_ITERATOR ftsi, ftsi2;
|
||||
my_off_t docid=ftb->info->lastpos;
|
||||
MY_FTB_FIND_PARAM ftb_param;
|
||||
MYSQL_FTPARSER_PARAM *param;
|
||||
struct st_mysql_ftparser *parser= ftb->keynr == NO_SUCH_KEY ?
|
||||
&ft_default_parser :
|
||||
ftb->info->s->keyinfo[ftb->keynr].parser;
|
||||
|
||||
if (docid == HA_OFFSET_ERROR)
|
||||
return -2.0;
|
||||
if (!ftb->queue.elements)
|
||||
return 0;
|
||||
if (! (param= maria_ftparser_call_initializer(ftb->info, ftb->keynr, 0)))
|
||||
return 0;
|
||||
|
||||
if (ftb->state != INDEX_SEARCH && docid <= ftb->lastpos)
|
||||
{
|
||||
FTB_EXPR *x;
|
||||
uint i;
|
||||
|
||||
for (i=0; i < ftb->queue.elements; i++)
|
||||
{
|
||||
ftb->list[i]->docid[1]=HA_OFFSET_ERROR;
|
||||
for (x=ftb->list[i]->up; x; x=x->up)
|
||||
x->docid[1]=HA_OFFSET_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ftb->lastpos=docid;
|
||||
|
||||
if (ftb->keynr==NO_SUCH_KEY)
|
||||
_ma_ft_segiterator_dummy_init(record, length, &ftsi);
|
||||
else
|
||||
_ma_ft_segiterator_init(ftb->info, ftb->keynr, record, &ftsi);
|
||||
memcpy(&ftsi2, &ftsi, sizeof(ftsi));
|
||||
|
||||
ftb_param.ftb= ftb;
|
||||
ftb_param.ftsi= &ftsi2;
|
||||
param->mysql_parse= ftb_find_relevance_parse;
|
||||
param->mysql_add_word= ftb_find_relevance_add_word;
|
||||
param->mysql_ftparam= (void *)&ftb_param;
|
||||
param->flags= 0;
|
||||
param->cs= ftb->charset;
|
||||
param->mode= MYSQL_FTPARSER_SIMPLE_MODE;
|
||||
|
||||
while (_ma_ft_segiterator(&ftsi))
|
||||
{
|
||||
if (!ftsi.pos)
|
||||
continue;
|
||||
param->doc= (byte *)ftsi.pos;
|
||||
param->length= ftsi.len;
|
||||
parser->parse(param);
|
||||
}
|
||||
ftbe=ftb->root;
|
||||
if (ftbe->docid[1]==docid && ftbe->cur_weight>0 &&
|
||||
ftbe->yesses>=ftbe->ythresh && !ftbe->nos)
|
||||
{ /* row matched ! */
|
||||
return ftbe->cur_weight;
|
||||
}
|
||||
else
|
||||
{ /* match failed ! */
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void maria_ft_boolean_close_search(FT_INFO *ftb)
|
||||
{
|
||||
if (is_tree_inited(& ftb->no_dupes))
|
||||
{
|
||||
delete_tree(& ftb->no_dupes);
|
||||
}
|
||||
free_root(& ftb->mem_root, MYF(0));
|
||||
my_free((gptr)ftb,MYF(0));
|
||||
}
|
||||
|
||||
|
||||
float maria_ft_boolean_get_relevance(FT_INFO *ftb)
|
||||
{
|
||||
return ftb->root->cur_weight;
|
||||
}
|
||||
|
||||
|
||||
void maria_ft_boolean_reinit_search(FT_INFO *ftb)
|
||||
{
|
||||
_ftb_init_index_search(ftb);
|
||||
}
|
||||
255
storage/maria/ma_ft_eval.c
Normal file
255
storage/maria/ma_ft_eval.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code
|
||||
added support for long options (my_getopt) 22.5.2002 by Jani Tolonen */
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
#include "maria_ft_eval.h"
|
||||
#include <stdarg.h>
|
||||
#include <my_getopt.h>
|
||||
|
||||
static void print_error(int exit_code, const char *fmt,...);
|
||||
static void get_options(int argc, char *argv[]);
|
||||
static int create_record(char *pos, FILE *file);
|
||||
static void usage();
|
||||
|
||||
static struct my_option my_long_options[] =
|
||||
{
|
||||
{"", 's', "", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'q', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'S', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", '#', "", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'V', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", '?', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'h', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MARIA_HA *file;
|
||||
int i,j;
|
||||
|
||||
MY_INIT(argv[0]);
|
||||
get_options(argc,argv);
|
||||
bzero((char*)recinfo,sizeof(recinfo));
|
||||
|
||||
maria_init();
|
||||
/* First define 2 columns */
|
||||
recinfo[0].type=FIELD_SKIP_ENDSPACE;
|
||||
recinfo[0].length=docid_length;
|
||||
recinfo[1].type=FIELD_BLOB;
|
||||
recinfo[1].length= 4+maria_portable_sizeof_char_ptr;
|
||||
|
||||
/* Define a key over the first column */
|
||||
keyinfo[0].seg=keyseg;
|
||||
keyinfo[0].keysegs=1;
|
||||
keyinfo[0].block_length= 0; /* Default block length */
|
||||
keyinfo[0].seg[0].type= HA_KEYTYPE_TEXT;
|
||||
keyinfo[0].seg[0].flag= HA_BLOB_PART;
|
||||
keyinfo[0].seg[0].start=recinfo[0].length;
|
||||
keyinfo[0].seg[0].length=key_length;
|
||||
keyinfo[0].seg[0].null_bit=0;
|
||||
keyinfo[0].seg[0].null_pos=0;
|
||||
keyinfo[0].seg[0].bit_start=4;
|
||||
keyinfo[0].seg[0].language=MY_CHARSET_CURRENT;
|
||||
keyinfo[0].flag = HA_FULLTEXT;
|
||||
|
||||
if (!silent)
|
||||
printf("- Creating isam-file\n");
|
||||
if (maria_create(filename,1,keyinfo,2,recinfo,0,NULL,(MARIA_CREATE_INFO*) 0,0))
|
||||
goto err;
|
||||
if (!(file=maria_open(filename,2,0)))
|
||||
goto err;
|
||||
if (!silent)
|
||||
printf("Initializing stopwords\n");
|
||||
maria_ft_init_stopwords(stopwordlist);
|
||||
|
||||
if (!silent)
|
||||
printf("- Writing key:s\n");
|
||||
|
||||
my_errno=0;
|
||||
i=0;
|
||||
while (create_record(record,df))
|
||||
{
|
||||
error=maria_write(file,record);
|
||||
if (error)
|
||||
printf("I= %2d maria_write: %d errno: %d\n",i,error,my_errno);
|
||||
i++;
|
||||
}
|
||||
fclose(df);
|
||||
|
||||
if (maria_close(file)) goto err;
|
||||
if (!silent)
|
||||
printf("- Reopening file\n");
|
||||
if (!(file=maria_open(filename,2,0))) goto err;
|
||||
if (!silent)
|
||||
printf("- Reading rows with key\n");
|
||||
for (i=1;create_record(record,qf);i++)
|
||||
{
|
||||
FT_DOCLIST *result;
|
||||
double w;
|
||||
int t, err;
|
||||
|
||||
result=maria_ft_nlq_init_search(file,0,blob_record,(uint) strlen(blob_record),1);
|
||||
if (!result)
|
||||
{
|
||||
printf("Query %d failed with errno %3d\n",i,my_errno);
|
||||
goto err;
|
||||
}
|
||||
if (!silent)
|
||||
printf("Query %d. Found: %d.\n",i,result->ndocs);
|
||||
for (j=0;(err=maria_ft_nlq_read_next(result, read_record))==0;j++)
|
||||
{
|
||||
t=uint2korr(read_record);
|
||||
w=maria_ft_nlq_get_relevance(result);
|
||||
printf("%d %.*s %f\n",i,t,read_record+2,w);
|
||||
}
|
||||
if (err != HA_ERR_END_OF_FILE)
|
||||
{
|
||||
printf("maria_ft_read_next %d failed with errno %3d\n",j,my_errno);
|
||||
goto err;
|
||||
}
|
||||
maria_ft_nlq_close_search(result);
|
||||
}
|
||||
|
||||
if (maria_close(file)) goto err;
|
||||
maria_end();
|
||||
my_end(MY_CHECK_ERROR);
|
||||
|
||||
return (0);
|
||||
|
||||
err:
|
||||
printf("got error: %3d when using maria-database\n",my_errno);
|
||||
return 1; /* skip warning */
|
||||
|
||||
}
|
||||
|
||||
|
||||
static my_bool
|
||||
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
||||
char *argument)
|
||||
{
|
||||
switch (optid) {
|
||||
case 's':
|
||||
if (stopwordlist && stopwordlist != maria_ft_precompiled_stopwords)
|
||||
break;
|
||||
{
|
||||
FILE *f; char s[HA_FT_MAXLEN]; int i=0,n=SWL_INIT;
|
||||
|
||||
if (!(stopwordlist=(const char**) malloc(n*sizeof(char *))))
|
||||
print_error(1,"malloc(%d)",n*sizeof(char *));
|
||||
if (!(f=fopen(argument,"r")))
|
||||
print_error(1,"fopen(%s)",argument);
|
||||
while (!feof(f))
|
||||
{
|
||||
if (!(fgets(s,HA_FT_MAXLEN,f)))
|
||||
print_error(1,"fgets(s,%d,%s)",HA_FT_MAXLEN,argument);
|
||||
if (!(stopwordlist[i++]=strdup(s)))
|
||||
print_error(1,"strdup(%s)",s);
|
||||
if (i >= n)
|
||||
{
|
||||
n+=SWL_PLUS;
|
||||
if (!(stopwordlist=(const char**) realloc((char*) stopwordlist,
|
||||
n*sizeof(char *))))
|
||||
print_error(1,"realloc(%d)",n*sizeof(char *));
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
stopwordlist[i]=NULL;
|
||||
break;
|
||||
}
|
||||
case 'q': silent=1; break;
|
||||
case 'S': if (stopwordlist==maria_ft_precompiled_stopwords) stopwordlist=NULL; break;
|
||||
case '#':
|
||||
DBUG_PUSH (argument);
|
||||
break;
|
||||
case 'V':
|
||||
case '?':
|
||||
case 'h':
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void get_options(int argc, char *argv[])
|
||||
{
|
||||
int ho_error;
|
||||
|
||||
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
|
||||
exit(ho_error);
|
||||
|
||||
if (!(d_file=argv[optind])) print_error(1,"No d_file");
|
||||
if (!(df=fopen(d_file,"r")))
|
||||
print_error(1,"fopen(%s)",d_file);
|
||||
if (!(q_file=argv[optind+1])) print_error(1,"No q_file");
|
||||
if (!(qf=fopen(q_file,"r")))
|
||||
print_error(1,"fopen(%s)",q_file);
|
||||
return;
|
||||
} /* get options */
|
||||
|
||||
|
||||
static int create_record(char *pos, FILE *file)
|
||||
{
|
||||
uint tmp; char *ptr;
|
||||
|
||||
bzero((char *)pos,MAX_REC_LENGTH);
|
||||
|
||||
/* column 1 - VARCHAR */
|
||||
if (!(fgets(pos+2,MAX_REC_LENGTH-32,file)))
|
||||
{
|
||||
if (feof(file))
|
||||
return 0;
|
||||
else
|
||||
print_error(1,"fgets(docid) - 1");
|
||||
}
|
||||
tmp=(uint) strlen(pos+2)-1;
|
||||
int2store(pos,tmp);
|
||||
pos+=recinfo[0].length;
|
||||
|
||||
/* column 2 - BLOB */
|
||||
|
||||
if (!(fgets(blob_record,MAX_BLOB_LENGTH,file)))
|
||||
print_error(1,"fgets(docid) - 2");
|
||||
tmp=(uint) strlen(blob_record);
|
||||
int4store(pos,tmp);
|
||||
ptr=blob_record;
|
||||
memcpy_fixed(pos+4,&ptr,sizeof(char*));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* VARARGS */
|
||||
|
||||
static void print_error(int exit_code, const char *fmt,...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args,fmt);
|
||||
fprintf(stderr,"%s: error: ",my_progname);
|
||||
VOID(vfprintf(stderr, fmt, args));
|
||||
VOID(fputc('\n',stderr));
|
||||
fflush(stderr);
|
||||
va_end(args);
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
|
||||
static void usage()
|
||||
{
|
||||
printf("%s [options]\n", my_progname);
|
||||
my_print_help(my_long_options);
|
||||
my_print_variables(my_long_options);
|
||||
}
|
||||
42
storage/maria/ma_ft_eval.h
Normal file
42
storage/maria/ma_ft_eval.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Copyright (C) 2006 MySQL AB & Sergei A. Golubchik
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
const char **stopwordlist=maria_ft_precompiled_stopwords;
|
||||
|
||||
#define MAX_REC_LENGTH 128
|
||||
#define MAX_BLOB_LENGTH 60000
|
||||
char record[MAX_REC_LENGTH], read_record[MAX_REC_LENGTH+MAX_BLOB_LENGTH];
|
||||
char blob_record[MAX_BLOB_LENGTH+20*20];
|
||||
|
||||
char *filename= (char*) "EVAL";
|
||||
|
||||
int silent=0, error=0;
|
||||
|
||||
uint key_length=MAX_BLOB_LENGTH,docid_length=32;
|
||||
char *d_file, *q_file;
|
||||
FILE *df,*qf;
|
||||
|
||||
MARIA_COLUMNDEF recinfo[3];
|
||||
MARIA_KEYDEF keyinfo[2];
|
||||
HA_KEYSEG keyseg[10];
|
||||
|
||||
#define SWL_INIT 500
|
||||
#define SWL_PLUS 50
|
||||
|
||||
#define MAX_LINE_LENGTH 128
|
||||
char line[MAX_LINE_LENGTH];
|
||||
370
storage/maria/ma_ft_nlq_search.c
Normal file
370
storage/maria/ma_ft_nlq_search.c
Normal file
@@ -0,0 +1,370 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
#define FT_CORE
|
||||
#include "ma_ftdefs.h"
|
||||
|
||||
/* search with natural language queries */
|
||||
|
||||
typedef struct ft_doc_rec
|
||||
{
|
||||
my_off_t dpos;
|
||||
double weight;
|
||||
} FT_DOC;
|
||||
|
||||
struct st_ft_info
|
||||
{
|
||||
struct _ft_vft *please;
|
||||
MARIA_HA *info;
|
||||
int ndocs;
|
||||
int curdoc;
|
||||
FT_DOC doc[1];
|
||||
};
|
||||
|
||||
typedef struct st_all_in_one
|
||||
{
|
||||
MARIA_HA *info;
|
||||
uint keynr;
|
||||
CHARSET_INFO *charset;
|
||||
uchar *keybuff;
|
||||
TREE dtree;
|
||||
} ALL_IN_ONE;
|
||||
|
||||
typedef struct st_ft_superdoc
|
||||
{
|
||||
FT_DOC doc;
|
||||
FT_WORD *word_ptr;
|
||||
double tmp_weight;
|
||||
} FT_SUPERDOC;
|
||||
|
||||
static int FT_SUPERDOC_cmp(void* cmp_arg __attribute__((unused)),
|
||||
FT_SUPERDOC *p1, FT_SUPERDOC *p2)
|
||||
{
|
||||
if (p1->doc.dpos < p2->doc.dpos)
|
||||
return -1;
|
||||
if (p1->doc.dpos == p2->doc.dpos)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int walk_and_match(FT_WORD *word, uint32 count, ALL_IN_ONE *aio)
|
||||
{
|
||||
int subkeys, r;
|
||||
uint keylen, doc_cnt;
|
||||
FT_SUPERDOC sdoc, *sptr;
|
||||
TREE_ELEMENT *selem;
|
||||
double gweight=1;
|
||||
MARIA_HA *info=aio->info;
|
||||
uchar *keybuff=aio->keybuff;
|
||||
MARIA_KEYDEF *keyinfo=info->s->keyinfo+aio->keynr;
|
||||
my_off_t key_root=info->s->state.key_root[aio->keynr];
|
||||
uint extra=HA_FT_WLEN+info->s->base.rec_reflength;
|
||||
#if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
|
||||
float tmp_weight;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
|
||||
DBUG_ENTER("walk_and_match");
|
||||
|
||||
word->weight=LWS_FOR_QUERY;
|
||||
|
||||
keylen= _ma_ft_make_key(info,aio->keynr,(char*) keybuff,word,0);
|
||||
keylen-=HA_FT_WLEN;
|
||||
doc_cnt=0;
|
||||
|
||||
/* Skip rows inserted by current inserted */
|
||||
for (r= _ma_search(info, keyinfo, keybuff, keylen, SEARCH_FIND, key_root) ;
|
||||
!r &&
|
||||
(subkeys=ft_sintXkorr(info->lastkey+info->lastkey_length-extra)) > 0 &&
|
||||
info->lastpos >= info->state->data_file_length ;
|
||||
r= _ma_search_next(info, keyinfo, info->lastkey,
|
||||
info->lastkey_length, SEARCH_BIGGER, key_root))
|
||||
;
|
||||
|
||||
info->update|= HA_STATE_AKTIV; /* for _ma_test_if_changed() */
|
||||
|
||||
/* The following should be safe, even if we compare doubles */
|
||||
while (!r && gweight)
|
||||
{
|
||||
|
||||
if (keylen &&
|
||||
ha_compare_text(aio->charset,info->lastkey+1,
|
||||
info->lastkey_length-extra-1, keybuff+1,keylen-1,0,0))
|
||||
break;
|
||||
|
||||
if (subkeys<0)
|
||||
{
|
||||
if (doc_cnt)
|
||||
DBUG_RETURN(1); /* index is corrupted */
|
||||
/*
|
||||
TODO here: unsafe optimization, should this word
|
||||
be skipped (based on subkeys) ?
|
||||
*/
|
||||
keybuff+=keylen;
|
||||
keyinfo=& info->s->ft2_keyinfo;
|
||||
key_root=info->lastpos;
|
||||
keylen=0;
|
||||
r= _ma_search_first(info, keyinfo, key_root);
|
||||
goto do_skip;
|
||||
}
|
||||
#if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
|
||||
tmp_weight=*(float*)&subkeys;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
/* The following should be safe, even if we compare doubles */
|
||||
if (tmp_weight==0)
|
||||
DBUG_RETURN(doc_cnt); /* stopword, doc_cnt should be 0 */
|
||||
|
||||
sdoc.doc.dpos=info->lastpos;
|
||||
|
||||
/* saving document matched into dtree */
|
||||
if (!(selem=tree_insert(&aio->dtree, &sdoc, 0, aio->dtree.custom_arg)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
sptr=(FT_SUPERDOC *)ELEMENT_KEY((&aio->dtree), selem);
|
||||
|
||||
if (selem->count==1) /* document's first match */
|
||||
sptr->doc.weight=0;
|
||||
else
|
||||
sptr->doc.weight+=sptr->tmp_weight*sptr->word_ptr->weight;
|
||||
|
||||
sptr->word_ptr=word;
|
||||
sptr->tmp_weight=tmp_weight;
|
||||
|
||||
doc_cnt++;
|
||||
|
||||
gweight=word->weight*GWS_IN_USE;
|
||||
if (gweight < 0 || doc_cnt > 2000000)
|
||||
gweight=0;
|
||||
|
||||
if (_ma_test_if_changed(info) == 0)
|
||||
r= _ma_search_next(info, keyinfo, info->lastkey, info->lastkey_length,
|
||||
SEARCH_BIGGER, key_root);
|
||||
else
|
||||
r= _ma_search(info, keyinfo, info->lastkey, info->lastkey_length,
|
||||
SEARCH_BIGGER, key_root);
|
||||
do_skip:
|
||||
while ((subkeys=ft_sintXkorr(info->lastkey+info->lastkey_length-extra)) > 0 &&
|
||||
!r && info->lastpos >= info->state->data_file_length)
|
||||
r= _ma_search_next(info, keyinfo, info->lastkey, info->lastkey_length,
|
||||
SEARCH_BIGGER, key_root);
|
||||
|
||||
}
|
||||
word->weight=gweight;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
static int walk_and_copy(FT_SUPERDOC *from,
|
||||
uint32 count __attribute__((unused)), FT_DOC **to)
|
||||
{
|
||||
DBUG_ENTER("walk_and_copy");
|
||||
from->doc.weight+=from->tmp_weight*from->word_ptr->weight;
|
||||
(*to)->dpos=from->doc.dpos;
|
||||
(*to)->weight=from->doc.weight;
|
||||
(*to)++;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
static int walk_and_push(FT_SUPERDOC *from,
|
||||
uint32 count __attribute__((unused)), QUEUE *best)
|
||||
{
|
||||
DBUG_ENTER("walk_and_copy");
|
||||
from->doc.weight+=from->tmp_weight*from->word_ptr->weight;
|
||||
set_if_smaller(best->elements, ft_query_expansion_limit-1);
|
||||
queue_insert(best, (byte *)& from->doc);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
static int FT_DOC_cmp(void *unused __attribute__((unused)),
|
||||
FT_DOC *a, FT_DOC *b)
|
||||
{
|
||||
return sgn(b->weight - a->weight);
|
||||
}
|
||||
|
||||
|
||||
FT_INFO *maria_ft_init_nlq_search(MARIA_HA *info, uint keynr, byte *query,
|
||||
uint query_len, uint flags, byte *record)
|
||||
{
|
||||
TREE wtree;
|
||||
ALL_IN_ONE aio;
|
||||
FT_DOC *dptr;
|
||||
FT_INFO *dlist=NULL;
|
||||
my_off_t saved_lastpos=info->lastpos;
|
||||
struct st_mysql_ftparser *parser;
|
||||
MYSQL_FTPARSER_PARAM *ftparser_param;
|
||||
DBUG_ENTER("maria_ft_init_nlq_search");
|
||||
|
||||
/* black magic ON */
|
||||
if ((int) (keynr = _ma_check_index(info,keynr)) < 0)
|
||||
DBUG_RETURN(NULL);
|
||||
if (_ma_readinfo(info,F_RDLCK,1))
|
||||
DBUG_RETURN(NULL);
|
||||
/* black magic OFF */
|
||||
|
||||
aio.info=info;
|
||||
aio.keynr=keynr;
|
||||
aio.charset=info->s->keyinfo[keynr].seg->charset;
|
||||
aio.keybuff=info->lastkey+info->s->base.max_key_length;
|
||||
parser= info->s->keyinfo[keynr].parser;
|
||||
if (! (ftparser_param= maria_ftparser_call_initializer(info, keynr, 0)))
|
||||
goto err;
|
||||
|
||||
bzero(&wtree,sizeof(wtree));
|
||||
|
||||
init_tree(&aio.dtree,0,0,sizeof(FT_SUPERDOC),(qsort_cmp2)&FT_SUPERDOC_cmp,0,
|
||||
NULL, NULL);
|
||||
|
||||
maria_ft_parse_init(&wtree, aio.charset);
|
||||
ftparser_param->flags= 0;
|
||||
if (maria_ft_parse(&wtree, query, query_len, parser, ftparser_param,
|
||||
&wtree.mem_root))
|
||||
goto err;
|
||||
|
||||
if (tree_walk(&wtree, (tree_walk_action)&walk_and_match, &aio,
|
||||
left_root_right))
|
||||
goto err;
|
||||
|
||||
if (flags & FT_EXPAND && ft_query_expansion_limit)
|
||||
{
|
||||
QUEUE best;
|
||||
init_queue(&best,ft_query_expansion_limit,0,0, (queue_compare) &FT_DOC_cmp,
|
||||
0);
|
||||
tree_walk(&aio.dtree, (tree_walk_action) &walk_and_push,
|
||||
&best, left_root_right);
|
||||
while (best.elements)
|
||||
{
|
||||
my_off_t docid=((FT_DOC *)queue_remove(& best, 0))->dpos;
|
||||
if (!(*info->read_record)(info,docid,record))
|
||||
{
|
||||
info->update|= HA_STATE_AKTIV;
|
||||
ftparser_param->flags= MYSQL_FTFLAGS_NEED_COPY;
|
||||
_ma_ft_parse(&wtree, info, keynr, record, ftparser_param,
|
||||
&wtree.mem_root);
|
||||
}
|
||||
}
|
||||
delete_queue(&best);
|
||||
reset_tree(&aio.dtree);
|
||||
if (tree_walk(&wtree, (tree_walk_action)&walk_and_match, &aio,
|
||||
left_root_right))
|
||||
goto err;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
If ndocs == 0, this will not allocate RAM for FT_INFO.doc[],
|
||||
so if ndocs == 0, FT_INFO.doc[] must not be accessed.
|
||||
*/
|
||||
dlist=(FT_INFO *)my_malloc(sizeof(FT_INFO)+
|
||||
sizeof(FT_DOC)*
|
||||
(int)(aio.dtree.elements_in_tree-1),
|
||||
MYF(0));
|
||||
if (!dlist)
|
||||
goto err;
|
||||
|
||||
dlist->please= (struct _ft_vft *) & _ma_ft_vft_nlq;
|
||||
dlist->ndocs=aio.dtree.elements_in_tree;
|
||||
dlist->curdoc=-1;
|
||||
dlist->info=aio.info;
|
||||
dptr=dlist->doc;
|
||||
|
||||
tree_walk(&aio.dtree, (tree_walk_action) &walk_and_copy,
|
||||
&dptr, left_root_right);
|
||||
|
||||
if (flags & FT_SORTED)
|
||||
qsort2(dlist->doc, dlist->ndocs, sizeof(FT_DOC), (qsort2_cmp)&FT_DOC_cmp, 0);
|
||||
|
||||
err:
|
||||
delete_tree(&aio.dtree);
|
||||
delete_tree(&wtree);
|
||||
info->lastpos=saved_lastpos;
|
||||
DBUG_RETURN(dlist);
|
||||
}
|
||||
|
||||
|
||||
int maria_ft_nlq_read_next(FT_INFO *handler, char *record)
|
||||
{
|
||||
MARIA_HA *info= (MARIA_HA *) handler->info;
|
||||
|
||||
if (++handler->curdoc >= handler->ndocs)
|
||||
{
|
||||
--handler->curdoc;
|
||||
return HA_ERR_END_OF_FILE;
|
||||
}
|
||||
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
|
||||
info->lastpos=handler->doc[handler->curdoc].dpos;
|
||||
if (!(*info->read_record)(info,info->lastpos,record))
|
||||
{
|
||||
info->update|= HA_STATE_AKTIV; /* Record is read */
|
||||
return 0;
|
||||
}
|
||||
return my_errno;
|
||||
}
|
||||
|
||||
|
||||
float maria_ft_nlq_find_relevance(FT_INFO *handler,
|
||||
byte *record __attribute__((unused)),
|
||||
uint length __attribute__((unused)))
|
||||
{
|
||||
int a,b,c;
|
||||
FT_DOC *docs=handler->doc;
|
||||
my_off_t docid=handler->info->lastpos;
|
||||
|
||||
if (docid == HA_POS_ERROR)
|
||||
return -5.0;
|
||||
|
||||
/* Assuming docs[] is sorted by dpos... */
|
||||
|
||||
for (a=0, b=handler->ndocs, c=(a+b)/2; b-a>1; c=(a+b)/2)
|
||||
{
|
||||
if (docs[c].dpos > docid)
|
||||
b=c;
|
||||
else
|
||||
a=c;
|
||||
}
|
||||
/* bounds check to avoid accessing unallocated handler->doc */
|
||||
if (a < handler->ndocs && docs[a].dpos == docid)
|
||||
return (float) docs[a].weight;
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
void maria_ft_nlq_close_search(FT_INFO *handler)
|
||||
{
|
||||
my_free((gptr)handler,MYF(0));
|
||||
}
|
||||
|
||||
|
||||
float maria_ft_nlq_get_relevance(FT_INFO *handler)
|
||||
{
|
||||
return (float) handler->doc[handler->curdoc].weight;
|
||||
}
|
||||
|
||||
|
||||
void maria_ft_nlq_reinit_search(FT_INFO *handler)
|
||||
{
|
||||
handler->curdoc=-1;
|
||||
}
|
||||
|
||||
425
storage/maria/ma_ft_parser.c
Normal file
425
storage/maria/ma_ft_parser.c
Normal file
@@ -0,0 +1,425 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
|
||||
typedef struct st_maria_ft_docstat {
|
||||
FT_WORD *list;
|
||||
uint uniq;
|
||||
double sum;
|
||||
} FT_DOCSTAT;
|
||||
|
||||
|
||||
typedef struct st_my_maria_ft_parser_param
|
||||
{
|
||||
TREE *wtree;
|
||||
MEM_ROOT *mem_root;
|
||||
} MY_FT_PARSER_PARAM;
|
||||
|
||||
|
||||
static int FT_WORD_cmp(CHARSET_INFO* cs, FT_WORD *w1, FT_WORD *w2)
|
||||
{
|
||||
return ha_compare_text(cs, (uchar*) w1->pos, w1->len,
|
||||
(uchar*) w2->pos, w2->len, 0, 0);
|
||||
}
|
||||
|
||||
static int walk_and_copy(FT_WORD *word,uint32 count,FT_DOCSTAT *docstat)
|
||||
{
|
||||
word->weight=LWS_IN_USE;
|
||||
docstat->sum+=word->weight;
|
||||
memcpy_fixed((docstat->list)++,word,sizeof(FT_WORD));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* transforms tree of words into the array, applying normalization */
|
||||
|
||||
FT_WORD * maria_ft_linearize(TREE *wtree, MEM_ROOT *mem_root)
|
||||
{
|
||||
FT_WORD *wlist,*p;
|
||||
FT_DOCSTAT docstat;
|
||||
DBUG_ENTER("maria_ft_linearize");
|
||||
|
||||
if ((wlist=(FT_WORD *) alloc_root(mem_root, sizeof(FT_WORD)*
|
||||
(1+wtree->elements_in_tree))))
|
||||
{
|
||||
docstat.list=wlist;
|
||||
docstat.uniq=wtree->elements_in_tree;
|
||||
docstat.sum=0;
|
||||
tree_walk(wtree,(tree_walk_action)&walk_and_copy,&docstat,left_root_right);
|
||||
}
|
||||
delete_tree(wtree);
|
||||
if (!wlist)
|
||||
DBUG_RETURN(NULL);
|
||||
|
||||
docstat.list->pos=NULL;
|
||||
|
||||
for (p=wlist;p->pos;p++)
|
||||
{
|
||||
p->weight=PRENORM_IN_USE;
|
||||
}
|
||||
|
||||
for (p=wlist;p->pos;p++)
|
||||
{
|
||||
p->weight/=NORM_IN_USE;
|
||||
}
|
||||
|
||||
DBUG_RETURN(wlist);
|
||||
}
|
||||
|
||||
my_bool maria_ft_boolean_check_syntax_string(const byte *str)
|
||||
{
|
||||
uint i, j;
|
||||
|
||||
if (!str ||
|
||||
(strlen(str)+1 != sizeof(ft_boolean_syntax)) ||
|
||||
(str[0] != ' ' && str[1] != ' '))
|
||||
return 1;
|
||||
for (i=0; i<sizeof(ft_boolean_syntax); i++)
|
||||
{
|
||||
/* limiting to 7-bit ascii only */
|
||||
if ((unsigned char)(str[i]) > 127 ||
|
||||
my_isalnum(default_charset_info, str[i]))
|
||||
return 1;
|
||||
for (j=0; j<i; j++)
|
||||
if (str[i] == str[j] && (i != 11 || j != 10))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
RETURN VALUE
|
||||
0 - eof
|
||||
1 - word found
|
||||
2 - left bracket
|
||||
3 - right bracket
|
||||
4 - stopword found
|
||||
*/
|
||||
byte maria_ft_get_word(CHARSET_INFO *cs, byte **start, byte *end,
|
||||
FT_WORD *word, MYSQL_FTPARSER_BOOLEAN_INFO *param)
|
||||
{
|
||||
byte *doc=*start;
|
||||
int ctype;
|
||||
uint mwc, length, mbl;
|
||||
|
||||
param->yesno=(FTB_YES==' ') ? 1 : (param->quot != 0);
|
||||
param->weight_adjust= param->wasign= 0;
|
||||
param->type= FT_TOKEN_EOF;
|
||||
|
||||
while (doc<end)
|
||||
{
|
||||
for (; doc < end; doc+= (mbl > 0 ? mbl : 1))
|
||||
{
|
||||
mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end);
|
||||
if (true_word_char(ctype, *doc))
|
||||
break;
|
||||
if (*doc == FTB_RQUOT && param->quot)
|
||||
{
|
||||
param->quot=doc;
|
||||
*start=doc+1;
|
||||
param->type= FT_TOKEN_RIGHT_PAREN;
|
||||
goto ret;
|
||||
}
|
||||
if (!param->quot)
|
||||
{
|
||||
if (*doc == FTB_LBR || *doc == FTB_RBR || *doc == FTB_LQUOT)
|
||||
{
|
||||
/* param->prev=' '; */
|
||||
*start=doc+1;
|
||||
if (*doc == FTB_LQUOT) param->quot=*start;
|
||||
param->type= (*doc == FTB_RBR ? FT_TOKEN_RIGHT_PAREN : FT_TOKEN_LEFT_PAREN);
|
||||
goto ret;
|
||||
}
|
||||
if (param->prev == ' ')
|
||||
{
|
||||
if (*doc == FTB_YES ) { param->yesno=+1; continue; } else
|
||||
if (*doc == FTB_EGAL) { param->yesno= 0; continue; } else
|
||||
if (*doc == FTB_NO ) { param->yesno=-1; continue; } else
|
||||
if (*doc == FTB_INC ) { param->weight_adjust++; continue; } else
|
||||
if (*doc == FTB_DEC ) { param->weight_adjust--; continue; } else
|
||||
if (*doc == FTB_NEG ) { param->wasign= !param->wasign; continue; }
|
||||
}
|
||||
}
|
||||
param->prev=*doc;
|
||||
param->yesno=(FTB_YES==' ') ? 1 : (param->quot != 0);
|
||||
param->weight_adjust= param->wasign= 0;
|
||||
}
|
||||
|
||||
mwc=length=0;
|
||||
for (word->pos= doc; doc < end; length++, doc+= (mbl > 0 ? mbl : 1))
|
||||
{
|
||||
mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end);
|
||||
if (true_word_char(ctype, *doc))
|
||||
mwc=0;
|
||||
else if (!misc_word_char(*doc) || mwc)
|
||||
break;
|
||||
else
|
||||
mwc++;
|
||||
}
|
||||
param->prev='A'; /* be sure *prev is true_word_char */
|
||||
word->len= (uint)(doc-word->pos) - mwc;
|
||||
if ((param->trunc=(doc<end && *doc == FTB_TRUNC)))
|
||||
doc++;
|
||||
|
||||
if (((length >= ft_min_word_len && !is_stopword(word->pos, word->len))
|
||||
|| param->trunc) && length < ft_max_word_len)
|
||||
{
|
||||
*start=doc;
|
||||
param->type= FT_TOKEN_WORD;
|
||||
goto ret;
|
||||
}
|
||||
else if (length) /* make sure length > 0 (if start contains spaces only) */
|
||||
{
|
||||
*start= doc;
|
||||
param->type= FT_TOKEN_STOPWORD;
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
if (param->quot)
|
||||
{
|
||||
param->quot=*start=doc;
|
||||
param->type= 3; /* FT_RBR */
|
||||
goto ret;
|
||||
}
|
||||
ret:
|
||||
return param->type;
|
||||
}
|
||||
|
||||
byte maria_ft_simple_get_word(CHARSET_INFO *cs, byte **start, const byte *end,
|
||||
FT_WORD *word, my_bool skip_stopwords)
|
||||
{
|
||||
byte *doc= *start;
|
||||
uint mwc, length, mbl;
|
||||
int ctype;
|
||||
DBUG_ENTER("maria_ft_simple_get_word");
|
||||
|
||||
do
|
||||
{
|
||||
for (;; doc+= (mbl > 0 ? mbl : 1))
|
||||
{
|
||||
if (doc >= end)
|
||||
DBUG_RETURN(0);
|
||||
mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end);
|
||||
if (true_word_char(ctype, *doc))
|
||||
break;
|
||||
}
|
||||
|
||||
mwc= length= 0;
|
||||
for (word->pos= doc; doc < end; length++, doc+= (mbl > 0 ? mbl : 1))
|
||||
{
|
||||
mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end);
|
||||
if (true_word_char(ctype, *doc))
|
||||
mwc= 0;
|
||||
else if (!misc_word_char(*doc) || mwc)
|
||||
break;
|
||||
else
|
||||
mwc++;
|
||||
}
|
||||
|
||||
word->len= (uint)(doc-word->pos) - mwc;
|
||||
|
||||
if (skip_stopwords == FALSE ||
|
||||
(length >= ft_min_word_len && length < ft_max_word_len &&
|
||||
!is_stopword(word->pos, word->len)))
|
||||
{
|
||||
*start= doc;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
} while (doc < end);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
void maria_ft_parse_init(TREE *wtree, CHARSET_INFO *cs)
|
||||
{
|
||||
DBUG_ENTER("maria_ft_parse_init");
|
||||
if (!is_tree_inited(wtree))
|
||||
init_tree(wtree,0,0,sizeof(FT_WORD),(qsort_cmp2)&FT_WORD_cmp,0,NULL, cs);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
static int maria_ft_add_word(MYSQL_FTPARSER_PARAM *param,
|
||||
char *word, int word_len,
|
||||
MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info __attribute__((unused)))
|
||||
{
|
||||
TREE *wtree;
|
||||
FT_WORD w;
|
||||
MY_FT_PARSER_PARAM *ft_param=param->mysql_ftparam;
|
||||
DBUG_ENTER("maria_ft_add_word");
|
||||
wtree= ft_param->wtree;
|
||||
if (param->flags & MYSQL_FTFLAGS_NEED_COPY)
|
||||
{
|
||||
byte *ptr;
|
||||
DBUG_ASSERT(wtree->with_delete == 0);
|
||||
ptr= (byte *)alloc_root(ft_param->mem_root, word_len);
|
||||
memcpy(ptr, word, word_len);
|
||||
w.pos= ptr;
|
||||
}
|
||||
else
|
||||
w.pos= word;
|
||||
w.len= word_len;
|
||||
if (!tree_insert(wtree, &w, 0, wtree->custom_arg))
|
||||
{
|
||||
delete_tree(wtree);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
static int maria_ft_parse_internal(MYSQL_FTPARSER_PARAM *param,
|
||||
char *doc, int doc_len)
|
||||
{
|
||||
byte *end=doc+doc_len;
|
||||
MY_FT_PARSER_PARAM *ft_param=param->mysql_ftparam;
|
||||
TREE *wtree= ft_param->wtree;
|
||||
FT_WORD w;
|
||||
DBUG_ENTER("maria_ft_parse_internal");
|
||||
|
||||
while (maria_ft_simple_get_word(wtree->custom_arg, &doc, end, &w, TRUE))
|
||||
if (param->mysql_add_word(param, w.pos, w.len, 0))
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
int maria_ft_parse(TREE *wtree, byte *doc, int doclen,
|
||||
struct st_mysql_ftparser *parser,
|
||||
MYSQL_FTPARSER_PARAM *param, MEM_ROOT *mem_root)
|
||||
{
|
||||
MY_FT_PARSER_PARAM my_param;
|
||||
DBUG_ENTER("maria_ft_parse");
|
||||
DBUG_ASSERT(parser);
|
||||
my_param.wtree= wtree;
|
||||
my_param.mem_root= mem_root;
|
||||
|
||||
param->mysql_parse= maria_ft_parse_internal;
|
||||
param->mysql_add_word= maria_ft_add_word;
|
||||
param->mysql_ftparam= &my_param;
|
||||
param->cs= wtree->custom_arg;
|
||||
param->doc= doc;
|
||||
param->length= doclen;
|
||||
param->mode= MYSQL_FTPARSER_SIMPLE_MODE;
|
||||
DBUG_RETURN(parser->parse(param));
|
||||
}
|
||||
|
||||
|
||||
#define MAX_PARAM_NR 2
|
||||
MYSQL_FTPARSER_PARAM *maria_ftparser_call_initializer(MARIA_HA *info,
|
||||
uint keynr, uint paramnr)
|
||||
{
|
||||
uint32 ftparser_nr;
|
||||
struct st_mysql_ftparser *parser;
|
||||
if (! info->ftparser_param)
|
||||
{
|
||||
/* info->ftparser_param can not be zero after the initialization,
|
||||
because it always includes built-in fulltext parser. And built-in
|
||||
parser can be called even if the table has no fulltext indexes and
|
||||
no varchar/text fields. */
|
||||
if (! info->s->ftparsers)
|
||||
{
|
||||
/* It's ok that modification to shared structure is done w/o mutex
|
||||
locks, because all threads would set the same variables to the
|
||||
same values. */
|
||||
uint i, j, keys= info->s->state.header.keys, ftparsers= 1;
|
||||
for (i= 0; i < keys; i++)
|
||||
{
|
||||
MARIA_KEYDEF *keyinfo= &info->s->keyinfo[i];
|
||||
if (keyinfo->flag & HA_FULLTEXT)
|
||||
{
|
||||
for (j= 0;; j++)
|
||||
{
|
||||
if (j == i)
|
||||
{
|
||||
keyinfo->ftparser_nr= ftparsers++;
|
||||
break;
|
||||
}
|
||||
if (info->s->keyinfo[j].flag & HA_FULLTEXT &&
|
||||
keyinfo->parser == info->s->keyinfo[j].parser)
|
||||
{
|
||||
keyinfo->ftparser_nr= info->s->keyinfo[j].ftparser_nr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
info->s->ftparsers= ftparsers;
|
||||
}
|
||||
/*
|
||||
We have to allocate two MYSQL_FTPARSER_PARAM structures per plugin
|
||||
because in a boolean search a parser is called recursively
|
||||
ftb_find_relevance* calls ftb_check_phrase*
|
||||
(MAX_PARAM_NR=2)
|
||||
*/
|
||||
info->ftparser_param= (MYSQL_FTPARSER_PARAM *)
|
||||
my_malloc(MAX_PARAM_NR * sizeof(MYSQL_FTPARSER_PARAM) *
|
||||
info->s->ftparsers, MYF(MY_WME|MY_ZEROFILL));
|
||||
init_alloc_root(&info->ft_memroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0);
|
||||
if (! info->ftparser_param)
|
||||
return 0;
|
||||
}
|
||||
if (keynr == NO_SUCH_KEY)
|
||||
{
|
||||
ftparser_nr= 0;
|
||||
parser= &ft_default_parser;
|
||||
}
|
||||
else
|
||||
{
|
||||
ftparser_nr= info->s->keyinfo[keynr].ftparser_nr;
|
||||
parser= info->s->keyinfo[keynr].parser;
|
||||
}
|
||||
DBUG_ASSERT(paramnr < MAX_PARAM_NR);
|
||||
ftparser_nr= ftparser_nr*MAX_PARAM_NR + paramnr;
|
||||
if (! info->ftparser_param[ftparser_nr].mysql_add_word)
|
||||
{
|
||||
/* Note, that mysql_add_word is used here as a flag:
|
||||
mysql_add_word == 0 - parser is not initialized
|
||||
mysql_add_word != 0 - parser is initialized, or no
|
||||
initialization needed. */
|
||||
info->ftparser_param[ftparser_nr].mysql_add_word= (void *)1;
|
||||
if (parser->init && parser->init(&info->ftparser_param[ftparser_nr]))
|
||||
return 0;
|
||||
}
|
||||
return &info->ftparser_param[ftparser_nr];
|
||||
}
|
||||
|
||||
|
||||
void maria_ftparser_call_deinitializer(MARIA_HA *info)
|
||||
{
|
||||
uint i, j, keys= info->s->state.header.keys;
|
||||
free_root(&info->ft_memroot, MYF(0));
|
||||
if (! info->ftparser_param)
|
||||
return;
|
||||
for (i= 0; i < keys; i++)
|
||||
{
|
||||
MARIA_KEYDEF *keyinfo= &info->s->keyinfo[i];
|
||||
for (j=0; j < MAX_PARAM_NR; j++)
|
||||
{
|
||||
MYSQL_FTPARSER_PARAM *ftparser_param=
|
||||
&info->ftparser_param[keyinfo->ftparser_nr*MAX_PARAM_NR + j];
|
||||
if (keyinfo->flag & HA_FULLTEXT && ftparser_param->mysql_add_word)
|
||||
{
|
||||
if (keyinfo->parser->deinit)
|
||||
keyinfo->parser->deinit(ftparser_param);
|
||||
ftparser_param->mysql_add_word= 0;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
storage/maria/ma_ft_stem.c
Normal file
19
storage/maria/ma_ft_stem.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
/* mulitingual stem */
|
||||
318
storage/maria/ma_ft_test1.c
Normal file
318
storage/maria/ma_ft_test1.c
Normal file
@@ -0,0 +1,318 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code
|
||||
added support for long options (my_getopt) 22.5.2002 by Jani Tolonen */
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
#include "maria_ft_test1.h"
|
||||
#include <my_getopt.h>
|
||||
|
||||
static int key_field=FIELD_VARCHAR,extra_field=FIELD_SKIP_ENDSPACE;
|
||||
static uint key_length=200,extra_length=50;
|
||||
static int key_type=HA_KEYTYPE_TEXT;
|
||||
static int verbose=0,silent=0,skip_update=0,
|
||||
no_keys=0,no_stopwords=0,no_search=0,no_fulltext=0;
|
||||
static int create_flag=0,error=0;
|
||||
|
||||
#define MAX_REC_LENGTH 300
|
||||
static char record[MAX_REC_LENGTH],read_record[MAX_REC_LENGTH];
|
||||
|
||||
static int run_test(const char *filename);
|
||||
static void get_options(int argc, char *argv[]);
|
||||
static void create_record(char *, int);
|
||||
static void usage();
|
||||
|
||||
static struct my_option my_long_options[] =
|
||||
{
|
||||
{"", 'v', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", '?', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'h', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'V', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'v', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 's', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'N', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'S', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'K', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'F', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", 'U', "", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"", '#', "", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MY_INIT(argv[0]);
|
||||
|
||||
get_options(argc,argv);
|
||||
maria_init();
|
||||
|
||||
exit(run_test("FT1"));
|
||||
}
|
||||
|
||||
static MARIA_COLUMNDEF recinfo[3];
|
||||
static MARIA_KEYDEF keyinfo[2];
|
||||
static HA_KEYSEG keyseg[10];
|
||||
|
||||
static int run_test(const char *filename)
|
||||
{
|
||||
MARIA_HA *file;
|
||||
int i,j;
|
||||
my_off_t pos;
|
||||
|
||||
bzero((char*) recinfo,sizeof(recinfo));
|
||||
|
||||
/* First define 2 columns */
|
||||
recinfo[0].type=extra_field;
|
||||
recinfo[0].length= (extra_field == FIELD_BLOB ? 4 + maria_portable_sizeof_char_ptr :
|
||||
extra_length);
|
||||
if (extra_field == FIELD_VARCHAR)
|
||||
recinfo[0].length+= HA_VARCHAR_PACKLENGTH(extra_length);
|
||||
recinfo[1].type=key_field;
|
||||
recinfo[1].length= (key_field == FIELD_BLOB ? 4+maria_portable_sizeof_char_ptr :
|
||||
key_length);
|
||||
if (key_field == FIELD_VARCHAR)
|
||||
recinfo[1].length+= HA_VARCHAR_PACKLENGTH(key_length);
|
||||
|
||||
/* Define a key over the first column */
|
||||
keyinfo[0].seg=keyseg;
|
||||
keyinfo[0].keysegs=1;
|
||||
keyinfo[0].block_length= 0; /* Default block length */
|
||||
keyinfo[0].seg[0].type= key_type;
|
||||
keyinfo[0].seg[0].flag= (key_field == FIELD_BLOB) ? HA_BLOB_PART:
|
||||
(key_field == FIELD_VARCHAR) ? HA_VAR_LENGTH_PART:0;
|
||||
keyinfo[0].seg[0].start=recinfo[0].length;
|
||||
keyinfo[0].seg[0].length=key_length;
|
||||
keyinfo[0].seg[0].null_bit= 0;
|
||||
keyinfo[0].seg[0].null_pos=0;
|
||||
keyinfo[0].seg[0].language= default_charset_info->number;
|
||||
keyinfo[0].flag = (no_fulltext?HA_PACK_KEY:HA_FULLTEXT);
|
||||
|
||||
if (!silent)
|
||||
printf("- Creating isam-file\n");
|
||||
if (maria_create(filename,(no_keys?0:1),keyinfo,2,recinfo,0,NULL,
|
||||
(MARIA_CREATE_INFO*) 0, create_flag))
|
||||
goto err;
|
||||
if (!(file=maria_open(filename,2,0)))
|
||||
goto err;
|
||||
|
||||
if (!silent)
|
||||
printf("- %s stopwords\n",no_stopwords?"Skipping":"Initializing");
|
||||
maria_ft_init_stopwords(no_stopwords?NULL:maria_ft_precompiled_stopwords);
|
||||
|
||||
if (!silent)
|
||||
printf("- Writing key:s\n");
|
||||
|
||||
my_errno=0;
|
||||
for (i=NUPD ; i<NDATAS; i++ )
|
||||
{
|
||||
create_record(record,i);
|
||||
error=maria_write(file,record);
|
||||
if (verbose || error)
|
||||
printf("I= %2d maria_write: %d errno: %d, record: %s\n",
|
||||
i,error,my_errno,data[i].f0);
|
||||
}
|
||||
|
||||
if (!skip_update)
|
||||
{
|
||||
if (!silent)
|
||||
printf("- Updating rows\n");
|
||||
|
||||
/* Read through all rows and update them */
|
||||
pos=(ha_rows) 0;
|
||||
i=0;
|
||||
while ((error=maria_rrnd(file,read_record,pos)) == 0)
|
||||
{
|
||||
create_record(record,NUPD-i-1);
|
||||
if (maria_update(file,read_record,record))
|
||||
{
|
||||
printf("Can't update row: %.*s, error: %d\n",
|
||||
keyinfo[0].seg[0].length,record,my_errno);
|
||||
}
|
||||
if(++i == NUPD) break;
|
||||
pos=HA_OFFSET_ERROR;
|
||||
}
|
||||
if (i != NUPD)
|
||||
printf("Found %d of %d rows\n", i,NUPD);
|
||||
}
|
||||
|
||||
if (maria_close(file)) goto err;
|
||||
if(no_search) return 0;
|
||||
if (!silent)
|
||||
printf("- Reopening file\n");
|
||||
if (!(file=maria_open(filename,2,0))) goto err;
|
||||
if (!silent)
|
||||
printf("- Reading rows with key\n");
|
||||
for (i=0 ; i < NQUERIES ; i++)
|
||||
{
|
||||
FT_DOCLIST *result;
|
||||
result=maria_ft_nlq_init_search(file,0,(char*) query[i],strlen(query[i]),1);
|
||||
if(!result)
|
||||
{
|
||||
printf("Query %d: `%s' failed with errno %3d\n",i,query[i],my_errno);
|
||||
continue;
|
||||
}
|
||||
printf("Query %d: `%s'. Found: %d. Top five documents:\n",
|
||||
i,query[i],result->ndocs);
|
||||
for (j=0;j<5;j++)
|
||||
{
|
||||
double w; int err;
|
||||
err= maria_ft_nlq_read_next(result, read_record);
|
||||
if (err==HA_ERR_END_OF_FILE)
|
||||
{
|
||||
printf("No more matches!\n");
|
||||
break;
|
||||
}
|
||||
else if (err)
|
||||
{
|
||||
printf("maria_ft_read_next %d failed with errno %3d\n",j,my_errno);
|
||||
break;
|
||||
}
|
||||
w=maria_ft_nlq_get_relevance(result);
|
||||
if (key_field == FIELD_VARCHAR)
|
||||
{
|
||||
uint l;
|
||||
char *p;
|
||||
p=recinfo[0].length+read_record;
|
||||
l=uint2korr(p);
|
||||
printf("%10.7f: %.*s\n",w,(int) l,p+2);
|
||||
}
|
||||
else
|
||||
printf("%10.7f: %.*s\n",w,recinfo[1].length,
|
||||
recinfo[0].length+read_record);
|
||||
}
|
||||
maria_ft_nlq_close_search(result);
|
||||
}
|
||||
|
||||
if (maria_close(file)) goto err;
|
||||
maria_end();
|
||||
my_end(MY_CHECK_ERROR);
|
||||
|
||||
return (0);
|
||||
err:
|
||||
printf("got error: %3d when using maria-database\n",my_errno);
|
||||
return 1; /* skip warning */
|
||||
}
|
||||
|
||||
static char blob_key[MAX_REC_LENGTH];
|
||||
/* static char blob_record[MAX_REC_LENGTH+20*20]; */
|
||||
|
||||
void create_record(char *pos, int n)
|
||||
{
|
||||
bzero((char*) pos,MAX_REC_LENGTH);
|
||||
if (recinfo[0].type == FIELD_BLOB)
|
||||
{
|
||||
uint tmp;
|
||||
char *ptr;
|
||||
strnmov(blob_key,data[n].f0,keyinfo[0].seg[0].length);
|
||||
tmp=strlen(blob_key);
|
||||
int4store(pos,tmp);
|
||||
ptr=blob_key;
|
||||
memcpy_fixed(pos+4,&ptr,sizeof(char*));
|
||||
pos+=recinfo[0].length;
|
||||
}
|
||||
else if (recinfo[0].type == FIELD_VARCHAR)
|
||||
{
|
||||
uint tmp;
|
||||
/* -1 is here because pack_length is stored in seg->length */
|
||||
uint pack_length= HA_VARCHAR_PACKLENGTH(keyinfo[0].seg[0].length-1);
|
||||
strnmov(pos+pack_length,data[n].f0,keyinfo[0].seg[0].length);
|
||||
tmp=strlen(pos+pack_length);
|
||||
if (pack_length == 1)
|
||||
*pos= (char) tmp;
|
||||
else
|
||||
int2store(pos,tmp);
|
||||
pos+=recinfo[0].length;
|
||||
}
|
||||
else
|
||||
{
|
||||
strnmov(pos,data[n].f0,keyinfo[0].seg[0].length);
|
||||
pos+=recinfo[0].length;
|
||||
}
|
||||
if (recinfo[1].type == FIELD_BLOB)
|
||||
{
|
||||
uint tmp;
|
||||
char *ptr;
|
||||
strnmov(blob_key,data[n].f2,keyinfo[0].seg[0].length);
|
||||
tmp=strlen(blob_key);
|
||||
int4store(pos,tmp);
|
||||
ptr=blob_key;
|
||||
memcpy_fixed(pos+4,&ptr,sizeof(char*));
|
||||
pos+=recinfo[1].length;
|
||||
}
|
||||
else if (recinfo[1].type == FIELD_VARCHAR)
|
||||
{
|
||||
uint tmp;
|
||||
/* -1 is here because pack_length is stored in seg->length */
|
||||
uint pack_length= HA_VARCHAR_PACKLENGTH(keyinfo[0].seg[0].length-1);
|
||||
strnmov(pos+pack_length,data[n].f2,keyinfo[0].seg[0].length);
|
||||
tmp=strlen(pos+1);
|
||||
if (pack_length == 1)
|
||||
*pos= (char) tmp;
|
||||
else
|
||||
int2store(pos,tmp);
|
||||
pos+=recinfo[1].length;
|
||||
}
|
||||
else
|
||||
{
|
||||
strnmov(pos,data[n].f2,keyinfo[0].seg[0].length);
|
||||
pos+=recinfo[1].length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static my_bool
|
||||
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
||||
char *argument)
|
||||
{
|
||||
switch(optid) {
|
||||
case 'v': verbose=1; break;
|
||||
case 's': silent=1; break;
|
||||
case 'F': no_fulltext=1; no_search=1;
|
||||
case 'U': skip_update=1; break;
|
||||
case 'K': no_keys=no_search=1; break;
|
||||
case 'N': no_search=1; break;
|
||||
case 'S': no_stopwords=1; break;
|
||||
case '#':
|
||||
DBUG_PUSH (argument);
|
||||
break;
|
||||
case 'V':
|
||||
case '?':
|
||||
case 'h':
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read options */
|
||||
|
||||
static void get_options(int argc,char *argv[])
|
||||
{
|
||||
int ho_error;
|
||||
|
||||
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
|
||||
exit(ho_error);
|
||||
return;
|
||||
} /* get options */
|
||||
|
||||
|
||||
static void usage()
|
||||
{
|
||||
printf("%s [options]\n", my_progname);
|
||||
my_print_help(my_long_options);
|
||||
my_print_variables(my_long_options);
|
||||
}
|
||||
421
storage/maria/ma_ft_test1.h
Normal file
421
storage/maria/ma_ft_test1.h
Normal file
@@ -0,0 +1,421 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
#define NUPD 20
|
||||
#define NDATAS 389
|
||||
struct { const char *f0, *f2; } data[NDATAS] = {
|
||||
{"1", "General Information about MySQL"},
|
||||
{"1.1", "What is MySQL?"},
|
||||
{"1.2", "About this manual"},
|
||||
{"1.3", "History of MySQL"},
|
||||
{"1.4", "The main features of MySQL"},
|
||||
{"1.5", "General SQL information and tutorials"},
|
||||
{"1.6", "Useful MySQL-related links"},
|
||||
{"1.7", "What are stored procedures and triggers and so on?"},
|
||||
{"2", "MySQL mailing lists and how to ask questions/give error (bug) reports"},
|
||||
{"2.1", "Subscribing to/un-subscribing from the MySQL mailing list"},
|
||||
{"2.2", "Asking questions or reporting bugs"},
|
||||
{"2.3", "I think I have found a bug. What information do you need to help me?"},
|
||||
{"2.3.1", "MySQL keeps crashing"},
|
||||
{"2.4", "Guidelines for answering questions on the mailing list"},
|
||||
{"3", "Licensing or When do I have/want to pay for MySQL?"},
|
||||
{"3.1", "How much does MySQL cost?"},
|
||||
{"3.2", "How do I get commercial support?"},
|
||||
{"3.2.1", "Types of commercial support"},
|
||||
{"3.2.1.1", "Basic email support"},
|
||||
{"3.2.1.2", "Extended email support"},
|
||||
/*------------------------------- NUPD=20 -------------------------------*/
|
||||
{"3.2.1.3", "Asking: Login support"},
|
||||
{"3.2.1.4", "Extended login support"},
|
||||
{"3.3", "How do I pay for licenses/support?"},
|
||||
{"3.4", "Who do I contact when I want more information about licensing/support?"},
|
||||
{"3.5", "What Copyright does MySQL use?"},
|
||||
{"3.6", "When may I distribute MySQL commercially without a fee?"},
|
||||
{"3.7", "I want to sell a product that can be configured to use MySQL"},
|
||||
{"3.8", "I am running a commercial web server using MySQL"},
|
||||
{"3.9", "Do I need a license to sell commercial Perl/tcl/PHP/Web+ etc applications?"},
|
||||
{"3.10", "Possible future changes in the licensing"},
|
||||
{"4", "Compiling and installing MySQL"},
|
||||
{"4.1", "How do I get MySQL?"},
|
||||
{"4.2", "Which MySQL version should I use?"},
|
||||
{"4.3", "How/when will you release updates?"},
|
||||
{"4.4", "What operating systems does MySQL support?"},
|
||||
{"4.5", "Compiling MySQL from source code"},
|
||||
{"4.5.1", "Quick installation overview"},
|
||||
{"4.5.2", "Usual configure switches"},
|
||||
{"4.5.3", "Applying a patch"},
|
||||
{"4.6", "Problems compiling?"},
|
||||
{"4.7", "General compilation notes"},
|
||||
{"4.8", "MIT-pthreads notes (FreeBSD)"},
|
||||
{"4.9", "Perl installation comments"},
|
||||
{"4.10", "Special things to consider for some machine/OS combinations"},
|
||||
{"4.10.1", "Solaris notes"},
|
||||
{"4.10.2", "SunOS 4 notes"},
|
||||
{"4.10.3", "Linux notes for all versions"},
|
||||
{"4.10.3.1", "Linux-x86 notes"},
|
||||
{"4.10.3.2", "RedHat 5.0"},
|
||||
{"4.10.3.3", "RedHat 5.1"},
|
||||
{"4.10.3.4", "Linux-Sparc notes"},
|
||||
{"4.10.3.5", "Linux-Alpha notes"},
|
||||
{"4.10.3.6", "MkLinux notes"},
|
||||
{"4.10.4", "Alpha-DEC-Unix notes"},
|
||||
{"4.10.5", "Alpha-DEC-OSF1 notes"},
|
||||
{"4.10.6", "SGI-IRIX notes"},
|
||||
{"4.10.7", "FreeBSD notes"},
|
||||
{"4.10.7.1", "FreeBSD-3.0 notes"},
|
||||
{"4.10.8", "BSD/OS 2.# notes"},
|
||||
{"4.10.8.1", "BSD/OS 3.# notes"},
|
||||
{"4.10.9", "SCO notes"},
|
||||
{"4.10.10", "SCO Unixware 7.0 notes"},
|
||||
{"4.10.11", "IBM-AIX notes"},
|
||||
{"4.10.12", "HP-UX notes"},
|
||||
{"4.11", "TcX binaries"},
|
||||
{"4.12", "Win32 notes"},
|
||||
{"4.13", "Installation instructions for MySQL binary releases"},
|
||||
{"4.13.1", "How to get MySQL Perl support working"},
|
||||
{"4.13.2", "Linux notes"},
|
||||
{"4.13.3", "HP-UX notes"},
|
||||
{"4.13.4", "Linking client libraries"},
|
||||
{"4.14", "Problems running mysql_install_db"},
|
||||
{"4.15", "Problems starting MySQL"},
|
||||
{"4.16", "Automatic start/stop of MySQL"},
|
||||
{"4.17", "Option files"},
|
||||
{"5", "How standards-compatible is MySQL?"},
|
||||
{"5.1", "What extensions has MySQL to ANSI SQL92?"},
|
||||
{"5.2", "What functionality is missing in MySQL?"},
|
||||
{"5.2.1", "Sub-selects"},
|
||||
{"5.2.2", "SELECT INTO TABLE"},
|
||||
{"5.2.3", "Transactions"},
|
||||
{"5.2.4", "Triggers"},
|
||||
{"5.2.5", "Foreign Keys"},
|
||||
{"5.2.5.1", "Some reasons NOT to use FOREIGN KEYS"},
|
||||
{"5.2.6", "Views"},
|
||||
{"5.2.7", "-- as start of a comment"},
|
||||
{"5.3", "What standards does MySQL follow?"},
|
||||
{"5.4", "What functions exist only for compatibility?"},
|
||||
{"5.5", "Limitations of BLOB and TEXT types"},
|
||||
{"5.6", "How to cope without COMMIT-ROLLBACK"},
|
||||
{"6", "The MySQL access privilege system"},
|
||||
{"6.1", "What the privilege system does"},
|
||||
{"6.2", "Connecting to the MySQL server"},
|
||||
{"6.2.1", "Keeping your password secure"},
|
||||
{"6.3", "Privileges provided by MySQL"},
|
||||
{"6.4", "How the privilege system works"},
|
||||
{"6.5", "The privilege tables"},
|
||||
{"6.6", "Setting up the initial MySQL privileges"},
|
||||
{"6.7", "Adding new user privileges to MySQL"},
|
||||
{"6.8", "An example permission setup"},
|
||||
{"6.9", "Causes of Access denied errors"},
|
||||
{"6.10", "How to make MySQL secure against crackers"},
|
||||
{"7", "MySQL language reference"},
|
||||
{"7.1", "Literals: how to write strings and numbers"},
|
||||
{"7.1.1", "Strings"},
|
||||
{"7.1.2", "Numbers"},
|
||||
{"7.1.3", "NULL values"},
|
||||
{"7.1.4", "Database, table, index, column and alias names"},
|
||||
{"7.1.4.1", "Case sensitivity in names"},
|
||||
{"7.2", "Column types"},
|
||||
{"7.2.1", "Column type storage requirements"},
|
||||
{"7.2.5", "Numeric types"},
|
||||
{"7.2.6", "Date and time types"},
|
||||
{"7.2.6.1", "The DATE type"},
|
||||
{"7.2.6.2", "The TIME type"},
|
||||
{"7.2.6.3", "The DATETIME type"},
|
||||
{"7.2.6.4", "The TIMESTAMP type"},
|
||||
{"7.2.6.5", "The YEAR type"},
|
||||
{"7.2.6.6", "Miscellaneous date and time properties"},
|
||||
{"7.2.7", "String types"},
|
||||
{"7.2.7.1", "The CHAR and VARCHAR types"},
|
||||
{"7.2.7.2", "The BLOB and TEXT types"},
|
||||
{"7.2.7.3", "The ENUM type"},
|
||||
{"7.2.7.4", "The SET type"},
|
||||
{"7.2.8", "Choosing the right type for a column"},
|
||||
{"7.2.9", "Column indexes"},
|
||||
{"7.2.10", "Multiple-column indexes"},
|
||||
{"7.2.11", "Using column types from other database engines"},
|
||||
{"7.3", "Functions for use in SELECT and WHERE clauses"},
|
||||
{"7.3.1", "Grouping functions"},
|
||||
{"7.3.2", "Normal arithmetic operations"},
|
||||
{"7.3.3", "Bit functions"},
|
||||
{"7.3.4", "Logical operations"},
|
||||
{"7.3.5", "Comparison operators"},
|
||||
{"7.3.6", "String comparison functions"},
|
||||
{"7.3.7", "Control flow functions"},
|
||||
{"7.3.8", "Mathematical functions"},
|
||||
{"7.3.9", "String functions"},
|
||||
{"7.3.10", "Date and time functions"},
|
||||
{"7.3.11", "Miscellaneous functions"},
|
||||
{"7.3.12", "Functions for use with GROUP BY clauses"},
|
||||
{"7.4", "CREATE DATABASE syntax"},
|
||||
{"7.5", "DROP DATABASE syntax"},
|
||||
{"7.6", "CREATE TABLE syntax"},
|
||||
{"7.7", "ALTER TABLE syntax"},
|
||||
{"7.8", "OPTIMIZE TABLE syntax"},
|
||||
{"7.9", "DROP TABLE syntax"},
|
||||
{"7.10", "DELETE syntax"},
|
||||
{"7.11", "SELECT syntax"},
|
||||
{"7.12", "JOIN syntax"},
|
||||
{"7.13", "INSERT syntax"},
|
||||
{"7.14", "REPLACE syntax"},
|
||||
{"7.15", "LOAD DATA INFILE syntax"},
|
||||
{"7.16", "UPDATE syntax"},
|
||||
{"7.17", "USE syntax"},
|
||||
{"7.18", "SHOW syntax (Get information about tables, columns...)"},
|
||||
{"7.19", "EXPLAIN syntax (Get information about a SELECT)"},
|
||||
{"7.20", "DESCRIBE syntax (Get information about columns)"},
|
||||
{"7.21", "LOCK TABLES/UNLOCK TABLES syntax"},
|
||||
{"7.22", "SET OPTION syntax"},
|
||||
{"7.23", "GRANT syntax (Compatibility function)"},
|
||||
{"7.24", "CREATE INDEX syntax (Compatibility function)"},
|
||||
{"7.25", "DROP INDEX syntax (Compatibility function)"},
|
||||
{"7.26", "Comment syntax"},
|
||||
{"7.27", "CREATE FUNCTION/DROP FUNCTION syntax"},
|
||||
{"7.28", "Is MySQL picky about reserved words?"},
|
||||
{"8", "Example SQL queries"},
|
||||
{"8.1", "Queries from twin project"},
|
||||
{"8.1.1", "Find all non-distributed twins"},
|
||||
{"8.1.2", "Show a table on twin pair status"},
|
||||
{"9", "How safe/stable is MySQL?"},
|
||||
{"9.1", "How stable is MySQL?"},
|
||||
{"9.2", "Why are there is so many releases of MySQL?"},
|
||||
{"9.3", "Checking a table for errors"},
|
||||
{"9.4", "How to repair tables"},
|
||||
{"9.5", "Is there anything special to do when upgrading/downgrading MySQL?"},
|
||||
{"9.5.1", "Upgrading from a 3.21 version to 3.22"},
|
||||
{"9.5.2", "Upgrading from a 3.20 version to 3.21"},
|
||||
{"9.5.3", "Upgrading to another architecture"},
|
||||
{"9.6", "Year 2000 compliance"},
|
||||
{"10", "MySQL Server functions"},
|
||||
{"10.1", "What languages are supported by MySQL?"},
|
||||
{"10.1.1", "Character set used for data & sorting"},
|
||||
{"10.2", "The update log"},
|
||||
{"10.3", "How big can MySQL tables be?"},
|
||||
{"11", "Getting maximum performance from MySQL"},
|
||||
{"11.1", "How does one change the size of MySQL buffers?"},
|
||||
{"11.2", "How compiling and linking affects the speed of MySQL"},
|
||||
{"11.3", "How does MySQL use memory?"},
|
||||
{"11.4", "How does MySQL use indexes?"},
|
||||
{"11.5", "What optimizations are done on WHERE clauses?"},
|
||||
{"11.6", "How does MySQL open & close tables?"},
|
||||
{"11.6.0.1", "What are the drawbacks of creating possibly thousands of tables in a database?"},
|
||||
{"11.7", "How does MySQL lock tables?"},
|
||||
{"11.8", "How should I arrange my table to be as fast/small as possible?"},
|
||||
{"11.9", "What affects the speed of INSERT statements?"},
|
||||
{"11.10", "What affects the speed DELETE statements?"},
|
||||
{"11.11", "How do I get MySQL to run at full speed?"},
|
||||
{"11.12", "What are the different row formats? Or, when should VARCHAR/CHAR be used?"},
|
||||
{"11.13", "Why so many open tables?"},
|
||||
{"12", "MySQL benchmark suite"},
|
||||
{"13", "MySQL Utilites"},
|
||||
{"13.1", "Overview of the different MySQL programs"},
|
||||
{"13.2", "The MySQL table check, optimize and repair program"},
|
||||
{"13.2.1", "isamchk memory use"},
|
||||
{"13.2.2", "Getting low-level table information"},
|
||||
{"13.3", "The MySQL compressed read-only table generator"},
|
||||
{"14", "Adding new functions to MySQL"},
|
||||
{"15", "MySQL ODBC Support"},
|
||||
{"15.1", "Operating systems supported by MyODBC"},
|
||||
{"15.2", "How to report problems with MyODBC"},
|
||||
{"15.3", "Programs known to work with MyODBC"},
|
||||
{"15.4", "How to fill in the various fields in the ODBC administrator program"},
|
||||
{"15.5", "How to get the value of an AUTO_INCREMENT column in ODBC"},
|
||||
{"16", "Problems and common errors"},
|
||||
{"16.1", "Some common errors when using MySQL"},
|
||||
{"16.1.1", "MySQL server has gone away error"},
|
||||
{"16.1.2", "Can't connect to local MySQL server error"},
|
||||
{"16.1.3", "Out of memory error"},
|
||||
{"16.1.4", "Packet too large error"},
|
||||
{"16.1.5", "The table is full error"},
|
||||
{"16.1.6", "Commands out of sync error in client"},
|
||||
{"16.1.7", "Removing user error"},
|
||||
{"16.2", "How MySQL handles a full disk"},
|
||||
{"16.3", "How to run SQL commands from a text file"},
|
||||
{"16.4", "Where MySQL stores temporary files"},
|
||||
{"16.5", "Access denied error"},
|
||||
{"16.6", "How to run MySQL as a normal user"},
|
||||
{"16.7", "Problems with file permissions"},
|
||||
{"16.8", "File not found"},
|
||||
{"16.9", "Problems using DATE columns"},
|
||||
{"16.10", "Case sensitivity in searches"},
|
||||
{"16.11", "Problems with NULL values"},
|
||||
{"17", "Solving some common problems with MySQL"},
|
||||
{"17.1", "Database replication"},
|
||||
{"17.2", "Database backups"},
|
||||
{"18", "MySQL client tools and API's"},
|
||||
{"18.1", "MySQL C API"},
|
||||
{"18.2", "C API datatypes"},
|
||||
{"18.3", "C API function overview"},
|
||||
{"18.4", "C API function descriptions"},
|
||||
{"18.4.1", "mysql_affected_rows()"},
|
||||
{"18.4.2", "mysql_close()"},
|
||||
{"18.4.3", "mysql_connect()"},
|
||||
{"18.4.4", "mysql_create_db()"},
|
||||
{"18.4.5", "mysql_data_seek()"},
|
||||
{"18.4.6", "mysql_debug()"},
|
||||
{"18.4.7", "mysql_drop_db()"},
|
||||
{"18.4.8", "mysql_dump_debug_info()"},
|
||||
{"18.4.9", "mysql_eof()"},
|
||||
{"18.4.10", "mysql_errno()"},
|
||||
{"18.4.11", "mysql_error()"},
|
||||
{"18.4.12", "mysql_escape_string()"},
|
||||
{"18.4.13", "mysql_fetch_field()"},
|
||||
{"18.4.14", "mysql_fetch_fields()"},
|
||||
{"18.4.15", "mysql_fetch_field_direct()"},
|
||||
{"18.4.16", "mysql_fetch_lengths()"},
|
||||
{"18.4.17", "mysql_fetch_row()"},
|
||||
{"18.4.18", "mysql_field_seek()"},
|
||||
{"18.4.19", "mysql_field_tell()"},
|
||||
{"18.4.20", "mysql_free_result()"},
|
||||
{"18.4.21", "mysql_get_client_info()"},
|
||||
{"18.4.22", "mysql_get_host_info()"},
|
||||
{"18.4.23", "mysql_get_proto_info()"},
|
||||
{"18.4.24", "mysql_get_server_info()"},
|
||||
{"18.4.25", "mysql_info()"},
|
||||
{"18.4.26", "mysql_init()"},
|
||||
{"18.4.27", "mysql_insert_id()"},
|
||||
{"18.4.28", "mysql_kill()"},
|
||||
{"18.4.29", "mysql_list_dbs()"},
|
||||
{"18.4.30", "mysql_list_fields()"},
|
||||
{"18.4.31", "mysql_list_processes()"},
|
||||
{"18.4.32", "mysql_list_tables()"},
|
||||
{"18.4.33", "mysql_num_fields()"},
|
||||
{"18.4.34", "mysql_num_rows()"},
|
||||
{"18.4.35", "mysql_query()"},
|
||||
{"18.4.36", "mysql_real_connect()"},
|
||||
{"18.4.37", "mysql_real_query()"},
|
||||
{"18.4.38", "mysql_reload()"},
|
||||
{"18.4.39", "mysql_row_tell()"},
|
||||
{"18.4.40", "mysql_select_db()"},
|
||||
{"18.4.41", "mysql_shutdown()"},
|
||||
{"18.4.42", "mysql_stat()"},
|
||||
{"18.4.43", "mysql_store_result()"},
|
||||
{"18.4.44", "mysql_thread_id()"},
|
||||
{"18.4.45", "mysql_use_result()"},
|
||||
{"18.4.46", "Why is it that after mysql_query() returns success, mysql_store_result() sometimes returns NULL?"},
|
||||
{"18.4.47", "What results can I get from a query?"},
|
||||
{"18.4.48", "How can I get the unique ID for the last inserted row?"},
|
||||
{"18.4.49", "Problems linking with the C API"},
|
||||
{"18.4.50", "How to make a thread-safe client"},
|
||||
{"18.5", "MySQL Perl API's"},
|
||||
{"18.5.1", "DBI with DBD::mysql"},
|
||||
{"18.5.1.1", "The DBI interface"},
|
||||
{"18.5.1.2", "More DBI/DBD information"},
|
||||
{"18.6", "MySQL Java connectivity (JDBC)"},
|
||||
{"18.7", "MySQL PHP API's"},
|
||||
{"18.8", "MySQL C++ API's"},
|
||||
{"18.9", "MySQL Python API's"},
|
||||
{"18.10", "MySQL TCL API's"},
|
||||
{"19", "How MySQL compares to other databases"},
|
||||
{"19.1", "How MySQL compares to mSQL"},
|
||||
{"19.1.1", "How to convert mSQL tools for MySQL"},
|
||||
{"19.1.2", "How mSQL and MySQL client/server communications protocols differ"},
|
||||
{"19.1.3", "How mSQL 2.0 SQL syntax differs from MySQL"},
|
||||
{"19.2", "How MySQL compares to PostgreSQL"},
|
||||
{"A", "Some users of MySQL"},
|
||||
{"B", "Contributed programs"},
|
||||
{"C", "Contributors to MySQL"},
|
||||
{"D", "MySQL change history"},
|
||||
{"19.3", "Changes in release 3.22.x (Alpha version)"},
|
||||
{"19.3.1", "Changes in release 3.22.7"},
|
||||
{"19.3.2", "Changes in release 3.22.6"},
|
||||
{"19.3.3", "Changes in release 3.22.5"},
|
||||
{"19.3.4", "Changes in release 3.22.4"},
|
||||
{"19.3.5", "Changes in release 3.22.3"},
|
||||
{"19.3.6", "Changes in release 3.22.2"},
|
||||
{"19.3.7", "Changes in release 3.22.1"},
|
||||
{"19.3.8", "Changes in release 3.22.0"},
|
||||
{"19.4", "Changes in release 3.21.x"},
|
||||
{"19.4.1", "Changes in release 3.21.33"},
|
||||
{"19.4.2", "Changes in release 3.21.32"},
|
||||
{"19.4.3", "Changes in release 3.21.31"},
|
||||
{"19.4.4", "Changes in release 3.21.30"},
|
||||
{"19.4.5", "Changes in release 3.21.29"},
|
||||
{"19.4.6", "Changes in release 3.21.28"},
|
||||
{"19.4.7", "Changes in release 3.21.27"},
|
||||
{"19.4.8", "Changes in release 3.21.26"},
|
||||
{"19.4.9", "Changes in release 3.21.25"},
|
||||
{"19.4.10", "Changes in release 3.21.24"},
|
||||
{"19.4.11", "Changes in release 3.21.23"},
|
||||
{"19.4.12", "Changes in release 3.21.22"},
|
||||
{"19.4.13", "Changes in release 3.21.21a"},
|
||||
{"19.4.14", "Changes in release 3.21.21"},
|
||||
{"19.4.15", "Changes in release 3.21.20"},
|
||||
{"19.4.16", "Changes in release 3.21.19"},
|
||||
{"19.4.17", "Changes in release 3.21.18"},
|
||||
{"19.4.18", "Changes in release 3.21.17"},
|
||||
{"19.4.19", "Changes in release 3.21.16"},
|
||||
{"19.4.20", "Changes in release 3.21.15"},
|
||||
{"19.4.21", "Changes in release 3.21.14b"},
|
||||
{"19.4.22", "Changes in release 3.21.14a"},
|
||||
{"19.4.23", "Changes in release 3.21.13"},
|
||||
{"19.4.24", "Changes in release 3.21.12"},
|
||||
{"19.4.25", "Changes in release 3.21.11"},
|
||||
{"19.4.26", "Changes in release 3.21.10"},
|
||||
{"19.4.27", "Changes in release 3.21.9"},
|
||||
{"19.4.28", "Changes in release 3.21.8"},
|
||||
{"19.4.29", "Changes in release 3.21.7"},
|
||||
{"19.4.30", "Changes in release 3.21.6"},
|
||||
{"19.4.31", "Changes in release 3.21.5"},
|
||||
{"19.4.32", "Changes in release 3.21.4"},
|
||||
{"19.4.33", "Changes in release 3.21.3"},
|
||||
{"19.4.34", "Changes in release 3.21.2"},
|
||||
{"19.4.35", "Changes in release 3.21.0"},
|
||||
{"19.5", "Changes in release 3.20.x"},
|
||||
{"19.5.1", "Changes in release 3.20.18"},
|
||||
{"19.5.2", "Changes in release 3.20.17"},
|
||||
{"19.5.3", "Changes in release 3.20.16"},
|
||||
{"19.5.4", "Changes in release 3.20.15"},
|
||||
{"19.5.5", "Changes in release 3.20.14"},
|
||||
{"19.5.6", "Changes in release 3.20.13"},
|
||||
{"19.5.7", "Changes in release 3.20.11"},
|
||||
{"19.5.8", "Changes in release 3.20.10"},
|
||||
{"19.5.9", "Changes in release 3.20.9"},
|
||||
{"19.5.10", "Changes in release 3.20.8"},
|
||||
{"19.5.11", "Changes in release 3.20.7"},
|
||||
{"19.5.12", "Changes in release 3.20.6"},
|
||||
{"19.5.13", "Changes in release 3.20.3"},
|
||||
{"19.5.14", "Changes in release 3.20.0"},
|
||||
{"19.6", "Changes in release 3.19.x"},
|
||||
{"19.6.1", "Changes in release 3.19.5"},
|
||||
{"19.6.2", "Changes in release 3.19.4"},
|
||||
{"19.6.3", "Changes in release 3.19.3"},
|
||||
{"E", "Known errors and design deficiencies in MySQL"},
|
||||
{"F", "List of things we want to add to MySQL in the future (The TODO)"},
|
||||
{"19.7", "Things that must done in the real near future"},
|
||||
{"19.8", "Things that have to be done sometime"},
|
||||
{"19.9", "Some things we don't have any plans to do"},
|
||||
{"G", "Comments on porting to other systems"},
|
||||
{"19.10", "Debugging MySQL"},
|
||||
{"19.11", "Comments about RTS threads"},
|
||||
{"19.12", "What is the difference between different thread packages?"},
|
||||
{"H", "Description of MySQL regular expression syntax"},
|
||||
{"I", "What is Unireg?"},
|
||||
{"J", "The MySQL server license"},
|
||||
{"K", "The MySQL license for Microsoft operating systems"},
|
||||
{"*", "SQL command, type and function index"},
|
||||
{"*", "Concept Index"}
|
||||
};
|
||||
|
||||
#define NQUERIES 5
|
||||
const char *query[NQUERIES]={
|
||||
"mysql information and manual",
|
||||
"upgrading from previous version",
|
||||
"column indexes",
|
||||
"against about after more right the with/without", /* stopwords test */
|
||||
"mysql license and copyright"
|
||||
};
|
||||
352
storage/maria/ma_ft_update.c
Normal file
352
storage/maria/ma_ft_update.c
Normal file
@@ -0,0 +1,352 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
/* functions to work with full-text indices */
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
#include <math.h>
|
||||
|
||||
void _ma_ft_segiterator_init(MARIA_HA *info, uint keynr, const byte *record,
|
||||
FT_SEG_ITERATOR *ftsi)
|
||||
{
|
||||
DBUG_ENTER("_ma_ft_segiterator_init");
|
||||
|
||||
ftsi->num=info->s->keyinfo[keynr].keysegs;
|
||||
ftsi->seg=info->s->keyinfo[keynr].seg;
|
||||
ftsi->rec=record;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void _ma_ft_segiterator_dummy_init(const byte *record, uint len,
|
||||
FT_SEG_ITERATOR *ftsi)
|
||||
{
|
||||
DBUG_ENTER("_ma_ft_segiterator_dummy_init");
|
||||
|
||||
ftsi->num=1;
|
||||
ftsi->seg=0;
|
||||
ftsi->pos=record;
|
||||
ftsi->len=len;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
This function breaks convention "return 0 in success"
|
||||
but it's easier to use like this
|
||||
|
||||
while(_ma_ft_segiterator())
|
||||
|
||||
so "1" means "OK", "0" means "EOF"
|
||||
*/
|
||||
|
||||
uint _ma_ft_segiterator(register FT_SEG_ITERATOR *ftsi)
|
||||
{
|
||||
DBUG_ENTER("_ma_ft_segiterator");
|
||||
|
||||
if (!ftsi->num)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
ftsi->num--;
|
||||
if (!ftsi->seg)
|
||||
DBUG_RETURN(1);
|
||||
|
||||
ftsi->seg--;
|
||||
|
||||
if (ftsi->seg->null_bit &&
|
||||
(ftsi->rec[ftsi->seg->null_pos] & ftsi->seg->null_bit))
|
||||
{
|
||||
ftsi->pos=0;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
ftsi->pos= ftsi->rec+ftsi->seg->start;
|
||||
if (ftsi->seg->flag & HA_VAR_LENGTH_PART)
|
||||
{
|
||||
uint pack_length= (ftsi->seg->bit_start);
|
||||
ftsi->len= (pack_length == 1 ? (uint) *(uchar*) ftsi->pos :
|
||||
uint2korr(ftsi->pos));
|
||||
ftsi->pos+= pack_length; /* Skip VARCHAR length */
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
if (ftsi->seg->flag & HA_BLOB_PART)
|
||||
{
|
||||
ftsi->len= _ma_calc_blob_length(ftsi->seg->bit_start,ftsi->pos);
|
||||
memcpy_fixed((char*) &ftsi->pos, ftsi->pos+ftsi->seg->bit_start,
|
||||
sizeof(char*));
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
ftsi->len=ftsi->seg->length;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
|
||||
/* parses a document i.e. calls maria_ft_parse for every keyseg */
|
||||
|
||||
uint _ma_ft_parse(TREE *parsed, MARIA_HA *info, uint keynr, const byte *record,
|
||||
MYSQL_FTPARSER_PARAM *param, MEM_ROOT *mem_root)
|
||||
{
|
||||
FT_SEG_ITERATOR ftsi;
|
||||
struct st_mysql_ftparser *parser;
|
||||
DBUG_ENTER("_ma_ft_parse");
|
||||
|
||||
_ma_ft_segiterator_init(info, keynr, record, &ftsi);
|
||||
|
||||
maria_ft_parse_init(parsed, info->s->keyinfo[keynr].seg->charset);
|
||||
parser= info->s->keyinfo[keynr].parser;
|
||||
while (_ma_ft_segiterator(&ftsi))
|
||||
{
|
||||
if (ftsi.pos)
|
||||
if (maria_ft_parse(parsed, (byte *)ftsi.pos, ftsi.len, parser, param,
|
||||
mem_root))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
FT_WORD * _ma_ft_parserecord(MARIA_HA *info, uint keynr, const byte *record,
|
||||
MEM_ROOT *mem_root)
|
||||
{
|
||||
TREE ptree;
|
||||
MYSQL_FTPARSER_PARAM *param;
|
||||
DBUG_ENTER("_ma_ft_parserecord");
|
||||
if (! (param= maria_ftparser_call_initializer(info, keynr, 0)))
|
||||
DBUG_RETURN(NULL);
|
||||
bzero((char*) &ptree, sizeof(ptree));
|
||||
param->flags= 0;
|
||||
if (_ma_ft_parse(&ptree, info, keynr, record, param, mem_root))
|
||||
DBUG_RETURN(NULL);
|
||||
|
||||
DBUG_RETURN(maria_ft_linearize(&ptree, mem_root));
|
||||
}
|
||||
|
||||
static int _ma_ft_store(MARIA_HA *info, uint keynr, byte *keybuf,
|
||||
FT_WORD *wlist, my_off_t filepos)
|
||||
{
|
||||
uint key_length;
|
||||
DBUG_ENTER("_ma_ft_store");
|
||||
|
||||
for (; wlist->pos; wlist++)
|
||||
{
|
||||
key_length= _ma_ft_make_key(info,keynr,keybuf,wlist,filepos);
|
||||
if (_ma_ck_write(info,keynr,(uchar*) keybuf,key_length))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
static int _ma_ft_erase(MARIA_HA *info, uint keynr, byte *keybuf,
|
||||
FT_WORD *wlist, my_off_t filepos)
|
||||
{
|
||||
uint key_length, err=0;
|
||||
DBUG_ENTER("_ma_ft_erase");
|
||||
|
||||
for (; wlist->pos; wlist++)
|
||||
{
|
||||
key_length= _ma_ft_make_key(info,keynr,keybuf,wlist,filepos);
|
||||
if (_ma_ck_delete(info,keynr,(uchar*) keybuf,key_length))
|
||||
err=1;
|
||||
}
|
||||
DBUG_RETURN(err);
|
||||
}
|
||||
|
||||
/*
|
||||
Compares an appropriate parts of two WORD_KEY keys directly out of records
|
||||
returns 1 if they are different
|
||||
*/
|
||||
|
||||
#define THOSE_TWO_DAMN_KEYS_ARE_REALLY_DIFFERENT 1
|
||||
#define GEE_THEY_ARE_ABSOLUTELY_IDENTICAL 0
|
||||
|
||||
int _ma_ft_cmp(MARIA_HA *info, uint keynr, const byte *rec1, const byte *rec2)
|
||||
{
|
||||
FT_SEG_ITERATOR ftsi1, ftsi2;
|
||||
CHARSET_INFO *cs=info->s->keyinfo[keynr].seg->charset;
|
||||
DBUG_ENTER("_ma_ft_cmp");
|
||||
|
||||
_ma_ft_segiterator_init(info, keynr, rec1, &ftsi1);
|
||||
_ma_ft_segiterator_init(info, keynr, rec2, &ftsi2);
|
||||
|
||||
while (_ma_ft_segiterator(&ftsi1) && _ma_ft_segiterator(&ftsi2))
|
||||
{
|
||||
if ((ftsi1.pos != ftsi2.pos) &&
|
||||
(!ftsi1.pos || !ftsi2.pos ||
|
||||
ha_compare_text(cs, (uchar*) ftsi1.pos,ftsi1.len,
|
||||
(uchar*) ftsi2.pos,ftsi2.len,0,0)))
|
||||
DBUG_RETURN(THOSE_TWO_DAMN_KEYS_ARE_REALLY_DIFFERENT);
|
||||
}
|
||||
DBUG_RETURN(GEE_THEY_ARE_ABSOLUTELY_IDENTICAL);
|
||||
}
|
||||
|
||||
|
||||
/* update a document entry */
|
||||
|
||||
int _ma_ft_update(MARIA_HA *info, uint keynr, byte *keybuf,
|
||||
const byte *oldrec, const byte *newrec, my_off_t pos)
|
||||
{
|
||||
int error= -1;
|
||||
FT_WORD *oldlist,*newlist, *old_word, *new_word;
|
||||
CHARSET_INFO *cs=info->s->keyinfo[keynr].seg->charset;
|
||||
uint key_length;
|
||||
int cmp, cmp2;
|
||||
DBUG_ENTER("_ma_ft_update");
|
||||
|
||||
if (!(old_word=oldlist=_ma_ft_parserecord(info, keynr, oldrec,
|
||||
&info->ft_memroot)) ||
|
||||
!(new_word=newlist=_ma_ft_parserecord(info, keynr, newrec,
|
||||
&info->ft_memroot)))
|
||||
goto err;
|
||||
|
||||
error=0;
|
||||
while(old_word->pos && new_word->pos)
|
||||
{
|
||||
cmp= ha_compare_text(cs, (uchar*) old_word->pos,old_word->len,
|
||||
(uchar*) new_word->pos,new_word->len,0,0);
|
||||
cmp2= cmp ? 0 : (fabs(old_word->weight - new_word->weight) > 1.e-5);
|
||||
|
||||
if (cmp < 0 || cmp2)
|
||||
{
|
||||
key_length= _ma_ft_make_key(info,keynr,keybuf,old_word,pos);
|
||||
if ((error= _ma_ck_delete(info,keynr,(uchar*) keybuf,key_length)))
|
||||
goto err;
|
||||
}
|
||||
if (cmp > 0 || cmp2)
|
||||
{
|
||||
key_length= _ma_ft_make_key(info,keynr,keybuf,new_word,pos);
|
||||
if ((error= _ma_ck_write(info,keynr,(uchar*) keybuf,key_length)))
|
||||
goto err;
|
||||
}
|
||||
if (cmp<=0) old_word++;
|
||||
if (cmp>=0) new_word++;
|
||||
}
|
||||
if (old_word->pos)
|
||||
error= _ma_ft_erase(info,keynr,keybuf,old_word,pos);
|
||||
else if (new_word->pos)
|
||||
error= _ma_ft_store(info,keynr,keybuf,new_word,pos);
|
||||
|
||||
err:
|
||||
free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE));
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/* adds a document to the collection */
|
||||
|
||||
int _ma_ft_add(MARIA_HA *info, uint keynr, byte *keybuf, const byte *record,
|
||||
my_off_t pos)
|
||||
{
|
||||
int error= -1;
|
||||
FT_WORD *wlist;
|
||||
DBUG_ENTER("_ma_ft_add");
|
||||
DBUG_PRINT("enter",("keynr: %d",keynr));
|
||||
|
||||
if ((wlist= _ma_ft_parserecord(info, keynr, record, &info->ft_memroot)))
|
||||
error= _ma_ft_store(info,keynr,keybuf,wlist,pos);
|
||||
free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE));
|
||||
DBUG_PRINT("exit",("Return: %d",error));
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/* removes a document from the collection */
|
||||
|
||||
int _ma_ft_del(MARIA_HA *info, uint keynr, byte *keybuf, const byte *record,
|
||||
my_off_t pos)
|
||||
{
|
||||
int error= -1;
|
||||
FT_WORD *wlist;
|
||||
DBUG_ENTER("_ma_ft_del");
|
||||
DBUG_PRINT("enter",("keynr: %d",keynr));
|
||||
|
||||
if ((wlist= _ma_ft_parserecord(info, keynr, record, &info->ft_memroot)))
|
||||
error= _ma_ft_erase(info,keynr,keybuf,wlist,pos);
|
||||
free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE));
|
||||
DBUG_PRINT("exit",("Return: %d",error));
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
uint _ma_ft_make_key(MARIA_HA *info, uint keynr, byte *keybuf, FT_WORD *wptr,
|
||||
my_off_t filepos)
|
||||
{
|
||||
byte buf[HA_FT_MAXBYTELEN+16];
|
||||
DBUG_ENTER("_ma_ft_make_key");
|
||||
|
||||
#if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
|
||||
{
|
||||
float weight=(float) ((filepos==HA_OFFSET_ERROR) ? 0 : wptr->weight);
|
||||
mi_float4store(buf,weight);
|
||||
}
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
|
||||
int2store(buf+HA_FT_WLEN,wptr->len);
|
||||
memcpy(buf+HA_FT_WLEN+2,wptr->pos,wptr->len);
|
||||
DBUG_RETURN(_ma_make_key(info,keynr,(uchar*) keybuf,buf,filepos));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
convert key value to ft2
|
||||
*/
|
||||
|
||||
uint _ma_ft_convert_to_ft2(MARIA_HA *info, uint keynr, uchar *key)
|
||||
{
|
||||
my_off_t root;
|
||||
DYNAMIC_ARRAY *da=info->ft1_to_ft2;
|
||||
MARIA_KEYDEF *keyinfo=&info->s->ft2_keyinfo;
|
||||
uchar *key_ptr= (uchar*) dynamic_array_ptr(da, 0), *end;
|
||||
uint length, key_length;
|
||||
DBUG_ENTER("_ma_ft_convert_to_ft2");
|
||||
|
||||
/* we'll generate one pageful at once, and insert the rest one-by-one */
|
||||
/* calculating the length of this page ...*/
|
||||
length=(keyinfo->block_length-2) / keyinfo->keylength;
|
||||
set_if_smaller(length, da->elements);
|
||||
length=length * keyinfo->keylength;
|
||||
|
||||
get_key_full_length_rdonly(key_length, key);
|
||||
while (_ma_ck_delete(info, keynr, key, key_length) == 0)
|
||||
{
|
||||
/*
|
||||
nothing to do here.
|
||||
_ma_ck_delete() will populate info->ft1_to_ft2 with deleted keys
|
||||
*/
|
||||
}
|
||||
|
||||
/* creating pageful of keys */
|
||||
maria_putint(info->buff,length+2,0);
|
||||
memcpy(info->buff+2, key_ptr, length);
|
||||
info->buff_used=info->page_changed=1; /* info->buff is used */
|
||||
if ((root= _ma_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR ||
|
||||
_ma_write_keypage(info,keyinfo,root,DFLT_INIT_HITS,info->buff))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* inserting the rest of key values */
|
||||
end= (uchar*) dynamic_array_ptr(da, da->elements);
|
||||
for (key_ptr+=length; key_ptr < end; key_ptr+=keyinfo->keylength)
|
||||
if(_ma_ck_real_write_btree(info, keyinfo, key_ptr, 0, &root, SEARCH_SAME))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
/* now, writing the word key entry */
|
||||
ft_intXstore(key+key_length, - (int) da->elements);
|
||||
_ma_dpointer(info, key+key_length+HA_FT_WLEN, root);
|
||||
|
||||
DBUG_RETURN(_ma_ck_real_write_btree(info,
|
||||
info->s->keyinfo+keynr,
|
||||
key, 0,
|
||||
&info->s->state.key_root[keynr],
|
||||
SEARCH_SAME));
|
||||
}
|
||||
153
storage/maria/ma_ftdefs.h
Normal file
153
storage/maria/ma_ftdefs.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
/* some definitions for full-text indices */
|
||||
|
||||
#include "ma_fulltext.h"
|
||||
#include <m_ctype.h>
|
||||
#include <my_tree.h>
|
||||
#include <queues.h>
|
||||
#include <mysql/plugin.h>
|
||||
|
||||
#define true_word_char(ctype, character) \
|
||||
((ctype) & (_MY_U | _MY_L | _MY_NMR) || \
|
||||
(character) == '_')
|
||||
#define misc_word_char(X) 0
|
||||
|
||||
#define FT_MAX_WORD_LEN_FOR_SORT 31
|
||||
|
||||
#define FTPARSER_MEMROOT_ALLOC_SIZE 65536
|
||||
|
||||
#define COMPILE_STOPWORDS_IN
|
||||
|
||||
/* Interested readers may consult SMART
|
||||
(ftp://ftp.cs.cornell.edu/pub/smart/smart.11.0.tar.Z)
|
||||
for an excellent implementation of vector space model we use.
|
||||
It also demonstrate the usage of different weghting techniques.
|
||||
This code, though, is completely original and is not based on the
|
||||
SMART code but was in some cases inspired by it.
|
||||
|
||||
NORM_PIVOT was taken from the article
|
||||
A.Singhal, C.Buckley, M.Mitra, "Pivoted Document Length Normalization",
|
||||
ACM SIGIR'96, 21-29, 1996
|
||||
*/
|
||||
|
||||
#define LWS_FOR_QUERY LWS_TF
|
||||
#define LWS_IN_USE LWS_LOG
|
||||
#define PRENORM_IN_USE PRENORM_AVG
|
||||
#define NORM_IN_USE NORM_PIVOT
|
||||
#define GWS_IN_USE GWS_PROB
|
||||
/*==============================================================*/
|
||||
#define LWS_TF (count)
|
||||
#define LWS_BINARY (count>0)
|
||||
#define LWS_SQUARE (count*count)
|
||||
#define LWS_LOG (count?(log( (double) count)+1):0)
|
||||
/*--------------------------------------------------------------*/
|
||||
#define PRENORM_NONE (p->weight)
|
||||
#define PRENORM_MAX (p->weight/docstat.max)
|
||||
#define PRENORM_AUG (0.4+0.6*p->weight/docstat.max)
|
||||
#define PRENORM_AVG (p->weight/docstat.sum*docstat.uniq)
|
||||
#define PRENORM_AVGLOG ((1+log(p->weight))/(1+log(docstat.sum/docstat.uniq)))
|
||||
/*--------------------------------------------------------------*/
|
||||
#define NORM_NONE (1)
|
||||
#define NORM_SUM (docstat.nsum)
|
||||
#define NORM_COS (sqrt(docstat.nsum2))
|
||||
|
||||
#define PIVOT_VAL (0.0115)
|
||||
#define NORM_PIVOT (1+PIVOT_VAL*docstat.uniq)
|
||||
/*---------------------------------------------------------------*/
|
||||
#define GWS_NORM (1/sqrt(sum2))
|
||||
#define GWS_GFIDF (sum/doc_cnt)
|
||||
/* Mysterious, but w/o (double) GWS_IDF performs better :-o */
|
||||
#define GWS_IDF log(aio->info->state->records/doc_cnt)
|
||||
#define GWS_IDF1 log((double)aio->info->state->records/doc_cnt)
|
||||
#define GWS_PROB ((aio->info->state->records > doc_cnt) ? log(((double)(aio->info->state->records-doc_cnt))/doc_cnt) : 0 )
|
||||
#define GWS_FREQ (1.0/doc_cnt)
|
||||
#define GWS_SQUARED pow(log((double)aio->info->state->records/doc_cnt),2)
|
||||
#define GWS_CUBIC pow(log((double)aio->info->state->records/doc_cnt),3)
|
||||
#define GWS_ENTROPY (1-(suml/sum-log(sum))/log(aio->info->state->records))
|
||||
/*=================================================================*/
|
||||
|
||||
/* Boolean search operators */
|
||||
#define FTB_YES (ft_boolean_syntax[0])
|
||||
#define FTB_EGAL (ft_boolean_syntax[1])
|
||||
#define FTB_NO (ft_boolean_syntax[2])
|
||||
#define FTB_INC (ft_boolean_syntax[3])
|
||||
#define FTB_DEC (ft_boolean_syntax[4])
|
||||
#define FTB_LBR (ft_boolean_syntax[5])
|
||||
#define FTB_RBR (ft_boolean_syntax[6])
|
||||
#define FTB_NEG (ft_boolean_syntax[7])
|
||||
#define FTB_TRUNC (ft_boolean_syntax[8])
|
||||
#define FTB_LQUOT (ft_boolean_syntax[10])
|
||||
#define FTB_RQUOT (ft_boolean_syntax[11])
|
||||
|
||||
typedef struct st_maria_ft_word {
|
||||
byte * pos;
|
||||
uint len;
|
||||
double weight;
|
||||
} FT_WORD;
|
||||
|
||||
int is_stopword(char *word, uint len);
|
||||
|
||||
uint _ma_ft_make_key(MARIA_HA *, uint , byte *, FT_WORD *, my_off_t);
|
||||
|
||||
byte maria_ft_get_word(CHARSET_INFO *, byte **, byte *, FT_WORD *,
|
||||
MYSQL_FTPARSER_BOOLEAN_INFO *);
|
||||
byte maria_ft_simple_get_word(CHARSET_INFO *, byte **, const byte *,
|
||||
FT_WORD *, my_bool);
|
||||
|
||||
typedef struct _st_maria_ft_seg_iterator {
|
||||
uint num, len;
|
||||
HA_KEYSEG *seg;
|
||||
const byte *rec, *pos;
|
||||
} FT_SEG_ITERATOR;
|
||||
|
||||
void _ma_ft_segiterator_init(MARIA_HA *, uint, const byte *, FT_SEG_ITERATOR *);
|
||||
void _ma_ft_segiterator_dummy_init(const byte *, uint, FT_SEG_ITERATOR *);
|
||||
uint _ma_ft_segiterator(FT_SEG_ITERATOR *);
|
||||
|
||||
void maria_ft_parse_init(TREE *, CHARSET_INFO *);
|
||||
int maria_ft_parse(TREE *, byte *, int, struct st_mysql_ftparser *parser,
|
||||
MYSQL_FTPARSER_PARAM *, MEM_ROOT *);
|
||||
FT_WORD * maria_ft_linearize(TREE *, MEM_ROOT *);
|
||||
FT_WORD * _ma_ft_parserecord(MARIA_HA *, uint, const byte *, MEM_ROOT *);
|
||||
uint _ma_ft_parse(TREE *, MARIA_HA *, uint, const byte *,
|
||||
MYSQL_FTPARSER_PARAM *, MEM_ROOT *);
|
||||
|
||||
FT_INFO *maria_ft_init_nlq_search(MARIA_HA *, uint, byte *, uint, uint, byte *);
|
||||
FT_INFO *maria_ft_init_boolean_search(MARIA_HA *, uint, byte *, uint, CHARSET_INFO *);
|
||||
|
||||
extern const struct _ft_vft _ma_ft_vft_nlq;
|
||||
int maria_ft_nlq_read_next(FT_INFO *, char *);
|
||||
float maria_ft_nlq_find_relevance(FT_INFO *, byte *, uint);
|
||||
void maria_ft_nlq_close_search(FT_INFO *);
|
||||
float maria_ft_nlq_get_relevance(FT_INFO *);
|
||||
my_off_t maria_ft_nlq_get_docid(FT_INFO *);
|
||||
void maria_ft_nlq_reinit_search(FT_INFO *);
|
||||
|
||||
extern const struct _ft_vft _ma_ft_vft_boolean;
|
||||
int maria_ft_boolean_read_next(FT_INFO *, char *);
|
||||
float maria_ft_boolean_find_relevance(FT_INFO *, byte *, uint);
|
||||
void maria_ft_boolean_close_search(FT_INFO *);
|
||||
float maria_ft_boolean_get_relevance(FT_INFO *);
|
||||
my_off_t maria_ft_boolean_get_docid(FT_INFO *);
|
||||
void maria_ft_boolean_reinit_search(FT_INFO *);
|
||||
extern MYSQL_FTPARSER_PARAM *maria_ftparser_call_initializer(MARIA_HA *info,
|
||||
uint keynr,
|
||||
uint paramnr);
|
||||
extern void maria_ftparser_call_deinitializer(MARIA_HA *info);
|
||||
28
storage/maria/ma_fulltext.h
Normal file
28
storage/maria/ma_fulltext.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
|
||||
|
||||
/* some definitions for full-text indices */
|
||||
|
||||
#include "maria_def.h"
|
||||
#include "ft_global.h"
|
||||
|
||||
int _ma_ft_cmp(MARIA_HA *, uint, const byte *, const byte *);
|
||||
int _ma_ft_add(MARIA_HA *, uint, byte *, const byte *, my_off_t);
|
||||
int _ma_ft_del(MARIA_HA *, uint, byte *, const byte *, my_off_t);
|
||||
|
||||
uint _ma_ft_convert_to_ft2(MARIA_HA *, uint, uchar *);
|
||||
133
storage/maria/ma_info.c
Normal file
133
storage/maria/ma_info.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Return useful base information for an open table */
|
||||
|
||||
#include "maria_def.h"
|
||||
#ifdef __WIN__
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
/* Get position to last record */
|
||||
|
||||
my_off_t maria_position(MARIA_HA *info)
|
||||
{
|
||||
return info->lastpos;
|
||||
}
|
||||
|
||||
|
||||
/* Get information about the table */
|
||||
/* if flag == 2 one get current info (no sync from database */
|
||||
|
||||
int maria_status(MARIA_HA *info, register MARIA_INFO *x, uint flag)
|
||||
{
|
||||
MY_STAT state;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_status");
|
||||
|
||||
x->recpos = info->lastpos;
|
||||
if (flag == HA_STATUS_POS)
|
||||
DBUG_RETURN(0); /* Compatible with ISAM */
|
||||
if (!(flag & HA_STATUS_NO_LOCK))
|
||||
{
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
VOID(_ma_readinfo(info,F_RDLCK,0));
|
||||
fast_ma_writeinfo(info);
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
}
|
||||
if (flag & HA_STATUS_VARIABLE)
|
||||
{
|
||||
x->records = info->state->records;
|
||||
x->deleted = info->state->del;
|
||||
x->delete_length = info->state->empty;
|
||||
x->data_file_length =info->state->data_file_length;
|
||||
x->index_file_length=info->state->key_file_length;
|
||||
|
||||
x->keys = share->state.header.keys;
|
||||
x->check_time = share->state.check_time;
|
||||
x->mean_reclength = info->state->records ?
|
||||
(ulong) ((info->state->data_file_length-info->state->empty)/
|
||||
info->state->records) : (ulong) share->min_pack_length;
|
||||
}
|
||||
if (flag & HA_STATUS_ERRKEY)
|
||||
{
|
||||
x->errkey = info->errkey;
|
||||
x->dupp_key_pos= info->dupp_key_pos;
|
||||
}
|
||||
if (flag & HA_STATUS_CONST)
|
||||
{
|
||||
x->reclength = share->base.reclength;
|
||||
x->max_data_file_length=share->base.max_data_file_length;
|
||||
x->max_index_file_length=info->s->base.max_key_file_length;
|
||||
x->filenr = info->dfile;
|
||||
x->options = share->options;
|
||||
x->create_time=share->state.create_time;
|
||||
x->reflength= maria_get_pointer_length(share->base.max_data_file_length,
|
||||
maria_data_pointer_size);
|
||||
x->record_offset= ((share->options &
|
||||
(HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ?
|
||||
0L : share->base.pack_reclength);
|
||||
x->sortkey= -1; /* No clustering */
|
||||
x->rec_per_key = share->state.rec_per_key_part;
|
||||
x->key_map = share->state.key_map;
|
||||
x->data_file_name = share->data_file_name;
|
||||
x->index_file_name = share->index_file_name;
|
||||
}
|
||||
if ((flag & HA_STATUS_TIME) && !my_fstat(info->dfile,&state,MYF(0)))
|
||||
x->update_time=state.st_mtime;
|
||||
else
|
||||
x->update_time=0;
|
||||
if (flag & HA_STATUS_AUTO)
|
||||
{
|
||||
x->auto_increment= share->state.auto_increment+1;
|
||||
if (!x->auto_increment) /* This shouldn't happen */
|
||||
x->auto_increment= ~(ulonglong) 0;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Write a message to the error log.
|
||||
|
||||
SYNOPSIS
|
||||
_ma_report_error()
|
||||
file_name Name of table file (e.g. index_file_name).
|
||||
errcode Error number.
|
||||
|
||||
DESCRIPTION
|
||||
This function supplies my_error() with a table name. Most error
|
||||
messages need one. Since string arguments in error messages are limited
|
||||
to 64 characters by convention, we ensure that in case of truncation,
|
||||
that the end of the index file path is in the message. This contains
|
||||
the most valuable information (the table name and the database name).
|
||||
|
||||
RETURN
|
||||
void
|
||||
*/
|
||||
|
||||
void _ma_report_error(int errcode, const char *file_name)
|
||||
{
|
||||
size_t lgt;
|
||||
DBUG_ENTER("_ma_report_error");
|
||||
DBUG_PRINT("enter",("errcode %d, table '%s'", errcode, file_name));
|
||||
|
||||
if ((lgt= strlen(file_name)) > 64)
|
||||
file_name+= lgt - 64;
|
||||
my_error(errcode, MYF(ME_NOREFRESH), file_name);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
58
storage/maria/ma_init.c
Normal file
58
storage/maria/ma_init.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Initialize an maria-database */
|
||||
|
||||
#include "maria_def.h"
|
||||
#include <ft_global.h>
|
||||
|
||||
static int maria_inited= 0;
|
||||
pthread_mutex_t THR_LOCK_maria;
|
||||
|
||||
/*
|
||||
Initialize maria
|
||||
|
||||
SYNOPSIS
|
||||
maria_init()
|
||||
|
||||
TODO
|
||||
Open log files and do recovery if need
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
# error number
|
||||
*/
|
||||
|
||||
int maria_init(void)
|
||||
{
|
||||
if (!maria_inited)
|
||||
{
|
||||
maria_inited= 1;
|
||||
pthread_mutex_init(&THR_LOCK_maria,MY_MUTEX_INIT_SLOW);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void maria_end(void)
|
||||
{
|
||||
if (maria_inited)
|
||||
{
|
||||
maria_inited= 0;
|
||||
ft_free_stopwords();
|
||||
pthread_mutex_destroy(&THR_LOCK_maria);
|
||||
}
|
||||
}
|
||||
590
storage/maria/ma_key.c
Normal file
590
storage/maria/ma_key.c
Normal file
@@ -0,0 +1,590 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Functions to handle keys */
|
||||
|
||||
#include "maria_def.h"
|
||||
#include "m_ctype.h"
|
||||
#include "ma_sp_defs.h"
|
||||
#ifdef HAVE_IEEEFP_H
|
||||
#include <ieeefp.h>
|
||||
#endif
|
||||
|
||||
#define CHECK_KEYS /* Enable safety checks */
|
||||
|
||||
#define FIX_LENGTH(cs, pos, length, char_length) \
|
||||
do { \
|
||||
if (length > char_length) \
|
||||
char_length= my_charpos(cs, pos, pos+length, char_length); \
|
||||
set_if_smaller(char_length,length); \
|
||||
} while(0)
|
||||
|
||||
static int _ma_put_key_in_record(MARIA_HA *info,uint keynr,byte *record);
|
||||
|
||||
/*
|
||||
Make a intern key from a record
|
||||
|
||||
SYNOPSIS
|
||||
_ma_make_key()
|
||||
info MyiSAM handler
|
||||
keynr key number
|
||||
key Store created key here
|
||||
record Record
|
||||
filepos Position to record in the data file
|
||||
|
||||
RETURN
|
||||
Length of key
|
||||
*/
|
||||
|
||||
uint _ma_make_key(register MARIA_HA *info, uint keynr, uchar *key,
|
||||
const byte *record, my_off_t filepos)
|
||||
{
|
||||
byte *pos,*end;
|
||||
uchar *start;
|
||||
reg1 HA_KEYSEG *keyseg;
|
||||
my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
|
||||
DBUG_ENTER("_ma_make_key");
|
||||
|
||||
if (info->s->keyinfo[keynr].flag & HA_SPATIAL)
|
||||
{
|
||||
/*
|
||||
TODO: nulls processing
|
||||
*/
|
||||
#ifdef HAVE_SPATIAL
|
||||
DBUG_RETURN(sp_make_key(info,keynr,key,record,filepos));
|
||||
#else
|
||||
DBUG_ASSERT(0); /* maria_open should check that this never happens*/
|
||||
#endif
|
||||
}
|
||||
|
||||
start=key;
|
||||
for (keyseg=info->s->keyinfo[keynr].seg ; keyseg->type ;keyseg++)
|
||||
{
|
||||
enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
|
||||
uint length=keyseg->length;
|
||||
uint char_length;
|
||||
CHARSET_INFO *cs=keyseg->charset;
|
||||
|
||||
if (keyseg->null_bit)
|
||||
{
|
||||
if (record[keyseg->null_pos] & keyseg->null_bit)
|
||||
{
|
||||
*key++= 0; /* NULL in key */
|
||||
continue;
|
||||
}
|
||||
*key++=1; /* Not NULL */
|
||||
}
|
||||
|
||||
char_length= ((!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen :
|
||||
length);
|
||||
|
||||
pos= (byte*) record+keyseg->start;
|
||||
if (type == HA_KEYTYPE_BIT)
|
||||
{
|
||||
if (keyseg->bit_length)
|
||||
{
|
||||
uchar bits= get_rec_bits((uchar*) record + keyseg->bit_pos,
|
||||
keyseg->bit_start, keyseg->bit_length);
|
||||
*key++= bits;
|
||||
length--;
|
||||
}
|
||||
memcpy((byte*) key, pos, length);
|
||||
key+= length;
|
||||
continue;
|
||||
}
|
||||
if (keyseg->flag & HA_SPACE_PACK)
|
||||
{
|
||||
end= pos + length;
|
||||
if (type != HA_KEYTYPE_NUM)
|
||||
{
|
||||
while (end > pos && end[-1] == ' ')
|
||||
end--;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (pos < end && pos[0] == ' ')
|
||||
pos++;
|
||||
}
|
||||
length=(uint) (end-pos);
|
||||
FIX_LENGTH(cs, pos, length, char_length);
|
||||
store_key_length_inc(key,char_length);
|
||||
memcpy((byte*) key,(byte*) pos,(size_t) char_length);
|
||||
key+=char_length;
|
||||
continue;
|
||||
}
|
||||
if (keyseg->flag & HA_VAR_LENGTH_PART)
|
||||
{
|
||||
uint pack_length= (keyseg->bit_start == 1 ? 1 : 2);
|
||||
uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos :
|
||||
uint2korr(pos));
|
||||
pos+= pack_length; /* Skip VARCHAR length */
|
||||
set_if_smaller(length,tmp_length);
|
||||
FIX_LENGTH(cs, pos, length, char_length);
|
||||
store_key_length_inc(key,char_length);
|
||||
memcpy((byte*) key,(byte*) pos,(size_t) char_length);
|
||||
key+= char_length;
|
||||
continue;
|
||||
}
|
||||
else if (keyseg->flag & HA_BLOB_PART)
|
||||
{
|
||||
uint tmp_length= _ma_calc_blob_length(keyseg->bit_start,pos);
|
||||
memcpy_fixed((byte*) &pos,pos+keyseg->bit_start,sizeof(char*));
|
||||
set_if_smaller(length,tmp_length);
|
||||
FIX_LENGTH(cs, pos, length, char_length);
|
||||
store_key_length_inc(key,char_length);
|
||||
memcpy((byte*) key,(byte*) pos,(size_t) char_length);
|
||||
key+= char_length;
|
||||
continue;
|
||||
}
|
||||
else if (keyseg->flag & HA_SWAP_KEY)
|
||||
{ /* Numerical column */
|
||||
#ifdef HAVE_ISNAN
|
||||
if (type == HA_KEYTYPE_FLOAT)
|
||||
{
|
||||
float nr;
|
||||
float4get(nr,pos);
|
||||
if (isnan(nr))
|
||||
{
|
||||
/* Replace NAN with zero */
|
||||
bzero(key,length);
|
||||
key+=length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (type == HA_KEYTYPE_DOUBLE)
|
||||
{
|
||||
double nr;
|
||||
float8get(nr,pos);
|
||||
if (isnan(nr))
|
||||
{
|
||||
bzero(key,length);
|
||||
key+=length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pos+=length;
|
||||
while (length--)
|
||||
{
|
||||
*key++ = *--pos;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
FIX_LENGTH(cs, pos, length, char_length);
|
||||
memcpy((byte*) key, pos, char_length);
|
||||
if (length > char_length)
|
||||
cs->cset->fill(cs, (char*) key+char_length, length-char_length, ' ');
|
||||
key+= length;
|
||||
}
|
||||
_ma_dpointer(info,key,filepos);
|
||||
DBUG_PRINT("exit",("keynr: %d",keynr));
|
||||
DBUG_DUMP("key",(byte*) start,(uint) (key-start)+keyseg->length);
|
||||
DBUG_EXECUTE("key",
|
||||
_ma_print_key(DBUG_FILE,info->s->keyinfo[keynr].seg,start,
|
||||
(uint) (key-start)););
|
||||
DBUG_RETURN((uint) (key-start)); /* Return keylength */
|
||||
} /* _ma_make_key */
|
||||
|
||||
|
||||
/*
|
||||
Pack a key to intern format from given format (c_rkey)
|
||||
|
||||
SYNOPSIS
|
||||
_ma_pack_key()
|
||||
info MARIA handler
|
||||
uint keynr key number
|
||||
key Store packed key here
|
||||
old Not packed key
|
||||
k_length Length of 'old' to use
|
||||
last_used_keyseg out parameter. May be NULL
|
||||
|
||||
RETURN
|
||||
length of packed key
|
||||
|
||||
last_use_keyseg Store pointer to the keyseg after the last used one
|
||||
*/
|
||||
|
||||
uint _ma_pack_key(register MARIA_HA *info, uint keynr, uchar *key, uchar *old,
|
||||
uint k_length, HA_KEYSEG **last_used_keyseg)
|
||||
{
|
||||
uchar *start_key=key;
|
||||
HA_KEYSEG *keyseg;
|
||||
my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT;
|
||||
DBUG_ENTER("_ma_pack_key");
|
||||
|
||||
for (keyseg=info->s->keyinfo[keynr].seg ;
|
||||
keyseg->type && (int) k_length > 0;
|
||||
old+=keyseg->length, keyseg++)
|
||||
{
|
||||
enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
|
||||
uint length=min((uint) keyseg->length,(uint) k_length);
|
||||
uint char_length;
|
||||
uchar *pos;
|
||||
CHARSET_INFO *cs=keyseg->charset;
|
||||
|
||||
if (keyseg->null_bit)
|
||||
{
|
||||
k_length--;
|
||||
if (!(*key++= (char) 1-*old++)) /* Copy null marker */
|
||||
{
|
||||
k_length-=length;
|
||||
if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
|
||||
{
|
||||
k_length-=2; /* Skip length */
|
||||
old+= 2;
|
||||
}
|
||||
continue; /* Found NULL */
|
||||
}
|
||||
}
|
||||
char_length= (!is_ft && cs && cs->mbmaxlen > 1) ? length/cs->mbmaxlen : length;
|
||||
pos=old;
|
||||
if (keyseg->flag & HA_SPACE_PACK)
|
||||
{
|
||||
uchar *end=pos+length;
|
||||
if (type != HA_KEYTYPE_NUM)
|
||||
{
|
||||
while (end > pos && end[-1] == ' ')
|
||||
end--;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (pos < end && pos[0] == ' ')
|
||||
pos++;
|
||||
}
|
||||
k_length-=length;
|
||||
length=(uint) (end-pos);
|
||||
FIX_LENGTH(cs, pos, length, char_length);
|
||||
store_key_length_inc(key,char_length);
|
||||
memcpy((byte*) key,pos,(size_t) char_length);
|
||||
key+= char_length;
|
||||
continue;
|
||||
}
|
||||
else if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART))
|
||||
{
|
||||
/* Length of key-part used with maria_rkey() always 2 */
|
||||
uint tmp_length=uint2korr(pos);
|
||||
k_length-= 2+length;
|
||||
pos+=2;
|
||||
set_if_smaller(length,tmp_length); /* Safety */
|
||||
FIX_LENGTH(cs, pos, length, char_length);
|
||||
store_key_length_inc(key,char_length);
|
||||
old+=2; /* Skip length */
|
||||
memcpy((byte*) key, pos,(size_t) char_length);
|
||||
key+= char_length;
|
||||
continue;
|
||||
}
|
||||
else if (keyseg->flag & HA_SWAP_KEY)
|
||||
{ /* Numerical column */
|
||||
pos+=length;
|
||||
k_length-=length;
|
||||
while (length--)
|
||||
{
|
||||
*key++ = *--pos;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
FIX_LENGTH(cs, pos, length, char_length);
|
||||
memcpy((byte*) key, pos, char_length);
|
||||
if (length > char_length)
|
||||
cs->cset->fill(cs, (char*) key+char_length, length-char_length, ' ');
|
||||
key+= length;
|
||||
k_length-=length;
|
||||
}
|
||||
if (last_used_keyseg)
|
||||
*last_used_keyseg= keyseg;
|
||||
|
||||
#ifdef NOT_USED
|
||||
if (keyseg->type)
|
||||
{
|
||||
/* Part-key ; fill with ASCII 0 for easier searching */
|
||||
length= (uint) -k_length; /* unused part of last key */
|
||||
do
|
||||
{
|
||||
if (keyseg->flag & HA_NULL_PART)
|
||||
length++;
|
||||
if (keyseg->flag & HA_SPACE_PACK)
|
||||
length+=2;
|
||||
else
|
||||
length+= keyseg->length;
|
||||
keyseg++;
|
||||
} while (keyseg->type);
|
||||
bzero((byte*) key,length);
|
||||
key+=length;
|
||||
}
|
||||
#endif
|
||||
DBUG_RETURN((uint) (key-start_key));
|
||||
} /* _ma_pack_key */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Store found key in record
|
||||
|
||||
SYNOPSIS
|
||||
_ma_put_key_in_record()
|
||||
info MARIA handler
|
||||
keynr Key number that was used
|
||||
record Store key here
|
||||
|
||||
Last read key is in info->lastkey
|
||||
|
||||
NOTES
|
||||
Used when only-keyread is wanted
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
1 error
|
||||
*/
|
||||
|
||||
static int _ma_put_key_in_record(register MARIA_HA *info, uint keynr,
|
||||
byte *record)
|
||||
{
|
||||
reg2 byte *key;
|
||||
byte *pos,*key_end;
|
||||
reg1 HA_KEYSEG *keyseg;
|
||||
byte *blob_ptr;
|
||||
DBUG_ENTER("_ma_put_key_in_record");
|
||||
|
||||
blob_ptr= (byte*) info->lastkey2; /* Place to put blob parts */
|
||||
key=(byte*) info->lastkey; /* KEy that was read */
|
||||
key_end=key+info->lastkey_length;
|
||||
for (keyseg=info->s->keyinfo[keynr].seg ; keyseg->type ;keyseg++)
|
||||
{
|
||||
if (keyseg->null_bit)
|
||||
{
|
||||
if (!*key++)
|
||||
{
|
||||
record[keyseg->null_pos]|= keyseg->null_bit;
|
||||
continue;
|
||||
}
|
||||
record[keyseg->null_pos]&= ~keyseg->null_bit;
|
||||
}
|
||||
if (keyseg->type == HA_KEYTYPE_BIT)
|
||||
{
|
||||
uint length= keyseg->length;
|
||||
|
||||
if (keyseg->bit_length)
|
||||
{
|
||||
uchar bits= *key++;
|
||||
set_rec_bits(bits, record + keyseg->bit_pos, keyseg->bit_start,
|
||||
keyseg->bit_length);
|
||||
length--;
|
||||
}
|
||||
else
|
||||
{
|
||||
clr_rec_bits(record + keyseg->bit_pos, keyseg->bit_start,
|
||||
keyseg->bit_length);
|
||||
}
|
||||
memcpy(record + keyseg->start, (byte*) key, length);
|
||||
key+= length;
|
||||
continue;
|
||||
}
|
||||
if (keyseg->flag & HA_SPACE_PACK)
|
||||
{
|
||||
uint length;
|
||||
get_key_length(length,key);
|
||||
#ifdef CHECK_KEYS
|
||||
if (length > keyseg->length || key+length > key_end)
|
||||
goto err;
|
||||
#endif
|
||||
pos= record+keyseg->start;
|
||||
if (keyseg->type != (int) HA_KEYTYPE_NUM)
|
||||
{
|
||||
memcpy(pos,key,(size_t) length);
|
||||
bfill(pos+length,keyseg->length-length,' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
bfill(pos,keyseg->length-length,' ');
|
||||
memcpy(pos+keyseg->length-length,key,(size_t) length);
|
||||
}
|
||||
key+=length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyseg->flag & HA_VAR_LENGTH_PART)
|
||||
{
|
||||
uint length;
|
||||
get_key_length(length,key);
|
||||
#ifdef CHECK_KEYS
|
||||
if (length > keyseg->length || key+length > key_end)
|
||||
goto err;
|
||||
#endif
|
||||
/* Store key length */
|
||||
if (keyseg->bit_start == 1)
|
||||
*(uchar*) (record+keyseg->start)= (uchar) length;
|
||||
else
|
||||
int2store(record+keyseg->start, length);
|
||||
/* And key data */
|
||||
memcpy(record+keyseg->start + keyseg->bit_start, (byte*) key, length);
|
||||
key+= length;
|
||||
}
|
||||
else if (keyseg->flag & HA_BLOB_PART)
|
||||
{
|
||||
uint length;
|
||||
get_key_length(length,key);
|
||||
#ifdef CHECK_KEYS
|
||||
if (length > keyseg->length || key+length > key_end)
|
||||
goto err;
|
||||
#endif
|
||||
memcpy(record+keyseg->start+keyseg->bit_start,
|
||||
(char*) &blob_ptr,sizeof(char*));
|
||||
memcpy(blob_ptr,key,length);
|
||||
blob_ptr+=length;
|
||||
|
||||
/* The above changed info->lastkey2. Inform maria_rnext_same(). */
|
||||
info->update&= ~HA_STATE_RNEXT_SAME;
|
||||
|
||||
_ma_store_blob_length(record+keyseg->start,
|
||||
(uint) keyseg->bit_start,length);
|
||||
key+=length;
|
||||
}
|
||||
else if (keyseg->flag & HA_SWAP_KEY)
|
||||
{
|
||||
byte *to= record+keyseg->start+keyseg->length;
|
||||
byte *end= key+keyseg->length;
|
||||
#ifdef CHECK_KEYS
|
||||
if (end > key_end)
|
||||
goto err;
|
||||
#endif
|
||||
do
|
||||
{
|
||||
*--to= *key++;
|
||||
} while (key != end);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CHECK_KEYS
|
||||
if (key+keyseg->length > key_end)
|
||||
goto err;
|
||||
#endif
|
||||
memcpy(record+keyseg->start,(byte*) key,
|
||||
(size_t) keyseg->length);
|
||||
key+= keyseg->length;
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
DBUG_RETURN(1); /* Crashed row */
|
||||
} /* _ma_put_key_in_record */
|
||||
|
||||
|
||||
/* Here when key reads are used */
|
||||
|
||||
int _ma_read_key_record(MARIA_HA *info, my_off_t filepos, byte *buf)
|
||||
{
|
||||
fast_ma_writeinfo(info);
|
||||
if (filepos != HA_OFFSET_ERROR)
|
||||
{
|
||||
if (info->lastinx >= 0)
|
||||
{ /* Read only key */
|
||||
if (_ma_put_key_in_record(info,(uint) info->lastinx,buf))
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno=HA_ERR_CRASHED;
|
||||
return -1;
|
||||
}
|
||||
info->update|= HA_STATE_AKTIV; /* We should find a record */
|
||||
return 0;
|
||||
}
|
||||
my_errno=HA_ERR_WRONG_INDEX;
|
||||
}
|
||||
return(-1); /* Wrong data to read */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Retrieve auto_increment info
|
||||
|
||||
SYNOPSIS
|
||||
retrieve_auto_increment()
|
||||
info Maria handler
|
||||
record Row to update
|
||||
|
||||
IMPLEMENTATION
|
||||
For signed columns we don't retrieve the auto increment value if it's
|
||||
less than zero.
|
||||
*/
|
||||
|
||||
ulonglong ma_retrieve_auto_increment(MARIA_HA *info,const byte *record)
|
||||
{
|
||||
ulonglong value= 0; /* Store unsigned values here */
|
||||
longlong s_value= 0; /* Store signed values here */
|
||||
HA_KEYSEG *keyseg= info->s->keyinfo[info->s->base.auto_key-1].seg;
|
||||
const uchar *key= (uchar*) record + keyseg->start;
|
||||
|
||||
switch (keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
s_value= (longlong) *(char*)key;
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
value=(ulonglong) *(uchar*) key;
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
s_value= (longlong) sint2korr(key);
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
value=(ulonglong) uint2korr(key);
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
s_value= (longlong) sint4korr(key);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
value=(ulonglong) uint4korr(key);
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
s_value= (longlong) sint3korr(key);
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
value=(ulonglong) uint3korr(key);
|
||||
break;
|
||||
case HA_KEYTYPE_FLOAT: /* This shouldn't be used */
|
||||
{
|
||||
float f_1;
|
||||
float4get(f_1,key);
|
||||
/* Ignore negative values */
|
||||
value = (f_1 < (float) 0.0) ? 0 : (ulonglong) f_1;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_DOUBLE: /* This shouldn't be used */
|
||||
{
|
||||
double f_1;
|
||||
float8get(f_1,key);
|
||||
/* Ignore negative values */
|
||||
value = (f_1 < 0.0) ? 0 : (ulonglong) f_1;
|
||||
break;
|
||||
}
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
s_value= sint8korr(key);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
value= uint8korr(key);
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
value=0; /* Error */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
The following code works becasue if s_value < 0 then value is 0
|
||||
and if s_value == 0 then value will contain either s_value or the
|
||||
correct value.
|
||||
*/
|
||||
return (s_value > 0) ? (ulonglong) s_value : value;
|
||||
}
|
||||
163
storage/maria/ma_keycache.c
Normal file
163
storage/maria/ma_keycache.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/* 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 */
|
||||
|
||||
/*
|
||||
Key cache assignments
|
||||
*/
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/*
|
||||
Assign pages of the index file for a table to a key cache
|
||||
|
||||
SYNOPSIS
|
||||
maria_assign_to_key_cache()
|
||||
info open table
|
||||
key_map map of indexes to assign to the key cache
|
||||
key_cache_ptr pointer to the key cache handle
|
||||
assign_lock Mutex to lock during assignment
|
||||
|
||||
PREREQUESTS
|
||||
One must have a READ lock or a WRITE lock on the table when calling
|
||||
the function to ensure that there is no other writers to it.
|
||||
|
||||
The caller must also ensure that one doesn't call this function from
|
||||
two different threads with the same table.
|
||||
|
||||
NOTES
|
||||
At present pages for all indexes must be assigned to the same key cache.
|
||||
In future only pages for indexes specified in the key_map parameter
|
||||
of the table will be assigned to the specified key cache.
|
||||
|
||||
RETURN VALUE
|
||||
0 If a success
|
||||
# Error code
|
||||
*/
|
||||
|
||||
int maria_assign_to_key_cache(MARIA_HA *info,
|
||||
ulonglong key_map __attribute__((unused)),
|
||||
KEY_CACHE *key_cache)
|
||||
{
|
||||
int error= 0;
|
||||
MARIA_SHARE* share= info->s;
|
||||
DBUG_ENTER("maria_assign_to_key_cache");
|
||||
DBUG_PRINT("enter",("old_key_cache_handle: %lx new_key_cache_handle: %lx",
|
||||
share->key_cache, key_cache));
|
||||
|
||||
/*
|
||||
Skip operation if we didn't change key cache. This can happen if we
|
||||
call this for all open instances of the same table
|
||||
*/
|
||||
if (share->key_cache == key_cache)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
/*
|
||||
First flush all blocks for the table in the old key cache.
|
||||
This is to ensure that the disk is consistent with the data pages
|
||||
in memory (which may not be the case if the table uses delayed_key_write)
|
||||
|
||||
Note that some other read thread may still fill in the key cache with
|
||||
new blocks during this call and after, but this doesn't matter as
|
||||
all threads will start using the new key cache for their next call to
|
||||
maria library and we know that there will not be any changed blocks
|
||||
in the old key cache.
|
||||
*/
|
||||
|
||||
if (flush_key_blocks(share->key_cache, share->kfile, FLUSH_RELEASE))
|
||||
{
|
||||
error= my_errno;
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info); /* Mark that table must be checked */
|
||||
}
|
||||
|
||||
/*
|
||||
Flush the new key cache for this file. This is needed to ensure
|
||||
that there is no old blocks (with outdated data) left in the new key
|
||||
cache from an earlier assign_to_keycache operation
|
||||
|
||||
(This can never fail as there is never any not written data in the
|
||||
new key cache)
|
||||
*/
|
||||
(void) flush_key_blocks(key_cache, share->kfile, FLUSH_RELEASE);
|
||||
|
||||
/*
|
||||
ensure that setting the key cache and changing the multi_key_cache
|
||||
is done atomicly
|
||||
*/
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
/*
|
||||
Tell all threads to use the new key cache
|
||||
This should be seen at the lastes for the next call to an maria function.
|
||||
*/
|
||||
share->key_cache= key_cache;
|
||||
|
||||
/* store the key cache in the global hash structure for future opens */
|
||||
if (multi_key_cache_set(share->unique_file_name, share->unique_name_length,
|
||||
share->key_cache))
|
||||
error= my_errno;
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Change all MARIA entries that uses one key cache to another key cache
|
||||
|
||||
SYNOPSIS
|
||||
maria_change_key_cache()
|
||||
old_key_cache Old key cache
|
||||
new_key_cache New key cache
|
||||
|
||||
NOTES
|
||||
This is used when we delete one key cache.
|
||||
|
||||
To handle the case where some other threads tries to open an MARIA
|
||||
table associated with the to-be-deleted key cache while this operation
|
||||
is running, we have to call 'multi_key_cache_change()' from this
|
||||
function while we have a lock on the MARIA table list structure.
|
||||
|
||||
This is safe as long as it's only MARIA that is using this specific
|
||||
key cache.
|
||||
*/
|
||||
|
||||
|
||||
void maria_change_key_cache(KEY_CACHE *old_key_cache,
|
||||
KEY_CACHE *new_key_cache)
|
||||
{
|
||||
LIST *pos;
|
||||
DBUG_ENTER("maria_change_key_cache");
|
||||
|
||||
/*
|
||||
Lock list to ensure that no one can close the table while we manipulate it
|
||||
*/
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
for (pos=maria_open_list ; pos ; pos=pos->next)
|
||||
{
|
||||
MARIA_HA *info= (MARIA_HA*) pos->data;
|
||||
MARIA_SHARE *share= info->s;
|
||||
if (share->key_cache == old_key_cache)
|
||||
maria_assign_to_key_cache(info, (ulonglong) ~0, new_key_cache);
|
||||
}
|
||||
|
||||
/*
|
||||
We have to do the following call while we have the lock on the
|
||||
MARIA list structure to ensure that another thread is not trying to
|
||||
open a new table that will be associted with the old key cache
|
||||
*/
|
||||
multi_key_cache_change(old_key_cache, new_key_cache);
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
225
storage/maria/ma_least_recently_dirtied.c
Normal file
225
storage/maria/ma_least_recently_dirtied.c
Normal file
@@ -0,0 +1,225 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3261 Maria - background flushing of the least-recently-dirtied pages
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/*
|
||||
To be part of the page cache.
|
||||
The pseudocode below is dependent on the page cache
|
||||
which is being designed WL#3134. It is not clear if I need to do page
|
||||
copies, as the page cache already keeps page copies.
|
||||
So, this code will move to the page cache and take inspiration from its
|
||||
methods. Below is just to give the idea of what could be done.
|
||||
And I should compare my imaginations to WL#3134.
|
||||
*/
|
||||
|
||||
/* Here is the implementation of this module */
|
||||
|
||||
#include "page_cache.h"
|
||||
#include "least_recently_dirtied.h"
|
||||
|
||||
/*
|
||||
MikaelR suggested removing this global_LRD_mutex (I have a paper note of
|
||||
comments), however at least for the first version we'll start with this
|
||||
mutex (which will be a LOCK-based atomic_rwlock).
|
||||
*/
|
||||
pthread_mutex_t global_LRD_mutex;
|
||||
|
||||
/*
|
||||
When we flush a page, we should pin page.
|
||||
This "pin" is to protect against that:
|
||||
I make copy,
|
||||
you modify in memory and flush to disk and remove from LRD and from cache,
|
||||
I write copy to disk,
|
||||
checkpoint happens.
|
||||
result: old page is on disk, page is absent from LRD, your REDO will be
|
||||
wrongly ignored.
|
||||
|
||||
Pin: there can be multiple pins, flushing imposes that there are zero pins.
|
||||
For example, pin could be a uint counter protected by the page's latch.
|
||||
|
||||
Maybe it's ok if when there is a page replacement, the replacer does not
|
||||
remove page from the LRD (it would save global mutex); for that, background
|
||||
flusher should be prepared to see pages in the LRD which are not in the page
|
||||
cache (then just ignore them). However checkpoint will contain superfluous
|
||||
entries and so do more work.
|
||||
*/
|
||||
|
||||
#define PAGE_SIZE (16*1024) /* just as an example */
|
||||
/*
|
||||
Optimization:
|
||||
LRD flusher should not flush pages one by one: to be fast, it flushes a
|
||||
group of pages in sequential disk order if possible; a group of pages is just
|
||||
FLUSH_GROUP_SIZE pages.
|
||||
Key cache has groupping already somehow Monty said (investigate that).
|
||||
*/
|
||||
#define FLUSH_GROUP_SIZE 512 /* 8 MB */
|
||||
/*
|
||||
We don't want to probe for checkpoint requests all the time (it takes
|
||||
the log mutex).
|
||||
If FLUSH_GROUP_SIZE is 8MB, assuming a local disk which can write 30MB/s
|
||||
(1.8GB/min), probing every 16th call to flush_one_group_from_LRD() is every
|
||||
16*8=128MB which is every 128/30=4.2second.
|
||||
Using a power of 2 gives a fast modulo operation.
|
||||
*/
|
||||
#define CHECKPOINT_PROBING_PERIOD_LOG2 4
|
||||
|
||||
/*
|
||||
This thread does background flush of pieces of the LRD, and all checkpoints.
|
||||
Just launch it when engine starts.
|
||||
MikaelR questioned why the same thread does two different jobs, the risk
|
||||
could be that while a checkpoint happens no LRD flushing happens.
|
||||
*/
|
||||
pthread_handler_decl background_flush_and_checkpoint_thread()
|
||||
{
|
||||
char *flush_group_buffer= my_malloc(PAGE_SIZE*FLUSH_GROUP_SIZE);
|
||||
uint flush_calls= 0;
|
||||
while (this_thread_not_killed)
|
||||
{
|
||||
if ((flush_calls++) & ((2<<CHECKPOINT_PROBING_PERIOD_LOG2)-1) == 0)
|
||||
execute_asynchronous_checkpoint_if_any();
|
||||
lock(global_LRD_mutex);
|
||||
flush_one_group_from_LRD();
|
||||
safemutex_assert_not_owner(global_LRD_mutex);
|
||||
/*
|
||||
We are a background thread, leave time for client threads or we would
|
||||
monopolize the disk:
|
||||
*/
|
||||
pthread_yield();
|
||||
}
|
||||
my_free(flush_group_buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
flushes only the first FLUSH_GROUP_SIZE pages of the LRD.
|
||||
*/
|
||||
flush_one_group_from_LRD()
|
||||
{
|
||||
char *ptr;
|
||||
safe_mutex_assert_owner(global_LRD_mutex);
|
||||
|
||||
for (page= 0; page<FLUSH_GROUP_SIZE; page++)
|
||||
{
|
||||
copy_element_to_array;
|
||||
}
|
||||
/*
|
||||
One rule to better observe is "page must be flushed to disk before it is
|
||||
removed from LRD" (otherwise checkpoint is incomplete info, corruption).
|
||||
*/
|
||||
unlock(global_LRD_mutex);
|
||||
/* page id is concatenation of "file id" and "number of page in file" */
|
||||
qsort(array, sizeof(*element), FLUSH_GROUP_SIZE, by_page_id);
|
||||
for (scan_array)
|
||||
{
|
||||
if (page_cache_latch(page_id, READ) == PAGE_ABSENT)
|
||||
{
|
||||
/*
|
||||
page disappeared since we made the copy (it was flushed to be
|
||||
replaced), remove from array (memcpy tail of array over it)...
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
memcpy(flush_group_buffer+..., page->data, PAGE_SIZE);
|
||||
pin_page;
|
||||
page_cache_unlatch(page_id, KEEP_PINNED); /* but keep pinned */
|
||||
}
|
||||
for (scan_the_array)
|
||||
{
|
||||
/*
|
||||
As an optimization, we try to identify contiguous-in-the-file segments (to
|
||||
issue one big write()).
|
||||
In non-optimized version, contiguous segment is always only one page.
|
||||
*/
|
||||
if ((next_page.page_id - this_page.page_id) == 1)
|
||||
{
|
||||
/*
|
||||
this page and next page are in same file and are contiguous in the
|
||||
file: add page to contiguous segment...
|
||||
*/
|
||||
continue; /* defer write() to next pages */
|
||||
}
|
||||
/* contiguous segment ends */
|
||||
my_pwrite(file, contiguous_segment_start_offset, contiguous_segment_size);
|
||||
|
||||
/*
|
||||
note that if we had doublewrite, doublewrite buffer may prevent us from
|
||||
doing this write() grouping (if doublewrite space is shorter).
|
||||
*/
|
||||
}
|
||||
/*
|
||||
Now remove pages from LRD. As we have pinned them, all pages that we
|
||||
managed to pin are still in the LRD, in the same order, we can just cut
|
||||
the LRD at the last element of "array". This is more efficient that
|
||||
removing element by element (which would take LRD mutex many times) in the
|
||||
loop above.
|
||||
*/
|
||||
lock(global_LRD_mutex);
|
||||
/* cut LRD by bending LRD->first, free cut portion... */
|
||||
unlock(global_LRD_mutex);
|
||||
for (scan_array)
|
||||
{
|
||||
/*
|
||||
if the page has a property "modified since last flush" (i.e. which is
|
||||
redundant with the presence of the page in the LRD, this property can
|
||||
just be a pointer to the LRD element) we should reset it
|
||||
(note that then the property would live slightly longer than
|
||||
the presence in LRD).
|
||||
*/
|
||||
page_cache_unpin(page_id);
|
||||
/*
|
||||
order between unpin and removal from LRD is not clear, depends on what
|
||||
pin actually is.
|
||||
*/
|
||||
}
|
||||
free(array);
|
||||
/*
|
||||
MikaelR noted that he observed that Linux's file cache may never fsync to
|
||||
disk until this cache is full, at which point it decides to empty the
|
||||
cache, making the machine very slow. A solution was to fsync after writing
|
||||
2 MB.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
Flushes all page from LRD up to approximately rec_lsn>=max_lsn.
|
||||
This is approximate because we flush groups, and because the LRD list may
|
||||
not be exactly sorted by rec_lsn (because for a big row, all pages of the
|
||||
row are inserted into the LRD with rec_lsn being the LSN of the REDO for the
|
||||
first page, so if there are concurrent insertions, the last page of the big
|
||||
row may have a smaller rec_lsn than the previous pages inserted by
|
||||
concurrent inserters).
|
||||
*/
|
||||
int flush_all_LRD_to_lsn(LSN max_lsn)
|
||||
{
|
||||
lock(global_LRD_mutex);
|
||||
if (max_lsn == MAX_LSN) /* don't want to flush forever, so make it fixed: */
|
||||
max_lsn= LRD->first->prev->rec_lsn;
|
||||
while (LRD->first->rec_lsn < max_lsn)
|
||||
{
|
||||
if (flush_one_group_from_LRD()) /* will unlock LRD mutex */
|
||||
return 1;
|
||||
/*
|
||||
The scheduler may preempt us here as we released the mutex; this is good.
|
||||
*/
|
||||
lock(global_LRD_mutex);
|
||||
}
|
||||
unlock(global_LRD_mutex);
|
||||
return 0;
|
||||
}
|
||||
26
storage/maria/ma_least_recently_dirtied.h
Normal file
26
storage/maria/ma_least_recently_dirtied.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3261 Maria - background flushing of the least-recently-dirtied pages
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/* This is the interface of this module. */
|
||||
|
||||
/* flushes all page from LRD up to approximately rec_lsn>=max_lsn */
|
||||
int flush_all_LRD_to_lsn(LSN max_lsn);
|
||||
486
storage/maria/ma_locking.c
Normal file
486
storage/maria/ma_locking.c
Normal file
@@ -0,0 +1,486 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
locking of isam-tables.
|
||||
reads info from a isam-table. Must be first request before doing any furter
|
||||
calls to any isamfunktion. Is used to allow many process use the same
|
||||
isamdatabase.
|
||||
*/
|
||||
|
||||
#include "ma_ftdefs.h"
|
||||
|
||||
/* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
|
||||
|
||||
int maria_lock_database(MARIA_HA *info, int lock_type)
|
||||
{
|
||||
int error;
|
||||
uint count;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_lock_database");
|
||||
DBUG_PRINT("enter",("lock_type: %d old lock %d r_locks: %u w_locks: %u "
|
||||
"global_changed: %d open_count: %u name: '%s'",
|
||||
lock_type, info->lock_type, share->r_locks,
|
||||
share->w_locks,
|
||||
share->global_changed, share->state.open_count,
|
||||
share->index_file_name));
|
||||
if (share->options & HA_OPTION_READ_ONLY_DATA ||
|
||||
info->lock_type == lock_type)
|
||||
DBUG_RETURN(0);
|
||||
if (lock_type == F_EXTRA_LCK) /* Used by TMP tables */
|
||||
{
|
||||
++share->w_locks;
|
||||
++share->tot_locks;
|
||||
info->lock_type= lock_type;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
error=0;
|
||||
pthread_mutex_lock(&share->intern_lock);
|
||||
if (share->kfile >= 0) /* May only be false on windows */
|
||||
{
|
||||
switch (lock_type) {
|
||||
case F_UNLCK:
|
||||
maria_ftparser_call_deinitializer(info);
|
||||
if (info->lock_type == F_RDLCK)
|
||||
count= --share->r_locks;
|
||||
else
|
||||
count= --share->w_locks;
|
||||
--share->tot_locks;
|
||||
if (info->lock_type == F_WRLCK && !share->w_locks &&
|
||||
!share->delay_key_write && flush_key_blocks(share->key_cache,
|
||||
share->kfile,FLUSH_KEEP))
|
||||
{
|
||||
error=my_errno;
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info); /* Mark that table must be checked */
|
||||
}
|
||||
if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
|
||||
{
|
||||
if (end_io_cache(&info->rec_cache))
|
||||
{
|
||||
error=my_errno;
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info);
|
||||
}
|
||||
}
|
||||
if (!count)
|
||||
{
|
||||
DBUG_PRINT("info",("changed: %u w_locks: %u",
|
||||
(uint) share->changed, share->w_locks));
|
||||
if (share->changed && !share->w_locks)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
if ((info->s->mmaped_length != info->s->state.state.data_file_length) &&
|
||||
(info->s->nonmmaped_inserts > MAX_NONMAPPED_INSERTS))
|
||||
{
|
||||
if (info->s->concurrent_insert)
|
||||
rw_wrlock(&info->s->mmap_lock);
|
||||
_ma_remap_file(info, info->s->state.state.data_file_length);
|
||||
info->s->nonmmaped_inserts= 0;
|
||||
if (info->s->concurrent_insert)
|
||||
rw_unlock(&info->s->mmap_lock);
|
||||
}
|
||||
#endif
|
||||
share->state.process= share->last_process=share->this_process;
|
||||
share->state.unique= info->last_unique= info->this_unique;
|
||||
share->state.update_count= info->last_loop= ++info->this_loop;
|
||||
if (_ma_state_info_write(share->kfile, &share->state, 1))
|
||||
error=my_errno;
|
||||
share->changed=0;
|
||||
if (maria_flush)
|
||||
{
|
||||
if (my_sync(share->kfile, MYF(0)))
|
||||
error= my_errno;
|
||||
if (my_sync(info->dfile, MYF(0)))
|
||||
error= my_errno;
|
||||
}
|
||||
else
|
||||
share->not_flushed=1;
|
||||
if (error)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
|
||||
info->lock_type= F_UNLCK;
|
||||
break;
|
||||
case F_RDLCK:
|
||||
if (info->lock_type == F_WRLCK)
|
||||
{
|
||||
/*
|
||||
Change RW to READONLY
|
||||
|
||||
mysqld does not turn write locks to read locks,
|
||||
so we're never here in mysqld.
|
||||
*/
|
||||
share->w_locks--;
|
||||
share->r_locks++;
|
||||
info->lock_type=lock_type;
|
||||
break;
|
||||
}
|
||||
if (!share->r_locks && !share->w_locks)
|
||||
{
|
||||
if (_ma_state_info_read_dsk(share->kfile, &share->state, 1))
|
||||
{
|
||||
error=my_errno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
VOID(_ma_test_if_changed(info));
|
||||
share->r_locks++;
|
||||
share->tot_locks++;
|
||||
info->lock_type=lock_type;
|
||||
break;
|
||||
case F_WRLCK:
|
||||
if (info->lock_type == F_RDLCK)
|
||||
{ /* Change READONLY to RW */
|
||||
if (share->r_locks == 1)
|
||||
{
|
||||
share->r_locks--;
|
||||
share->w_locks++;
|
||||
info->lock_type=lock_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(share->options & HA_OPTION_READ_ONLY_DATA))
|
||||
{
|
||||
if (!share->w_locks)
|
||||
{
|
||||
if (!share->r_locks)
|
||||
{
|
||||
if (_ma_state_info_read_dsk(share->kfile, &share->state, 1))
|
||||
{
|
||||
error=my_errno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VOID(_ma_test_if_changed(info));
|
||||
|
||||
info->lock_type=lock_type;
|
||||
info->invalidator=info->s->invalidator;
|
||||
share->w_locks++;
|
||||
share->tot_locks++;
|
||||
break;
|
||||
default:
|
||||
break; /* Impossible */
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&share->intern_lock);
|
||||
DBUG_RETURN(error);
|
||||
} /* maria_lock_database */
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
The following functions are called by thr_lock() in threaded applications
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
Create a copy of the current status for the table
|
||||
|
||||
SYNOPSIS
|
||||
_ma_get_status()
|
||||
param Pointer to Myisam handler
|
||||
concurrent_insert Set to 1 if we are going to do concurrent inserts
|
||||
(THR_WRITE_CONCURRENT_INSERT was used)
|
||||
*/
|
||||
|
||||
void _ma_get_status(void* param, int concurrent_insert)
|
||||
{
|
||||
MARIA_HA *info=(MARIA_HA*) param;
|
||||
DBUG_ENTER("_ma_get_status");
|
||||
DBUG_PRINT("info",("key_file: %ld data_file: %ld concurrent_insert: %d",
|
||||
(long) info->s->state.state.key_file_length,
|
||||
(long) info->s->state.state.data_file_length,
|
||||
concurrent_insert));
|
||||
#ifndef DBUG_OFF
|
||||
if (info->state->key_file_length > info->s->state.state.key_file_length ||
|
||||
info->state->data_file_length > info->s->state.state.data_file_length)
|
||||
DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
|
||||
(long) info->state->key_file_length,
|
||||
(long) info->state->data_file_length));
|
||||
#endif
|
||||
info->save_state=info->s->state.state;
|
||||
info->state= &info->save_state;
|
||||
info->append_insert_at_end= concurrent_insert;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
void _ma_update_status(void* param)
|
||||
{
|
||||
MARIA_HA *info=(MARIA_HA*) param;
|
||||
/*
|
||||
Because someone may have closed the table we point at, we only
|
||||
update the state if its our own state. This isn't a problem as
|
||||
we are always pointing at our own lock or at a read lock.
|
||||
(This is enforced by thr_multi_lock.c)
|
||||
*/
|
||||
if (info->state == &info->save_state)
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
DBUG_PRINT("info",("updating status: key_file: %ld data_file: %ld",
|
||||
(long) info->state->key_file_length,
|
||||
(long) info->state->data_file_length));
|
||||
if (info->state->key_file_length < info->s->state.state.key_file_length ||
|
||||
info->state->data_file_length < info->s->state.state.data_file_length)
|
||||
DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
|
||||
(long) info->s->state.state.key_file_length,
|
||||
(long) info->s->state.state.data_file_length));
|
||||
#endif
|
||||
info->s->state.state= *info->state;
|
||||
info->state= &info->s->state.state;
|
||||
}
|
||||
info->append_insert_at_end= 0;
|
||||
|
||||
/*
|
||||
We have to flush the write cache here as other threads may start
|
||||
reading the table before maria_lock_database() is called
|
||||
*/
|
||||
if (info->opt_flag & WRITE_CACHE_USED)
|
||||
{
|
||||
if (end_io_cache(&info->rec_cache))
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
maria_mark_crashed(info);
|
||||
}
|
||||
info->opt_flag&= ~WRITE_CACHE_USED;
|
||||
}
|
||||
}
|
||||
|
||||
void _ma_copy_status(void* to,void *from)
|
||||
{
|
||||
((MARIA_HA*) to)->state= &((MARIA_HA*) from)->save_state;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if should allow concurrent inserts
|
||||
|
||||
IMPLEMENTATION
|
||||
Allow concurrent inserts if we don't have a hole in the table or
|
||||
if there is no active write lock and there is active read locks and
|
||||
maria_concurrent_insert == 2. In this last case the new
|
||||
row('s) are inserted at end of file instead of filling up the hole.
|
||||
|
||||
The last case is to allow one to inserts into a heavily read-used table
|
||||
even if there is holes.
|
||||
|
||||
NOTES
|
||||
If there is a an rtree indexes in the table, concurrent inserts are
|
||||
disabled in maria_open()
|
||||
|
||||
RETURN
|
||||
0 ok to use concurrent inserts
|
||||
1 not ok
|
||||
*/
|
||||
|
||||
my_bool _ma_check_status(void *param)
|
||||
{
|
||||
MARIA_HA *info=(MARIA_HA*) param;
|
||||
/*
|
||||
The test for w_locks == 1 is here because this thread has already done an
|
||||
external lock (in other words: w_locks == 1 means no other threads has
|
||||
a write lock)
|
||||
*/
|
||||
DBUG_PRINT("info",("dellink: %ld r_locks: %u w_locks: %u",
|
||||
(long) info->s->state.dellink, (uint) info->s->r_locks,
|
||||
(uint) info->s->w_locks));
|
||||
return (my_bool) !(info->s->state.dellink == HA_OFFSET_ERROR ||
|
||||
(maria_concurrent_insert == 2 && info->s->r_locks &&
|
||||
info->s->w_locks == 1));
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
** functions to read / write the state
|
||||
****************************************************************************/
|
||||
|
||||
int _ma_readinfo(register MARIA_HA *info, int lock_type, int check_keybuffer)
|
||||
{
|
||||
DBUG_ENTER("_ma_readinfo");
|
||||
|
||||
if (info->lock_type == F_UNLCK)
|
||||
{
|
||||
MARIA_SHARE *share=info->s;
|
||||
if (!share->tot_locks)
|
||||
{
|
||||
if (_ma_state_info_read_dsk(share->kfile, &share->state, 1))
|
||||
{
|
||||
int error=my_errno ? my_errno : -1;
|
||||
my_errno=error;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
if (check_keybuffer)
|
||||
VOID(_ma_test_if_changed(info));
|
||||
info->invalidator=info->s->invalidator;
|
||||
}
|
||||
else if (lock_type == F_WRLCK && info->lock_type == F_RDLCK)
|
||||
{
|
||||
my_errno=EACCES; /* Not allowed to change */
|
||||
DBUG_RETURN(-1); /* when have read_lock() */
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
} /* _ma_readinfo */
|
||||
|
||||
|
||||
/*
|
||||
Every isam-function that uppdates the isam-database MUST end with this
|
||||
request
|
||||
*/
|
||||
|
||||
int _ma_writeinfo(register MARIA_HA *info, uint operation)
|
||||
{
|
||||
int error,olderror;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("_ma_writeinfo");
|
||||
DBUG_PRINT("info",("operation: %u tot_locks: %u", operation,
|
||||
share->tot_locks));
|
||||
|
||||
error=0;
|
||||
if (share->tot_locks == 0)
|
||||
{
|
||||
olderror=my_errno; /* Remember last error */
|
||||
if (operation)
|
||||
{ /* Two threads can't be here */
|
||||
share->state.process= share->last_process= share->this_process;
|
||||
share->state.unique= info->last_unique= info->this_unique;
|
||||
share->state.update_count= info->last_loop= ++info->this_loop;
|
||||
if ((error=_ma_state_info_write(share->kfile, &share->state, 1)))
|
||||
olderror=my_errno;
|
||||
#ifdef __WIN__
|
||||
if (maria_flush)
|
||||
{
|
||||
_commit(share->kfile);
|
||||
_commit(info->dfile);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
my_errno=olderror;
|
||||
}
|
||||
else if (operation)
|
||||
share->changed= 1; /* Mark keyfile changed */
|
||||
DBUG_RETURN(error);
|
||||
} /* _ma_writeinfo */
|
||||
|
||||
|
||||
/* Test if someone has changed the database */
|
||||
/* (Should be called after readinfo) */
|
||||
|
||||
int _ma_test_if_changed(register MARIA_HA *info)
|
||||
{
|
||||
MARIA_SHARE *share=info->s;
|
||||
if (share->state.process != share->last_process ||
|
||||
share->state.unique != info->last_unique ||
|
||||
share->state.update_count != info->last_loop)
|
||||
{ /* Keyfile has changed */
|
||||
DBUG_PRINT("info",("index file changed"));
|
||||
if (share->state.process != share->this_process)
|
||||
VOID(flush_key_blocks(share->key_cache, share->kfile, FLUSH_RELEASE));
|
||||
share->last_process=share->state.process;
|
||||
info->last_unique= share->state.unique;
|
||||
info->last_loop= share->state.update_count;
|
||||
info->update|= HA_STATE_WRITTEN; /* Must use file on next */
|
||||
info->data_changed= 1; /* For maria_is_changed */
|
||||
return 1;
|
||||
}
|
||||
return (!(info->update & HA_STATE_AKTIV) ||
|
||||
(info->update & (HA_STATE_WRITTEN | HA_STATE_DELETED |
|
||||
HA_STATE_KEY_CHANGED)));
|
||||
} /* _ma_test_if_changed */
|
||||
|
||||
|
||||
/*
|
||||
Put a mark in the .MYI file that someone is updating the table
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
|
||||
state.open_count in the .MYI file is used the following way:
|
||||
- For the first change of the .MYI file in this process open_count is
|
||||
incremented by maria_mark_file_change(). (We have a write lock on the file
|
||||
when this happens)
|
||||
- In maria_close() it's decremented by _ma_decrement_open_count() if it
|
||||
was incremented in the same process.
|
||||
|
||||
This mean that if we are the only process using the file, the open_count
|
||||
tells us if the MARIA file wasn't properly closed. (This is true if
|
||||
my_disable_locking is set).
|
||||
*/
|
||||
|
||||
|
||||
int _ma_mark_file_changed(MARIA_HA *info)
|
||||
{
|
||||
char buff[3];
|
||||
register MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("_ma_mark_file_changed");
|
||||
|
||||
if (!(share->state.changed & STATE_CHANGED) || ! share->global_changed)
|
||||
{
|
||||
share->state.changed|=(STATE_CHANGED | STATE_NOT_ANALYZED |
|
||||
STATE_NOT_OPTIMIZED_KEYS);
|
||||
if (!share->global_changed)
|
||||
{
|
||||
share->global_changed=1;
|
||||
share->state.open_count++;
|
||||
}
|
||||
if (!share->temporary)
|
||||
{
|
||||
mi_int2store(buff,share->state.open_count);
|
||||
buff[2]=1; /* Mark that it's changed */
|
||||
DBUG_RETURN(my_pwrite(share->kfile,buff,sizeof(buff),
|
||||
sizeof(share->state.header),
|
||||
MYF(MY_NABP)));
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This is only called by close or by extra(HA_FLUSH) if the OS has the pwrite()
|
||||
call. In these context the following code should be safe!
|
||||
*/
|
||||
|
||||
int _ma_decrement_open_count(MARIA_HA *info)
|
||||
{
|
||||
char buff[2];
|
||||
register MARIA_SHARE *share=info->s;
|
||||
int lock_error=0,write_error=0;
|
||||
if (share->global_changed)
|
||||
{
|
||||
uint old_lock=info->lock_type;
|
||||
share->global_changed=0;
|
||||
lock_error=maria_lock_database(info,F_WRLCK);
|
||||
/* Its not fatal even if we couldn't get the lock ! */
|
||||
if (share->state.open_count > 0)
|
||||
{
|
||||
share->state.open_count--;
|
||||
mi_int2store(buff,share->state.open_count);
|
||||
write_error=my_pwrite(share->kfile,buff,sizeof(buff),
|
||||
sizeof(share->state.header),
|
||||
MYF(MY_NABP));
|
||||
}
|
||||
if (!lock_error)
|
||||
lock_error=maria_lock_database(info,old_lock);
|
||||
}
|
||||
return test(lock_error || write_error);
|
||||
}
|
||||
1267
storage/maria/ma_open.c
Normal file
1267
storage/maria/ma_open.c
Normal file
File diff suppressed because it is too large
Load Diff
1346
storage/maria/ma_packrec.c
Normal file
1346
storage/maria/ma_packrec.c
Normal file
File diff suppressed because it is too large
Load Diff
161
storage/maria/ma_page.c
Normal file
161
storage/maria/ma_page.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Read and write key blocks */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Fetch a key-page in memory */
|
||||
|
||||
uchar *_ma_fetch_keypage(register MARIA_HA *info, MARIA_KEYDEF *keyinfo,
|
||||
my_off_t page, int level,
|
||||
uchar *buff, int return_buffer)
|
||||
{
|
||||
uchar *tmp;
|
||||
uint page_size;
|
||||
DBUG_ENTER("_ma_fetch_keypage");
|
||||
DBUG_PRINT("enter",("page: %ld",page));
|
||||
|
||||
tmp=(uchar*) key_cache_read(info->s->key_cache,
|
||||
info->s->kfile, page, level, (byte*) buff,
|
||||
(uint) keyinfo->block_length,
|
||||
(uint) keyinfo->block_length,
|
||||
return_buffer);
|
||||
if (tmp == info->buff)
|
||||
info->buff_used=1;
|
||||
else if (!tmp)
|
||||
{
|
||||
DBUG_PRINT("error",("Got errno: %d from key_cache_read",my_errno));
|
||||
info->last_keypage=HA_OFFSET_ERROR;
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno=HA_ERR_CRASHED;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
info->last_keypage=page;
|
||||
page_size=maria_getint(tmp);
|
||||
if (page_size < 4 || page_size > keyinfo->block_length)
|
||||
{
|
||||
DBUG_PRINT("error",("page %lu had wrong page length: %u",
|
||||
(ulong) page, page_size));
|
||||
DBUG_DUMP("page", (char*) tmp, keyinfo->block_length);
|
||||
info->last_keypage = HA_OFFSET_ERROR;
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno = HA_ERR_CRASHED;
|
||||
tmp = 0;
|
||||
}
|
||||
DBUG_RETURN(tmp);
|
||||
} /* _ma_fetch_keypage */
|
||||
|
||||
|
||||
/* Write a key-page on disk */
|
||||
|
||||
int _ma_write_keypage(register MARIA_HA *info, register MARIA_KEYDEF *keyinfo,
|
||||
my_off_t page, int level, uchar *buff)
|
||||
{
|
||||
reg3 uint length;
|
||||
DBUG_ENTER("_ma_write_keypage");
|
||||
|
||||
#ifndef FAST /* Safety check */
|
||||
if (page < info->s->base.keystart ||
|
||||
page+keyinfo->block_length > info->state->key_file_length ||
|
||||
(page & (MARIA_MIN_KEY_BLOCK_LENGTH-1)))
|
||||
{
|
||||
DBUG_PRINT("error",("Trying to write inside key status region: key_start: %lu length: %lu page: %lu",
|
||||
(long) info->s->base.keystart,
|
||||
(long) info->state->key_file_length,
|
||||
(long) page));
|
||||
my_errno=EINVAL;
|
||||
DBUG_RETURN((-1));
|
||||
}
|
||||
DBUG_PRINT("page",("write page at: %lu",(long) page,buff));
|
||||
DBUG_DUMP("buff",(byte*) buff,maria_getint(buff));
|
||||
#endif
|
||||
|
||||
if ((length=keyinfo->block_length) > IO_SIZE*2 &&
|
||||
info->state->key_file_length != page+length)
|
||||
length= ((maria_getint(buff)+IO_SIZE-1) & (uint) ~(IO_SIZE-1));
|
||||
#ifdef HAVE_purify
|
||||
{
|
||||
length=maria_getint(buff);
|
||||
bzero((byte*) buff+length,keyinfo->block_length-length);
|
||||
length=keyinfo->block_length;
|
||||
}
|
||||
#endif
|
||||
DBUG_RETURN((key_cache_write(info->s->key_cache,
|
||||
info->s->kfile,page, level, (byte*) buff,length,
|
||||
(uint) keyinfo->block_length,
|
||||
(int) ((info->lock_type != F_UNLCK) ||
|
||||
info->s->delay_key_write))));
|
||||
} /* maria_write_keypage */
|
||||
|
||||
|
||||
/* Remove page from disk */
|
||||
|
||||
int _ma_dispose(register MARIA_HA *info, MARIA_KEYDEF *keyinfo, my_off_t pos,
|
||||
int level)
|
||||
{
|
||||
my_off_t old_link;
|
||||
char buff[8];
|
||||
DBUG_ENTER("_ma_dispose");
|
||||
DBUG_PRINT("enter",("pos: %ld", (long) pos));
|
||||
|
||||
old_link= info->s->state.key_del[keyinfo->block_size_index];
|
||||
info->s->state.key_del[keyinfo->block_size_index]= pos;
|
||||
mi_sizestore(buff,old_link);
|
||||
info->s->state.changed|= STATE_NOT_SORTED_PAGES;
|
||||
DBUG_RETURN(key_cache_write(info->s->key_cache,
|
||||
info->s->kfile, pos , level, buff,
|
||||
sizeof(buff),
|
||||
(uint) keyinfo->block_length,
|
||||
(int) (info->lock_type != F_UNLCK)));
|
||||
} /* _ma_dispose */
|
||||
|
||||
|
||||
/* Make new page on disk */
|
||||
|
||||
my_off_t _ma_new(register MARIA_HA *info, MARIA_KEYDEF *keyinfo, int level)
|
||||
{
|
||||
my_off_t pos;
|
||||
char buff[8];
|
||||
DBUG_ENTER("_ma_new");
|
||||
|
||||
if ((pos= info->s->state.key_del[keyinfo->block_size_index]) ==
|
||||
HA_OFFSET_ERROR)
|
||||
{
|
||||
if (info->state->key_file_length >=
|
||||
info->s->base.max_key_file_length - keyinfo->block_length)
|
||||
{
|
||||
my_errno=HA_ERR_INDEX_FILE_FULL;
|
||||
DBUG_RETURN(HA_OFFSET_ERROR);
|
||||
}
|
||||
pos=info->state->key_file_length;
|
||||
info->state->key_file_length+= keyinfo->block_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!key_cache_read(info->s->key_cache,
|
||||
info->s->kfile, pos, level,
|
||||
buff,
|
||||
(uint) sizeof(buff),
|
||||
(uint) keyinfo->block_length,0))
|
||||
pos= HA_OFFSET_ERROR;
|
||||
else
|
||||
info->s->state.key_del[keyinfo->block_size_index]= mi_sizekorr(buff);
|
||||
}
|
||||
info->s->state.changed|= STATE_NOT_SORTED_PAGES;
|
||||
DBUG_PRINT("exit",("Pos: %ld",(long) pos));
|
||||
DBUG_RETURN(pos);
|
||||
} /* _ma_new */
|
||||
124
storage/maria/ma_panic.c
Normal file
124
storage/maria/ma_panic.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "ma_fulltext.h"
|
||||
|
||||
/*
|
||||
Stop usage of Maria
|
||||
|
||||
SYNOPSIS
|
||||
maria_panic()
|
||||
flag HA_PANIC_CLOSE: All maria files (tables and log) are closed.
|
||||
maria_end() is called.
|
||||
HA_PANIC_WRITE: All misam files are unlocked and
|
||||
all changed data in single user maria is
|
||||
written to file
|
||||
HA_PANIC_READ All maria files that was locked when
|
||||
maria_panic(HA_PANIC_WRITE) was done is
|
||||
locked. A maria_readinfo() is done for
|
||||
all single user files to get changes
|
||||
in database
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
# error number in case of error
|
||||
*/
|
||||
|
||||
int maria_panic(enum ha_panic_function flag)
|
||||
{
|
||||
int error=0;
|
||||
LIST *list_element,*next_open;
|
||||
MARIA_HA *info;
|
||||
DBUG_ENTER("maria_panic");
|
||||
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
for (list_element=maria_open_list ; list_element ; list_element=next_open)
|
||||
{
|
||||
next_open=list_element->next; /* Save if close */
|
||||
info=(MARIA_HA*) list_element->data;
|
||||
switch (flag) {
|
||||
case HA_PANIC_CLOSE:
|
||||
pthread_mutex_unlock(&THR_LOCK_maria); /* Not exactly right... */
|
||||
if (maria_close(info))
|
||||
error=my_errno;
|
||||
pthread_mutex_lock(&THR_LOCK_maria);
|
||||
break;
|
||||
case HA_PANIC_WRITE: /* Do this to free databases */
|
||||
#ifdef CANT_OPEN_FILES_TWICE
|
||||
if (info->s->options & HA_OPTION_READ_ONLY_DATA)
|
||||
break;
|
||||
#endif
|
||||
if (flush_key_blocks(info->s->key_cache, info->s->kfile, FLUSH_RELEASE))
|
||||
error=my_errno;
|
||||
if (info->opt_flag & WRITE_CACHE_USED)
|
||||
if (flush_io_cache(&info->rec_cache))
|
||||
error=my_errno;
|
||||
if (info->opt_flag & READ_CACHE_USED)
|
||||
{
|
||||
if (flush_io_cache(&info->rec_cache))
|
||||
error=my_errno;
|
||||
reinit_io_cache(&info->rec_cache,READ_CACHE,0,
|
||||
(pbool) (info->lock_type != F_UNLCK),1);
|
||||
}
|
||||
if (info->lock_type != F_UNLCK && ! info->was_locked)
|
||||
{
|
||||
info->was_locked=info->lock_type;
|
||||
if (maria_lock_database(info,F_UNLCK))
|
||||
error=my_errno;
|
||||
}
|
||||
#ifdef CANT_OPEN_FILES_TWICE
|
||||
if (info->s->kfile >= 0 && my_close(info->s->kfile,MYF(0)))
|
||||
error = my_errno;
|
||||
if (info->dfile >= 0 && my_close(info->dfile,MYF(0)))
|
||||
error = my_errno;
|
||||
info->s->kfile=info->dfile= -1; /* Files aren't open anymore */
|
||||
break;
|
||||
#endif
|
||||
case HA_PANIC_READ: /* Restore to before WRITE */
|
||||
#ifdef CANT_OPEN_FILES_TWICE
|
||||
{ /* Open closed files */
|
||||
char name_buff[FN_REFLEN];
|
||||
if (info->s->kfile < 0)
|
||||
if ((info->s->kfile= my_open(fn_format(name_buff,info->filename,"",
|
||||
N_NAME_IEXT,4),info->mode,
|
||||
MYF(MY_WME))) < 0)
|
||||
error = my_errno;
|
||||
if (info->dfile < 0)
|
||||
{
|
||||
if ((info->dfile= my_open(fn_format(name_buff,info->filename,"",
|
||||
N_NAME_DEXT,4),info->mode,
|
||||
MYF(MY_WME))) < 0)
|
||||
error = my_errno;
|
||||
info->rec_cache.file=info->dfile;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (info->was_locked)
|
||||
{
|
||||
if (maria_lock_database(info, info->was_locked))
|
||||
error=my_errno;
|
||||
info->was_locked=0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&THR_LOCK_maria);
|
||||
if (flag == HA_PANIC_CLOSE)
|
||||
maria_end();
|
||||
if (!error)
|
||||
DBUG_RETURN(0);
|
||||
DBUG_RETURN(my_errno=error);
|
||||
} /* maria_panic */
|
||||
117
storage/maria/ma_preload.c
Normal file
117
storage/maria/ma_preload.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
Preload indexes into key cache
|
||||
*/
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
|
||||
/*
|
||||
Preload pages of the index file for a table into the key cache
|
||||
|
||||
SYNOPSIS
|
||||
maria_preload()
|
||||
info open table
|
||||
map map of indexes to preload into key cache
|
||||
ignore_leaves only non-leaves pages are to be preloaded
|
||||
|
||||
RETURN VALUE
|
||||
0 if a success. error code - otherwise.
|
||||
|
||||
NOTES.
|
||||
At present pages for all indexes are preloaded.
|
||||
In future only pages for indexes specified in the key_map parameter
|
||||
of the table will be preloaded.
|
||||
*/
|
||||
|
||||
int maria_preload(MARIA_HA *info, ulonglong key_map, my_bool ignore_leaves)
|
||||
{
|
||||
uint i;
|
||||
ulong length, block_length= 0;
|
||||
uchar *buff= NULL;
|
||||
MARIA_SHARE* share= info->s;
|
||||
uint keys= share->state.header.keys;
|
||||
MARIA_KEYDEF *keyinfo= share->keyinfo;
|
||||
my_off_t key_file_length= share->state.state.key_file_length;
|
||||
my_off_t pos= share->base.keystart;
|
||||
DBUG_ENTER("maria_preload");
|
||||
|
||||
if (!keys || !maria_is_any_key_active(key_map) || key_file_length == pos)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
block_length= keyinfo[0].block_length;
|
||||
|
||||
/* Check whether all indexes use the same block size */
|
||||
for (i= 1 ; i < keys ; i++)
|
||||
{
|
||||
if (keyinfo[i].block_length != block_length)
|
||||
DBUG_RETURN(my_errno= HA_ERR_NON_UNIQUE_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
length= info->preload_buff_size/block_length * block_length;
|
||||
set_if_bigger(length, block_length);
|
||||
|
||||
if (!(buff= (uchar *) my_malloc(length, MYF(MY_WME))))
|
||||
DBUG_RETURN(my_errno= HA_ERR_OUT_OF_MEM);
|
||||
|
||||
if (flush_key_blocks(share->key_cache,share->kfile, FLUSH_RELEASE))
|
||||
goto err;
|
||||
|
||||
do
|
||||
{
|
||||
/* Read the next block of index file into the preload buffer */
|
||||
if ((my_off_t) length > (key_file_length-pos))
|
||||
length= (ulong) (key_file_length-pos);
|
||||
if (my_pread(share->kfile, (byte*) buff, length, pos, MYF(MY_FAE|MY_FNABP)))
|
||||
goto err;
|
||||
|
||||
if (ignore_leaves)
|
||||
{
|
||||
uchar *end= buff+length;
|
||||
do
|
||||
{
|
||||
if (_ma_test_if_nod(buff))
|
||||
{
|
||||
if (key_cache_insert(share->key_cache,
|
||||
share->kfile, pos, DFLT_INIT_HITS,
|
||||
(byte*) buff, block_length))
|
||||
goto err;
|
||||
}
|
||||
pos+= block_length;
|
||||
}
|
||||
while ((buff+= block_length) != end);
|
||||
buff= end-length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (key_cache_insert(share->key_cache,
|
||||
share->kfile, pos, DFLT_INIT_HITS,
|
||||
(byte*) buff, length))
|
||||
goto err;
|
||||
pos+= length;
|
||||
}
|
||||
}
|
||||
while (pos != key_file_length);
|
||||
|
||||
my_free((char*) buff, MYF(0));
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
my_free((char*) buff, MYF(MY_ALLOW_ZERO_PTR));
|
||||
DBUG_RETURN(my_errno= errno);
|
||||
}
|
||||
244
storage/maria/ma_range.c
Normal file
244
storage/maria/ma_range.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
Gives a approximated number of how many records there is between two keys.
|
||||
Used when optimizing querries.
|
||||
*/
|
||||
|
||||
#include "maria_def.h"
|
||||
#include "ma_rt_index.h"
|
||||
|
||||
static ha_rows _ma_record_pos(MARIA_HA *info,const byte *key,uint key_len,
|
||||
enum ha_rkey_function search_flag);
|
||||
static double _ma_search_pos(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uchar *key,
|
||||
uint key_len,uint nextflag,my_off_t pos);
|
||||
static uint _ma_keynr(MARIA_HA *info,MARIA_KEYDEF *keyinfo,uchar *page,
|
||||
uchar *keypos,uint *ret_max_key);
|
||||
|
||||
|
||||
/*
|
||||
Estimate how many records there is in a given range
|
||||
|
||||
SYNOPSIS
|
||||
maria_records_in_range()
|
||||
info MARIA handler
|
||||
inx Index to use
|
||||
min_key Min key. Is = 0 if no min range
|
||||
max_key Max key. Is = 0 if no max range
|
||||
|
||||
NOTES
|
||||
We should ONLY return 0 if there is no rows in range
|
||||
|
||||
RETURN
|
||||
HA_POS_ERROR error (or we can't estimate number of rows)
|
||||
number Estimated number of rows
|
||||
*/
|
||||
|
||||
|
||||
ha_rows maria_records_in_range(MARIA_HA *info, int inx, key_range *min_key,
|
||||
key_range *max_key)
|
||||
{
|
||||
ha_rows start_pos,end_pos,res;
|
||||
DBUG_ENTER("maria_records_in_range");
|
||||
|
||||
if ((inx = _ma_check_index(info,inx)) < 0)
|
||||
DBUG_RETURN(HA_POS_ERROR);
|
||||
|
||||
if (fast_ma_readinfo(info))
|
||||
DBUG_RETURN(HA_POS_ERROR);
|
||||
info->update&= (HA_STATE_CHANGED+HA_STATE_ROW_CHANGED);
|
||||
if (info->s->concurrent_insert)
|
||||
rw_rdlock(&info->s->key_root_lock[inx]);
|
||||
|
||||
switch(info->s->keyinfo[inx].key_alg){
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
case HA_KEY_ALG_RTREE:
|
||||
{
|
||||
uchar * key_buff;
|
||||
uint start_key_len;
|
||||
|
||||
key_buff= info->lastkey+info->s->base.max_key_length;
|
||||
start_key_len= _ma_pack_key(info,inx, key_buff,
|
||||
(uchar*) min_key->key, min_key->length,
|
||||
(HA_KEYSEG**) 0);
|
||||
res= maria_rtree_estimate(info, inx, key_buff, start_key_len,
|
||||
maria_read_vec[min_key->flag]);
|
||||
res= res ? res : 1; /* Don't return 0 */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case HA_KEY_ALG_BTREE:
|
||||
default:
|
||||
start_pos= (min_key ?
|
||||
_ma_record_pos(info, min_key->key, min_key->length,
|
||||
min_key->flag) :
|
||||
(ha_rows) 0);
|
||||
end_pos= (max_key ?
|
||||
_ma_record_pos(info, max_key->key, max_key->length,
|
||||
max_key->flag) :
|
||||
info->state->records+ (ha_rows) 1);
|
||||
res= (end_pos < start_pos ? (ha_rows) 0 :
|
||||
(end_pos == start_pos ? (ha_rows) 1 : end_pos-start_pos));
|
||||
if (start_pos == HA_POS_ERROR || end_pos == HA_POS_ERROR)
|
||||
res=HA_POS_ERROR;
|
||||
}
|
||||
|
||||
if (info->s->concurrent_insert)
|
||||
rw_unlock(&info->s->key_root_lock[inx]);
|
||||
fast_ma_writeinfo(info);
|
||||
|
||||
DBUG_PRINT("info",("records: %ld",(ulong) (res)));
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
|
||||
/* Find relative position (in records) for key in index-tree */
|
||||
|
||||
static ha_rows _ma_record_pos(MARIA_HA *info, const byte *key, uint key_len,
|
||||
enum ha_rkey_function search_flag)
|
||||
{
|
||||
uint inx=(uint) info->lastinx, nextflag;
|
||||
MARIA_KEYDEF *keyinfo=info->s->keyinfo+inx;
|
||||
uchar *key_buff;
|
||||
double pos;
|
||||
|
||||
DBUG_ENTER("_ma_record_pos");
|
||||
DBUG_PRINT("enter",("search_flag: %d",search_flag));
|
||||
|
||||
if (key_len == 0)
|
||||
key_len=USE_WHOLE_KEY;
|
||||
key_buff=info->lastkey+info->s->base.max_key_length;
|
||||
key_len= _ma_pack_key(info,inx,key_buff,(uchar*) key,key_len,
|
||||
(HA_KEYSEG**) 0);
|
||||
DBUG_EXECUTE("key", _ma_print_key(DBUG_FILE,keyinfo->seg,
|
||||
(uchar*) key_buff,key_len););
|
||||
nextflag=maria_read_vec[search_flag];
|
||||
if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST)))
|
||||
key_len=USE_WHOLE_KEY;
|
||||
|
||||
pos= _ma_search_pos(info,keyinfo,key_buff,key_len,
|
||||
nextflag | SEARCH_SAVE_BUFF,
|
||||
info->s->state.key_root[inx]);
|
||||
if (pos >= 0.0)
|
||||
{
|
||||
DBUG_PRINT("exit",("pos: %ld",(ulong) (pos*info->state->records)));
|
||||
DBUG_RETURN((ulong) (pos*info->state->records+0.5));
|
||||
}
|
||||
DBUG_RETURN(HA_POS_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/* This is a modified version of _ma_search */
|
||||
/* Returns offset for key in indextable (decimal 0.0 <= x <= 1.0) */
|
||||
|
||||
static double _ma_search_pos(register MARIA_HA *info,
|
||||
register MARIA_KEYDEF *keyinfo,
|
||||
uchar *key, uint key_len, uint nextflag,
|
||||
register my_off_t pos)
|
||||
{
|
||||
int flag;
|
||||
uint nod_flag,keynr,max_keynr;
|
||||
my_bool after_key;
|
||||
uchar *keypos,*buff;
|
||||
double offset;
|
||||
DBUG_ENTER("_ma_search_pos");
|
||||
|
||||
if (pos == HA_OFFSET_ERROR)
|
||||
DBUG_RETURN(0.5);
|
||||
|
||||
if (!(buff= _ma_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,1)))
|
||||
goto err;
|
||||
flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag,
|
||||
&keypos,info->lastkey, &after_key);
|
||||
nod_flag=_ma_test_if_nod(buff);
|
||||
keynr= _ma_keynr(info,keyinfo,buff,keypos,&max_keynr);
|
||||
|
||||
if (flag)
|
||||
{
|
||||
if (flag == MARIA_FOUND_WRONG_KEY)
|
||||
DBUG_RETURN(-1); /* error */
|
||||
/*
|
||||
Didn't found match. keypos points at next (bigger) key
|
||||
Try to find a smaller, better matching key.
|
||||
Matches keynr + [0-1]
|
||||
*/
|
||||
if (flag > 0 && ! nod_flag)
|
||||
offset= 1.0;
|
||||
else if ((offset= _ma_search_pos(info,keyinfo,key,key_len,nextflag,
|
||||
_ma_kpos(nod_flag,keypos))) < 0)
|
||||
DBUG_RETURN(offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Found match. Keypos points at the start of the found key
|
||||
Matches keynr+1
|
||||
*/
|
||||
offset=1.0; /* Matches keynr+1 */
|
||||
if ((nextflag & SEARCH_FIND) && nod_flag &&
|
||||
((keyinfo->flag & (HA_NOSAME | HA_NULL_PART)) != HA_NOSAME ||
|
||||
key_len != USE_WHOLE_KEY))
|
||||
{
|
||||
/*
|
||||
There may be identical keys in the tree. Try to match on of those.
|
||||
Matches keynr + [0-1]
|
||||
*/
|
||||
if ((offset= _ma_search_pos(info,keyinfo,key,key_len,SEARCH_FIND,
|
||||
_ma_kpos(nod_flag,keypos))) < 0)
|
||||
DBUG_RETURN(offset); /* Read error */
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("info",("keynr: %d offset: %g max_keynr: %d nod: %d flag: %d",
|
||||
keynr,offset,max_keynr,nod_flag,flag));
|
||||
DBUG_RETURN((keynr+offset)/(max_keynr+1));
|
||||
err:
|
||||
DBUG_PRINT("exit",("Error: %d",my_errno));
|
||||
DBUG_RETURN (-1.0);
|
||||
}
|
||||
|
||||
|
||||
/* Get keynummer of current key and max number of keys in nod */
|
||||
|
||||
static uint _ma_keynr(MARIA_HA *info, register MARIA_KEYDEF *keyinfo, uchar *page,
|
||||
uchar *keypos, uint *ret_max_key)
|
||||
{
|
||||
uint nod_flag,keynr,max_key;
|
||||
uchar t_buff[HA_MAX_KEY_BUFF],*end;
|
||||
|
||||
end= page+maria_getint(page);
|
||||
nod_flag=_ma_test_if_nod(page);
|
||||
page+=2+nod_flag;
|
||||
|
||||
if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)))
|
||||
{
|
||||
*ret_max_key= (uint) (end-page)/(keyinfo->keylength+nod_flag);
|
||||
return (uint) (keypos-page)/(keyinfo->keylength+nod_flag);
|
||||
}
|
||||
|
||||
max_key=keynr=0;
|
||||
t_buff[0]=0; /* Safety */
|
||||
while (page < end)
|
||||
{
|
||||
if (!(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff))
|
||||
return 0; /* Error */
|
||||
max_key++;
|
||||
if (page == keypos)
|
||||
keynr=max_key;
|
||||
}
|
||||
*ret_max_key=max_key;
|
||||
return(keynr);
|
||||
}
|
||||
268
storage/maria/ma_recovery.c
Normal file
268
storage/maria/ma_recovery.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3072 Maria recovery
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/* Here is the implementation of this module */
|
||||
|
||||
#include "page_cache.h"
|
||||
#include "least_recently_dirtied.h"
|
||||
#include "transaction.h"
|
||||
#include "share.h"
|
||||
#include "log.h"
|
||||
|
||||
typedef struct st_record_type_properties {
|
||||
/* used for debug error messages or "maria_read_log" command-line tool: */
|
||||
char *name,
|
||||
my_bool record_ends_group;
|
||||
/* a function to execute when we see the record during the REDO phase */
|
||||
int (*record_execute_in_redo_phase)(RECORD *); /* param will be record header instead later */
|
||||
/* a function to execute when we see the record during the UNDO phase */
|
||||
int (*record_execute_in_undo_phase)(RECORD *); /* param will be record header instead later */
|
||||
} RECORD_TYPE_PROPERTIES;
|
||||
|
||||
int no_op(RECORD *) {return 0};
|
||||
|
||||
RECORD_TYPE_PROPERTIES all_record_type_properties[]=
|
||||
{
|
||||
/* listed here in the order of the "log records type" enumeration */
|
||||
{"REDO_INSERT_HEAD", FALSE, redo_insert_head_execute_in_redo_phase, no_op},
|
||||
...,
|
||||
{"UNDO_INSERT" , TRUE , undo_insert_execute_in_redo_phase, undo_insert_execute_in_undo_phase},
|
||||
{"COMMIT", , TRUE , commit_execute_in_redo_phase, no_op},
|
||||
...
|
||||
};
|
||||
|
||||
int redo_insert_head_execute_in_redo_phase(RECORD *record)
|
||||
{
|
||||
/* write the data to the proper page */
|
||||
}
|
||||
|
||||
int undo_insert_execute_in_redo_phase(RECORD *record)
|
||||
{
|
||||
trans_table[short_trans_id].undo_lsn= record.lsn;
|
||||
/* don't restore the old version of the row */
|
||||
}
|
||||
|
||||
int undo_insert_execute_in_undo_phase(RECORD *record)
|
||||
{
|
||||
/* restore the old version of the row */
|
||||
trans_table[short_trans_id].undo_lsn= record.prev_undo_lsn;
|
||||
}
|
||||
|
||||
int commit_execute_in_redo_phase(RECORD *record)
|
||||
{
|
||||
trans_table[short_trans_id].state= COMMITTED;
|
||||
/*
|
||||
and that's all: the delete/update handler should not be woken up! as there
|
||||
may be REDO for purge further in the log.
|
||||
*/
|
||||
}
|
||||
|
||||
#define record_ends_group(R) \
|
||||
all_record_type_properties[(R)->type].record_ends_group)
|
||||
|
||||
#define execute_log_record_in_redo_phase(R) \
|
||||
all_record_type_properties[(R).type].record_execute_in_redo_phase(R)
|
||||
|
||||
|
||||
int recovery()
|
||||
{
|
||||
control_file_create_or_open();
|
||||
/*
|
||||
init log handler: tell it that we are going to do large reads of the
|
||||
log, sequential and backward. Log handler could decide to alloc a big
|
||||
read-only IO_CACHE for this, or use its usual page cache.
|
||||
*/
|
||||
|
||||
/* read checkpoint log record from log handler */
|
||||
RECORD *checkpoint_record= log_read_record(last_checkpoint_lsn_at_start);
|
||||
|
||||
/* parse this record, build structs (dirty_pages, transactions table, file_map) */
|
||||
/*
|
||||
read log records (note: sometimes only the header is needed, for ex during
|
||||
REDO phase only the header of UNDO is needed, not the 4G blob in the
|
||||
variable-length part, so I could use that; however for PREPARE (which is a
|
||||
variable-length record) I'll need to read the full record in the REDO
|
||||
phase):
|
||||
*/
|
||||
|
||||
/**** REDO PHASE *****/
|
||||
|
||||
record= log_read_record(min(rec_lsn, ...)); /* later, read only header */
|
||||
|
||||
/*
|
||||
if log handler knows the end LSN of the log, we could print here how many
|
||||
MB of log we have to read (to give an idea of the time), and print
|
||||
progress notes.
|
||||
*/
|
||||
|
||||
while (record != NULL)
|
||||
{
|
||||
/*
|
||||
A complete group is a set of log records with an "end mark" record
|
||||
(e.g. a set of REDOs for an operation, terminated by an UNDO for this
|
||||
operation); if there is no "end mark" record the group is incomplete
|
||||
and won't be executed.
|
||||
*/
|
||||
if (record_ends_group(record)
|
||||
{
|
||||
if (trans_table[record.short_trans_id].group_start_lsn != 0)
|
||||
{
|
||||
/*
|
||||
There is a complete group for this transaction, containing more than
|
||||
this event.
|
||||
We're going to read recently read log records:
|
||||
for this log_read_record() to be efficient (not touch the disk),
|
||||
log handler could cache recently read pages
|
||||
(can just use an IO_CACHE of 10 MB to read the log, or the normal
|
||||
log handler page cache).
|
||||
Without it only OS file cache will help.
|
||||
*/
|
||||
record2=
|
||||
log_read_record(trans_table[record.short_trans_id].group_start_lsn);
|
||||
|
||||
do
|
||||
{
|
||||
if (record2.short_trans_id == record.short_trans_id)
|
||||
execute_log_record_in_redo_phase(record2); /* it's in our group */
|
||||
record2= log_read_next_record();
|
||||
}
|
||||
while (record2.lsn < record.lsn);
|
||||
trans_table[record.short_trans_id].group_start_lsn= 0; /* group finished */
|
||||
}
|
||||
execute_log_record_in_redo_phase(record);
|
||||
}
|
||||
else /* record does not end group */
|
||||
{
|
||||
/* just record the fact, can't know if can execute yet */
|
||||
if (trans_table[short_trans_id].group_start_lsn == 0) /* group not yet started */
|
||||
trans_table[short_trans_id].group_start_lsn= record.lsn;
|
||||
}
|
||||
|
||||
/*
|
||||
Later we can optimize: instead of "execute_log_record(record2)", do
|
||||
copy_record_into_exec_buffer(record2):
|
||||
this will just copy record into a multi-record (10 MB?) memory buffer,
|
||||
and when buffer is full, will do sorting of REDOs per
|
||||
page id and execute them.
|
||||
This sorting will enable us to do more sequential reads of the
|
||||
data/index pages.
|
||||
Note that updating bitmap pages (when we have executed a REDO for a page
|
||||
we update its bitmap page) may break the sequential read of pages,
|
||||
so maybe we should read and cache bitmap pages in the beginning.
|
||||
Or ok the sequence will be broken, but quickly all bitmap pages will be
|
||||
in memory and so the sequence will not be broken anymore.
|
||||
Sorting could even determine, based on physical device of files
|
||||
("st_dev" in stat()), that some files should be should be taken by
|
||||
different threads, if we want to do parallism.
|
||||
*/
|
||||
/*
|
||||
Here's how to read a complete variable-length record if needed:
|
||||
<sanja> read the header, allocate buffer of record length, read whole
|
||||
record.
|
||||
*/
|
||||
record= log_read_next_record();
|
||||
}
|
||||
|
||||
/*
|
||||
Earlier or here, create true transactions in TM.
|
||||
If done earlier, note that TM should not wake up the delete/update handler
|
||||
when it receives a commit info, as existing REDO for purge may exist in
|
||||
the log, and so the delete/update handler may do changes which conflict
|
||||
with these REDOs.
|
||||
Even if done here, better to not wake it up now as we're going to free the
|
||||
page cache.
|
||||
|
||||
MikaelR suggests: support checkpoints during REDO phase too: do checkpoint
|
||||
after a certain amount of log records have been executed. This helps
|
||||
against repeated crashes. Those checkpoints could not be user-requested
|
||||
(as engine is not communicating during the REDO phase), so they would be
|
||||
automatic: this changes the original assumption that we don't write to the
|
||||
log while in the REDO phase, but why not. How often should we checkpoint?
|
||||
*/
|
||||
|
||||
/*
|
||||
We want to have two steps:
|
||||
engine->recover_with_max_memory();
|
||||
next_engine->recover_with_max_memory();
|
||||
engine->init_with_normal_memory();
|
||||
next_engine->init_with_normal_memory();
|
||||
So: in recover_with_max_memory() allocate a giant page cache, do REDO
|
||||
phase, then all page cache is flushed and emptied and freed (only retain
|
||||
small structures like TM): take full checkpoint, which is useful if
|
||||
next engine crashes in its recovery the next second.
|
||||
Destroy all shares (maria_close()), then at init_with_normal_memory() we
|
||||
do this:
|
||||
*/
|
||||
|
||||
/**** UNDO PHASE *****/
|
||||
|
||||
print_information_to_error_log(nb of trans to roll back, nb of prepared trans);
|
||||
|
||||
/*
|
||||
Launch one or more threads to do the background rollback. Don't wait for
|
||||
them to complete their rollback (background rollback; for debugging, we
|
||||
can have an option which waits). Set a counter (total_of_rollback_threads)
|
||||
to the number of threads to lauch.
|
||||
|
||||
Note that InnoDB's rollback-in-background works as long as InnoDB is the
|
||||
last engine to recover, otherwise MySQL will refuse new connections until
|
||||
the last engine has recovered so it's not "background" from the user's
|
||||
point of view. InnoDB is near top of sys_table_types so all others
|
||||
(e.g. BDB) recover after it... So it's really "online rollback" only if
|
||||
InnoDB is the only engine.
|
||||
*/
|
||||
|
||||
/* wake up delete/update handler */
|
||||
/* tell the TM that it can now accept new transactions */
|
||||
|
||||
/*
|
||||
mark that checkpoint requests are now allowed.
|
||||
*/
|
||||
}
|
||||
|
||||
pthread_handler_decl rollback_background_thread()
|
||||
{
|
||||
/*
|
||||
execute the normal runtime-rollback code for a bunch of transactions.
|
||||
*/
|
||||
while (trans in list_of_trans_to_rollback_by_this_thread)
|
||||
{
|
||||
while (trans->undo_lsn != 0)
|
||||
{
|
||||
/* this is the normal runtime-rollback code: */
|
||||
record= log_read_record(trans->undo_lsn);
|
||||
execute_log_record_in_undo_phase(record);
|
||||
trans->undo_lsn= record.prev_undo_lsn;
|
||||
}
|
||||
/* remove trans from list */
|
||||
}
|
||||
lock_mutex(rollback_threads); /* or atomic counter */
|
||||
if (--total_of_rollback_threads == 0)
|
||||
{
|
||||
/*
|
||||
All rollback threads are done. Print "rollback finished" to the error
|
||||
log and take a full checkpoint.
|
||||
*/
|
||||
}
|
||||
unlock_mutex(rollback_threads);
|
||||
pthread_exit();
|
||||
}
|
||||
26
storage/maria/ma_recovery.h
Normal file
26
storage/maria/ma_recovery.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
WL#3072 Maria recovery
|
||||
First version written by Guilhem Bichot on 2006-04-27.
|
||||
Does not compile yet.
|
||||
*/
|
||||
|
||||
/* This is the interface of this module. */
|
||||
|
||||
/* Performs recovery of the engine at start */
|
||||
int recovery();
|
||||
61
storage/maria/ma_rename.c
Normal file
61
storage/maria/ma_rename.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/*
|
||||
Rename a table
|
||||
*/
|
||||
|
||||
#include "ma_fulltext.h"
|
||||
|
||||
int maria_rename(const char *old_name, const char *new_name)
|
||||
{
|
||||
char from[FN_REFLEN],to[FN_REFLEN];
|
||||
#ifdef USE_RAID
|
||||
uint raid_type=0,raid_chunks=0;
|
||||
#endif
|
||||
DBUG_ENTER("maria_rename");
|
||||
|
||||
#ifdef EXTRA_DEBUG
|
||||
_ma_check_table_is_closed(old_name,"rename old_table");
|
||||
_ma_check_table_is_closed(new_name,"rename new table2");
|
||||
#endif
|
||||
#ifdef USE_RAID
|
||||
{
|
||||
MARIA_HA *info;
|
||||
if (!(info=maria_open(old_name, O_RDONLY, 0)))
|
||||
DBUG_RETURN(my_errno);
|
||||
raid_type = info->s->base.raid_type;
|
||||
raid_chunks = info->s->base.raid_chunks;
|
||||
maria_close(info);
|
||||
}
|
||||
#ifdef EXTRA_DEBUG
|
||||
_ma_check_table_is_closed(old_name,"rename raidcheck");
|
||||
#endif
|
||||
#endif /* USE_RAID */
|
||||
|
||||
fn_format(from,old_name,"",MARIA_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
|
||||
fn_format(to,new_name,"",MARIA_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
|
||||
if (my_rename_with_symlink(from, to, MYF(MY_WME)))
|
||||
DBUG_RETURN(my_errno);
|
||||
fn_format(from,old_name,"",MARIA_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
|
||||
fn_format(to,new_name,"",MARIA_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
|
||||
#ifdef USE_RAID
|
||||
if (raid_type)
|
||||
DBUG_RETURN(my_raid_rename(from, to, raid_chunks, MYF(MY_WME)) ? my_errno :
|
||||
0);
|
||||
#endif
|
||||
DBUG_RETURN(my_rename_with_symlink(from, to,MYF(MY_WME)) ? my_errno : 0);
|
||||
}
|
||||
27
storage/maria/ma_rfirst.c
Normal file
27
storage/maria/ma_rfirst.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Read first row through a specfic key */
|
||||
|
||||
int maria_rfirst(MARIA_HA *info, byte *buf, int inx)
|
||||
{
|
||||
DBUG_ENTER("maria_rfirst");
|
||||
info->lastpos= HA_OFFSET_ERROR;
|
||||
info->update|= HA_STATE_PREV_FOUND;
|
||||
DBUG_RETURN(maria_rnext(info,buf,inx));
|
||||
} /* maria_rfirst */
|
||||
155
storage/maria/ma_rkey.c
Normal file
155
storage/maria/ma_rkey.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Read record based on a key */
|
||||
|
||||
#include "maria_def.h"
|
||||
#include "ma_rt_index.h"
|
||||
|
||||
/* Read a record using key */
|
||||
/* Ordinary search_flag is 0 ; Give error if no record with key */
|
||||
|
||||
int maria_rkey(MARIA_HA *info, byte *buf, int inx, const byte *key, uint key_len,
|
||||
enum ha_rkey_function search_flag)
|
||||
{
|
||||
uchar *key_buff;
|
||||
MARIA_SHARE *share=info->s;
|
||||
MARIA_KEYDEF *keyinfo;
|
||||
HA_KEYSEG *last_used_keyseg;
|
||||
uint pack_key_length, use_key_length, nextflag;
|
||||
DBUG_ENTER("maria_rkey");
|
||||
DBUG_PRINT("enter", ("base: %lx buf: %lx inx: %d search_flag: %d",
|
||||
(long) info, (long) buf, inx, search_flag));
|
||||
|
||||
if ((inx = _ma_check_index(info,inx)) < 0)
|
||||
DBUG_RETURN(my_errno);
|
||||
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
info->last_key_func= search_flag;
|
||||
keyinfo= share->keyinfo + inx;
|
||||
|
||||
if (info->once_flags & USE_PACKED_KEYS)
|
||||
{
|
||||
info->once_flags&= ~USE_PACKED_KEYS; /* Reset flag */
|
||||
/*
|
||||
key is already packed!; This happens when we are using a MERGE TABLE
|
||||
*/
|
||||
key_buff=info->lastkey+info->s->base.max_key_length;
|
||||
pack_key_length= key_len;
|
||||
bmove(key_buff,key,key_len);
|
||||
last_used_keyseg= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (key_len == 0)
|
||||
key_len=USE_WHOLE_KEY;
|
||||
/* Save the packed key for later use in the second buffer of lastkey. */
|
||||
key_buff=info->lastkey+info->s->base.max_key_length;
|
||||
pack_key_length= _ma_pack_key(info,(uint) inx, key_buff, (uchar*) key,
|
||||
key_len, &last_used_keyseg);
|
||||
/* Save packed_key_length for use by the MERGE engine. */
|
||||
info->pack_key_length= pack_key_length;
|
||||
DBUG_EXECUTE("key", _ma_print_key(DBUG_FILE, keyinfo->seg,
|
||||
key_buff, pack_key_length););
|
||||
}
|
||||
|
||||
if (fast_ma_readinfo(info))
|
||||
goto err;
|
||||
if (share->concurrent_insert)
|
||||
rw_rdlock(&share->key_root_lock[inx]);
|
||||
|
||||
nextflag=maria_read_vec[search_flag];
|
||||
use_key_length=pack_key_length;
|
||||
if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST)))
|
||||
use_key_length=USE_WHOLE_KEY;
|
||||
|
||||
switch (info->s->keyinfo[inx].key_alg) {
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
case HA_KEY_ALG_RTREE:
|
||||
if (maria_rtree_find_first(info,inx,key_buff,use_key_length,nextflag) < 0)
|
||||
{
|
||||
maria_print_error(info->s, HA_ERR_CRASHED);
|
||||
my_errno=HA_ERR_CRASHED;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case HA_KEY_ALG_BTREE:
|
||||
default:
|
||||
if (!_ma_search(info, keyinfo, key_buff, use_key_length,
|
||||
maria_read_vec[search_flag], info->s->state.key_root[inx]))
|
||||
{
|
||||
/*
|
||||
If we are searching for an exact key (including the data pointer)
|
||||
and this was added by an concurrent insert,
|
||||
then the result is "key not found".
|
||||
*/
|
||||
if ((search_flag == HA_READ_KEY_EXACT) &&
|
||||
(info->lastpos >= info->state->data_file_length))
|
||||
{
|
||||
my_errno= HA_ERR_KEY_NOT_FOUND;
|
||||
info->lastpos= HA_OFFSET_ERROR;
|
||||
}
|
||||
else while (info->lastpos >= info->state->data_file_length)
|
||||
{
|
||||
/*
|
||||
Skip rows that are inserted by other threads since we got a lock
|
||||
Note that this can only happen if we are not searching after an
|
||||
exact key, because the keys are sorted according to position
|
||||
*/
|
||||
|
||||
if (_ma_search_next(info, keyinfo, info->lastkey,
|
||||
info->lastkey_length,
|
||||
maria_readnext_vec[search_flag],
|
||||
info->s->state.key_root[inx]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (share->concurrent_insert)
|
||||
rw_unlock(&share->key_root_lock[inx]);
|
||||
|
||||
/* Calculate length of the found key; Used by maria_rnext_same */
|
||||
if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg &&
|
||||
info->lastpos != HA_OFFSET_ERROR)
|
||||
info->last_rkey_length= _ma_keylength_part(keyinfo, info->lastkey,
|
||||
last_used_keyseg);
|
||||
else
|
||||
info->last_rkey_length= pack_key_length;
|
||||
|
||||
/* Check if we don't want to have record back, only error message */
|
||||
if (!buf)
|
||||
DBUG_RETURN(info->lastpos == HA_OFFSET_ERROR ? my_errno : 0);
|
||||
|
||||
if (!(*info->read_record)(info,info->lastpos,buf))
|
||||
{
|
||||
info->update|= HA_STATE_AKTIV; /* Record is read */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
info->lastpos = HA_OFFSET_ERROR; /* Didn't find key */
|
||||
|
||||
/* Store last used key as a base for read next */
|
||||
memcpy(info->lastkey,key_buff,pack_key_length);
|
||||
info->last_rkey_length= pack_key_length;
|
||||
bzero((char*) info->lastkey+pack_key_length,info->s->base.rec_reflength);
|
||||
info->lastkey_length=pack_key_length+info->s->base.rec_reflength;
|
||||
|
||||
if (search_flag == HA_READ_AFTER_KEY)
|
||||
info->update|=HA_STATE_NEXT_FOUND; /* Previous gives last row */
|
||||
err:
|
||||
DBUG_RETURN(my_errno);
|
||||
} /* _ma_rkey */
|
||||
27
storage/maria/ma_rlast.c
Normal file
27
storage/maria/ma_rlast.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/* Read last row with the same key as the previous read. */
|
||||
|
||||
int maria_rlast(MARIA_HA *info, byte *buf, int inx)
|
||||
{
|
||||
DBUG_ENTER("maria_rlast");
|
||||
info->lastpos= HA_OFFSET_ERROR;
|
||||
info->update|= HA_STATE_NEXT_FOUND;
|
||||
DBUG_RETURN(maria_rprev(info,buf,inx));
|
||||
} /* maria_rlast */
|
||||
122
storage/maria/ma_rnext.c
Normal file
122
storage/maria/ma_rnext.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
#include "ma_rt_index.h"
|
||||
|
||||
/*
|
||||
Read next row with the same key as previous read
|
||||
One may have done a write, update or delete of the previous row.
|
||||
NOTE! Even if one changes the previous row, the next read is done
|
||||
based on the position of the last used key!
|
||||
*/
|
||||
|
||||
int maria_rnext(MARIA_HA *info, byte *buf, int inx)
|
||||
{
|
||||
int error,changed;
|
||||
uint flag;
|
||||
DBUG_ENTER("maria_rnext");
|
||||
|
||||
if ((inx = _ma_check_index(info,inx)) < 0)
|
||||
DBUG_RETURN(my_errno);
|
||||
flag=SEARCH_BIGGER; /* Read next */
|
||||
if (info->lastpos == HA_OFFSET_ERROR && info->update & HA_STATE_PREV_FOUND)
|
||||
flag=0; /* Read first */
|
||||
|
||||
if (fast_ma_readinfo(info))
|
||||
DBUG_RETURN(my_errno);
|
||||
if (info->s->concurrent_insert)
|
||||
rw_rdlock(&info->s->key_root_lock[inx]);
|
||||
changed= _ma_test_if_changed(info);
|
||||
if (!flag)
|
||||
{
|
||||
switch(info->s->keyinfo[inx].key_alg){
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
case HA_KEY_ALG_RTREE:
|
||||
error=maria_rtree_get_first(info,inx,info->lastkey_length);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEY_ALG_BTREE:
|
||||
default:
|
||||
error= _ma_search_first(info,info->s->keyinfo+inx,
|
||||
info->s->state.key_root[inx]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (info->s->keyinfo[inx].key_alg) {
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
case HA_KEY_ALG_RTREE:
|
||||
/*
|
||||
Note that rtree doesn't support that the table
|
||||
may be changed since last call, so we do need
|
||||
to skip rows inserted by other threads like in btree
|
||||
*/
|
||||
error= maria_rtree_get_next(info,inx,info->lastkey_length);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEY_ALG_BTREE:
|
||||
default:
|
||||
if (!changed)
|
||||
error= _ma_search_next(info,info->s->keyinfo+inx,info->lastkey,
|
||||
info->lastkey_length,flag,
|
||||
info->s->state.key_root[inx]);
|
||||
else
|
||||
error= _ma_search(info,info->s->keyinfo+inx,info->lastkey,
|
||||
USE_WHOLE_KEY,flag, info->s->state.key_root[inx]);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->s->concurrent_insert)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
while (info->lastpos >= info->state->data_file_length)
|
||||
{
|
||||
/* Skip rows inserted by other threads since we got a lock */
|
||||
if ((error= _ma_search_next(info,info->s->keyinfo+inx,
|
||||
info->lastkey,
|
||||
info->lastkey_length,
|
||||
SEARCH_BIGGER,
|
||||
info->s->state.key_root[inx])))
|
||||
break;
|
||||
}
|
||||
}
|
||||
rw_unlock(&info->s->key_root_lock[inx]);
|
||||
}
|
||||
/* Don't clear if database-changed */
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
info->update|= HA_STATE_NEXT_FOUND;
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (my_errno == HA_ERR_KEY_NOT_FOUND)
|
||||
my_errno=HA_ERR_END_OF_FILE;
|
||||
}
|
||||
else if (!buf)
|
||||
{
|
||||
DBUG_RETURN(info->lastpos==HA_OFFSET_ERROR ? my_errno : 0);
|
||||
}
|
||||
else if (!(*info->read_record)(info,info->lastpos,buf))
|
||||
{
|
||||
info->update|= HA_STATE_AKTIV; /* Record is read */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
DBUG_PRINT("error",("Got error: %d, errno: %d",error, my_errno));
|
||||
DBUG_RETURN(my_errno);
|
||||
} /* maria_rnext */
|
||||
105
storage/maria/ma_rnext_same.c
Normal file
105
storage/maria/ma_rnext_same.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
#include "ma_rt_index.h"
|
||||
|
||||
/*
|
||||
Read next row with the same key as previous read, but abort if
|
||||
the key changes.
|
||||
One may have done a write, update or delete of the previous row.
|
||||
NOTE! Even if one changes the previous row, the next read is done
|
||||
based on the position of the last used key!
|
||||
*/
|
||||
|
||||
int maria_rnext_same(MARIA_HA *info, byte *buf)
|
||||
{
|
||||
int error;
|
||||
uint inx,not_used[2];
|
||||
MARIA_KEYDEF *keyinfo;
|
||||
DBUG_ENTER("maria_rnext_same");
|
||||
|
||||
if ((int) (inx=info->lastinx) < 0 || info->lastpos == HA_OFFSET_ERROR)
|
||||
DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX);
|
||||
keyinfo=info->s->keyinfo+inx;
|
||||
if (fast_ma_readinfo(info))
|
||||
DBUG_RETURN(my_errno);
|
||||
|
||||
if (info->s->concurrent_insert)
|
||||
rw_rdlock(&info->s->key_root_lock[inx]);
|
||||
|
||||
switch (keyinfo->key_alg)
|
||||
{
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
case HA_KEY_ALG_RTREE:
|
||||
if ((error=maria_rtree_find_next(info,inx,
|
||||
maria_read_vec[info->last_key_func])))
|
||||
{
|
||||
error=1;
|
||||
my_errno=HA_ERR_END_OF_FILE;
|
||||
info->lastpos= HA_OFFSET_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case HA_KEY_ALG_BTREE:
|
||||
default:
|
||||
if (!(info->update & HA_STATE_RNEXT_SAME))
|
||||
{
|
||||
/* First rnext_same; Store old key */
|
||||
memcpy(info->lastkey2,info->lastkey,info->last_rkey_length);
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
if ((error= _ma_search_next(info,keyinfo,info->lastkey,
|
||||
info->lastkey_length,SEARCH_BIGGER,
|
||||
info->s->state.key_root[inx])))
|
||||
break;
|
||||
if (ha_key_cmp(keyinfo->seg, info->lastkey, info->lastkey2,
|
||||
info->last_rkey_length, SEARCH_FIND, not_used))
|
||||
{
|
||||
error=1;
|
||||
my_errno=HA_ERR_END_OF_FILE;
|
||||
info->lastpos= HA_OFFSET_ERROR;
|
||||
break;
|
||||
}
|
||||
/* Skip rows that are inserted by other threads since we got a lock */
|
||||
if (info->lastpos < info->state->data_file_length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (info->s->concurrent_insert)
|
||||
rw_unlock(&info->s->key_root_lock[inx]);
|
||||
/* Don't clear if database-changed */
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
info->update|= HA_STATE_NEXT_FOUND | HA_STATE_RNEXT_SAME;
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (my_errno == HA_ERR_KEY_NOT_FOUND)
|
||||
my_errno=HA_ERR_END_OF_FILE;
|
||||
}
|
||||
else if (!buf)
|
||||
{
|
||||
DBUG_RETURN(info->lastpos==HA_OFFSET_ERROR ? my_errno : 0);
|
||||
}
|
||||
else if (!(*info->read_record)(info,info->lastpos,buf))
|
||||
{
|
||||
info->update|= HA_STATE_AKTIV; /* Record is read */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
DBUG_RETURN(my_errno);
|
||||
} /* maria_rnext_same */
|
||||
88
storage/maria/ma_rprev.c
Normal file
88
storage/maria/ma_rprev.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/*
|
||||
Read previous row with the same key as previous read
|
||||
One may have done a write, update or delete of the previous row.
|
||||
NOTE! Even if one changes the previous row, the next read is done
|
||||
based on the position of the last used key!
|
||||
*/
|
||||
|
||||
int maria_rprev(MARIA_HA *info, byte *buf, int inx)
|
||||
{
|
||||
int error,changed;
|
||||
register uint flag;
|
||||
MARIA_SHARE *share=info->s;
|
||||
DBUG_ENTER("maria_rprev");
|
||||
|
||||
if ((inx = _ma_check_index(info,inx)) < 0)
|
||||
DBUG_RETURN(my_errno);
|
||||
flag=SEARCH_SMALLER; /* Read previous */
|
||||
if (info->lastpos == HA_OFFSET_ERROR && info->update & HA_STATE_NEXT_FOUND)
|
||||
flag=0; /* Read last */
|
||||
|
||||
if (fast_ma_readinfo(info))
|
||||
DBUG_RETURN(my_errno);
|
||||
changed= _ma_test_if_changed(info);
|
||||
if (share->concurrent_insert)
|
||||
rw_rdlock(&share->key_root_lock[inx]);
|
||||
if (!flag)
|
||||
error= _ma_search_last(info, share->keyinfo+inx,
|
||||
share->state.key_root[inx]);
|
||||
else if (!changed)
|
||||
error= _ma_search_next(info,share->keyinfo+inx,info->lastkey,
|
||||
info->lastkey_length,flag,
|
||||
share->state.key_root[inx]);
|
||||
else
|
||||
error= _ma_search(info,share->keyinfo+inx,info->lastkey,
|
||||
USE_WHOLE_KEY, flag, share->state.key_root[inx]);
|
||||
|
||||
if (share->concurrent_insert)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
while (info->lastpos >= info->state->data_file_length)
|
||||
{
|
||||
/* Skip rows that are inserted by other threads since we got a lock */
|
||||
if ((error= _ma_search_next(info,share->keyinfo+inx,info->lastkey,
|
||||
info->lastkey_length,
|
||||
SEARCH_SMALLER,
|
||||
share->state.key_root[inx])))
|
||||
break;
|
||||
}
|
||||
}
|
||||
rw_unlock(&share->key_root_lock[inx]);
|
||||
}
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
info->update|= HA_STATE_PREV_FOUND;
|
||||
if (error)
|
||||
{
|
||||
if (my_errno == HA_ERR_KEY_NOT_FOUND)
|
||||
my_errno=HA_ERR_END_OF_FILE;
|
||||
}
|
||||
else if (!buf)
|
||||
{
|
||||
DBUG_RETURN(info->lastpos==HA_OFFSET_ERROR ? my_errno : 0);
|
||||
}
|
||||
else if (!(*info->read_record)(info,info->lastpos,buf))
|
||||
{
|
||||
info->update|= HA_STATE_AKTIV; /* Record is read */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
DBUG_RETURN(my_errno);
|
||||
} /* maria_rprev */
|
||||
60
storage/maria/ma_rrnd.c
Normal file
60
storage/maria/ma_rrnd.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Read a record with random-access. The position to the record must
|
||||
get by MARIA_HA. The next record can be read with pos= MARIA_POS_ERROR */
|
||||
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/*
|
||||
Read a row based on position.
|
||||
If filepos= HA_OFFSET_ERROR then read next row
|
||||
Return values
|
||||
Returns one of following values:
|
||||
0 = Ok.
|
||||
HA_ERR_RECORD_DELETED = Record is deleted.
|
||||
HA_ERR_END_OF_FILE = EOF.
|
||||
*/
|
||||
|
||||
int maria_rrnd(MARIA_HA *info, byte *buf, register my_off_t filepos)
|
||||
{
|
||||
my_bool skip_deleted_blocks;
|
||||
DBUG_ENTER("maria_rrnd");
|
||||
|
||||
skip_deleted_blocks=0;
|
||||
|
||||
if (filepos == HA_OFFSET_ERROR)
|
||||
{
|
||||
skip_deleted_blocks=1;
|
||||
if (info->lastpos == HA_OFFSET_ERROR) /* First read ? */
|
||||
filepos= info->s->pack.header_length; /* Read first record */
|
||||
else
|
||||
filepos= info->nextpos;
|
||||
}
|
||||
|
||||
if (info->once_flags & RRND_PRESERVE_LASTINX)
|
||||
info->once_flags&= ~RRND_PRESERVE_LASTINX;
|
||||
else
|
||||
info->lastinx= -1; /* Can't forward or backward */
|
||||
/* Init all but update-flag */
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
|
||||
if (info->opt_flag & WRITE_CACHE_USED && flush_io_cache(&info->rec_cache))
|
||||
DBUG_RETURN(my_errno);
|
||||
|
||||
DBUG_RETURN ((*info->s->read_rnd)(info,buf,filepos,skip_deleted_blocks));
|
||||
}
|
||||
66
storage/maria/ma_rsame.c
Normal file
66
storage/maria/ma_rsame.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
/*
|
||||
** Find current row with read on position or read on key
|
||||
** If inx >= 0 find record using key
|
||||
** Return values:
|
||||
** 0 = Ok.
|
||||
** HA_ERR_KEY_NOT_FOUND = Row is deleted
|
||||
** HA_ERR_END_OF_FILE = End of file
|
||||
*/
|
||||
|
||||
|
||||
int maria_rsame(MARIA_HA *info, byte *record, int inx)
|
||||
{
|
||||
DBUG_ENTER("maria_rsame");
|
||||
|
||||
if (inx != -1 && ! maria_is_key_active(info->s->state.key_map, inx))
|
||||
{
|
||||
DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX);
|
||||
}
|
||||
if (info->lastpos == HA_OFFSET_ERROR || info->update & HA_STATE_DELETED)
|
||||
{
|
||||
DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); /* No current record */
|
||||
}
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
|
||||
/* Read row from data file */
|
||||
if (fast_ma_readinfo(info))
|
||||
DBUG_RETURN(my_errno);
|
||||
|
||||
if (inx >= 0)
|
||||
{
|
||||
info->lastinx=inx;
|
||||
info->lastkey_length= _ma_make_key(info,(uint) inx,info->lastkey,record,
|
||||
info->lastpos);
|
||||
if (info->s->concurrent_insert)
|
||||
rw_rdlock(&info->s->key_root_lock[inx]);
|
||||
VOID(_ma_search(info,info->s->keyinfo+inx,info->lastkey, USE_WHOLE_KEY,
|
||||
SEARCH_SAME,
|
||||
info->s->state.key_root[inx]));
|
||||
if (info->s->concurrent_insert)
|
||||
rw_unlock(&info->s->key_root_lock[inx]);
|
||||
}
|
||||
|
||||
if (!(*info->read_record)(info,info->lastpos,record))
|
||||
DBUG_RETURN(0);
|
||||
if (my_errno == HA_ERR_RECORD_DELETED)
|
||||
my_errno=HA_ERR_KEY_NOT_FOUND;
|
||||
DBUG_RETURN(my_errno);
|
||||
} /* maria_rsame */
|
||||
57
storage/maria/ma_rsamepos.c
Normal file
57
storage/maria/ma_rsamepos.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* read record through position and fix key-position */
|
||||
/* As maria_rsame but supply a position */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
|
||||
/*
|
||||
** If inx >= 0 update index pointer
|
||||
** Returns one of the following values:
|
||||
** 0 = Ok.
|
||||
** HA_ERR_KEY_NOT_FOUND = Row is deleted
|
||||
** HA_ERR_END_OF_FILE = End of file
|
||||
*/
|
||||
|
||||
int maria_rsame_with_pos(MARIA_HA *info, byte *record, int inx, my_off_t filepos)
|
||||
{
|
||||
DBUG_ENTER("maria_rsame_with_pos");
|
||||
DBUG_PRINT("enter",("index: %d filepos: %ld", inx, (long) filepos));
|
||||
|
||||
if (inx < -1 || (inx >= 0 && !maria_is_key_active(info->s->state.key_map, inx)))
|
||||
{
|
||||
DBUG_RETURN(my_errno=HA_ERR_WRONG_INDEX);
|
||||
}
|
||||
|
||||
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
|
||||
if ((*info->s->read_rnd)(info,record,filepos,0))
|
||||
{
|
||||
if (my_errno == HA_ERR_RECORD_DELETED)
|
||||
my_errno=HA_ERR_KEY_NOT_FOUND;
|
||||
DBUG_RETURN(my_errno);
|
||||
}
|
||||
info->lastpos=filepos;
|
||||
info->lastinx=inx;
|
||||
if (inx >= 0)
|
||||
{
|
||||
info->lastkey_length= _ma_make_key(info,(uint) inx,info->lastkey,record,
|
||||
info->lastpos);
|
||||
info->update|=HA_STATE_KEY_CHANGED; /* Don't use indexposition */
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
} /* maria_rsame_pos */
|
||||
1083
storage/maria/ma_rt_index.c
Normal file
1083
storage/maria/ma_rt_index.c
Normal file
File diff suppressed because it is too large
Load Diff
47
storage/maria/ma_rt_index.h
Normal file
47
storage/maria/ma_rt_index.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* Copyright (C) 2006 MySQL AB & Ramil Kalimullin & MySQL Finland AB
|
||||
& TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#ifndef _rt_index_h
|
||||
#define _rt_index_h
|
||||
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
|
||||
#define rt_PAGE_FIRST_KEY(page, nod_flag) (page + 2 + nod_flag)
|
||||
#define rt_PAGE_NEXT_KEY(key, key_length, nod_flag) (key + key_length + \
|
||||
(nod_flag ? nod_flag : info->s->base.rec_reflength))
|
||||
#define rt_PAGE_END(page) (page + maria_getint(page))
|
||||
|
||||
#define rt_PAGE_MIN_SIZE(block_length) ((uint)(block_length) / 3)
|
||||
|
||||
int maria_rtree_insert(MARIA_HA *info, uint keynr, uchar *key, uint key_length);
|
||||
int maria_rtree_delete(MARIA_HA *info, uint keynr, uchar *key, uint key_length);
|
||||
|
||||
int maria_rtree_find_first(MARIA_HA *info, uint keynr, uchar *key, uint key_length,
|
||||
uint search_flag);
|
||||
int maria_rtree_find_next(MARIA_HA *info, uint keynr, uint search_flag);
|
||||
|
||||
int maria_rtree_get_first(MARIA_HA *info, uint keynr, uint key_length);
|
||||
int maria_rtree_get_next(MARIA_HA *info, uint keynr, uint key_length);
|
||||
|
||||
ha_rows maria_rtree_estimate(MARIA_HA *info, uint keynr, uchar *key,
|
||||
uint key_length, uint flag);
|
||||
|
||||
int maria_rtree_split_page(MARIA_HA *info, MARIA_KEYDEF *keyinfo, uchar *page,
|
||||
uchar *key, uint key_length, my_off_t *new_page_offs);
|
||||
|
||||
#endif /*HAVE_RTREE_KEYS*/
|
||||
#endif /* _rt_index_h */
|
||||
100
storage/maria/ma_rt_key.c
Normal file
100
storage/maria/ma_rt_key.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Copyright (C) 2006 MySQL AB & Ramil Kalimullin
|
||||
|
||||
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 */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
#include "ma_rt_index.h"
|
||||
#include "ma_rt_key.h"
|
||||
#include "ma_rt_mbr.h"
|
||||
|
||||
/*
|
||||
Add key to the page
|
||||
|
||||
RESULT VALUES
|
||||
-1 Error
|
||||
0 Not split
|
||||
1 Split
|
||||
*/
|
||||
|
||||
int maria_rtree_add_key(MARIA_HA *info, MARIA_KEYDEF *keyinfo, uchar *key,
|
||||
uint key_length, uchar *page_buf, my_off_t *new_page)
|
||||
{
|
||||
uint page_size = maria_getint(page_buf);
|
||||
uint nod_flag = _ma_test_if_nod(page_buf);
|
||||
|
||||
if (page_size + key_length + info->s->base.rec_reflength <=
|
||||
keyinfo->block_length)
|
||||
{
|
||||
/* split won't be necessary */
|
||||
if (nod_flag)
|
||||
{
|
||||
/* save key */
|
||||
memcpy(rt_PAGE_END(page_buf), key - nod_flag, key_length + nod_flag);
|
||||
page_size += key_length + nod_flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* save key */
|
||||
memcpy(rt_PAGE_END(page_buf), key, key_length +
|
||||
info->s->base.rec_reflength);
|
||||
page_size += key_length + info->s->base.rec_reflength;
|
||||
}
|
||||
maria_putint(page_buf, page_size, nod_flag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (maria_rtree_split_page(info, keyinfo, page_buf, key, key_length,
|
||||
new_page) ? -1 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
Delete key from the page
|
||||
*/
|
||||
int maria_rtree_delete_key(MARIA_HA *info, uchar *page_buf, uchar *key,
|
||||
uint key_length, uint nod_flag)
|
||||
{
|
||||
uint16 page_size = maria_getint(page_buf);
|
||||
uchar *key_start;
|
||||
|
||||
key_start= key - nod_flag;
|
||||
if (!nod_flag)
|
||||
key_length += info->s->base.rec_reflength;
|
||||
|
||||
memmove(key_start, key + key_length, page_size - key_length -
|
||||
(key - page_buf));
|
||||
page_size-= key_length + nod_flag;
|
||||
|
||||
maria_putint(page_buf, page_size, nod_flag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate and store key MBR
|
||||
*/
|
||||
|
||||
int maria_rtree_set_key_mbr(MARIA_HA *info, MARIA_KEYDEF *keyinfo, uchar *key,
|
||||
uint key_length, my_off_t child_page)
|
||||
{
|
||||
if (!_ma_fetch_keypage(info, keyinfo, child_page,
|
||||
DFLT_INIT_HITS, info->buff, 0))
|
||||
return -1;
|
||||
|
||||
return maria_rtree_page_mbr(info, keyinfo->seg, info->buff, key, key_length);
|
||||
}
|
||||
|
||||
#endif /*HAVE_RTREE_KEYS*/
|
||||
33
storage/maria/ma_rt_key.h
Normal file
33
storage/maria/ma_rt_key.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Copyright (C) 2006 MySQL AB & Ramil Kalimullin & MySQL Finland AB
|
||||
& TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
/* Written by Ramil Kalimullin, who has a shared copyright to this code */
|
||||
|
||||
#ifndef _rt_key_h
|
||||
#define _rt_key_h
|
||||
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
|
||||
int maria_rtree_add_key(MARIA_HA *info, MARIA_KEYDEF *keyinfo, uchar *key,
|
||||
uint key_length, uchar *page_buf, my_off_t *new_page);
|
||||
int maria_rtree_delete_key(MARIA_HA *info, uchar *page, uchar *key,
|
||||
uint key_length, uint nod_flag);
|
||||
int maria_rtree_set_key_mbr(MARIA_HA *info, MARIA_KEYDEF *keyinfo, uchar *key,
|
||||
uint key_length, my_off_t child_page);
|
||||
|
||||
#endif /*HAVE_RTREE_KEYS*/
|
||||
#endif /* _rt_key_h */
|
||||
805
storage/maria/ma_rt_mbr.c
Normal file
805
storage/maria/ma_rt_mbr.c
Normal file
@@ -0,0 +1,805 @@
|
||||
/* Copyright (C) 2006 MySQL AB & Ramil Kalimullin & MySQL Finland AB
|
||||
& TCX DataKonsult AB
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "maria_def.h"
|
||||
|
||||
#ifdef HAVE_RTREE_KEYS
|
||||
|
||||
#include "ma_rt_index.h"
|
||||
#include "ma_rt_mbr.h"
|
||||
|
||||
#define INTERSECT_CMP(amin, amax, bmin, bmax) ((amin > bmax) || (bmin > amax))
|
||||
#define CONTAIN_CMP(amin, amax, bmin, bmax) ((bmin > amin) || (bmax < amax))
|
||||
#define WITHIN_CMP(amin, amax, bmin, bmax) ((amin > bmin) || (amax < bmax))
|
||||
#define DISJOINT_CMP(amin, amax, bmin, bmax) ((amin <= bmax) && (bmin <= amax))
|
||||
#define EQUAL_CMP(amin, amax, bmin, bmax) ((amin != bmin) || (amax != bmax))
|
||||
|
||||
#define FCMP(A, B) ((int)(A) - (int)(B))
|
||||
#define p_inc(A, B, X) {A += X; B += X;}
|
||||
|
||||
#define RT_CMP(nextflag) \
|
||||
if (nextflag & MBR_INTERSECT) \
|
||||
{ \
|
||||
if (INTERSECT_CMP(amin, amax, bmin, bmax)) \
|
||||
return 1; \
|
||||
} \
|
||||
else if (nextflag & MBR_CONTAIN) \
|
||||
{ \
|
||||
if (CONTAIN_CMP(amin, amax, bmin, bmax)) \
|
||||
return 1; \
|
||||
} \
|
||||
else if (nextflag & MBR_WITHIN) \
|
||||
{ \
|
||||
if (WITHIN_CMP(amin, amax, bmin, bmax)) \
|
||||
return 1; \
|
||||
} \
|
||||
else if (nextflag & MBR_EQUAL) \
|
||||
{ \
|
||||
if (EQUAL_CMP(amin, amax, bmin, bmax)) \
|
||||
return 1; \
|
||||
} \
|
||||
else if (nextflag & MBR_DISJOINT) \
|
||||
{ \
|
||||
if (DISJOINT_CMP(amin, amax, bmin, bmax)) \
|
||||
return 1; \
|
||||
}\
|
||||
else /* if unknown comparison operator */ \
|
||||
{ \
|
||||
DBUG_ASSERT(0); \
|
||||
}
|
||||
|
||||
#define RT_CMP_KORR(type, korr_func, len, nextflag) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
amin = korr_func(a); \
|
||||
bmin = korr_func(b); \
|
||||
amax = korr_func(a+len); \
|
||||
bmax = korr_func(b+len); \
|
||||
RT_CMP(nextflag); \
|
||||
}
|
||||
|
||||
#define RT_CMP_GET(type, get_func, len, nextflag) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
get_func(amin, a); \
|
||||
get_func(bmin, b); \
|
||||
get_func(amax, a+len); \
|
||||
get_func(bmax, b+len); \
|
||||
RT_CMP(nextflag); \
|
||||
}
|
||||
|
||||
/*
|
||||
Compares two keys a and b depending on nextflag
|
||||
nextflag can contain these flags:
|
||||
MBR_INTERSECT(a,b) a overlaps b
|
||||
MBR_CONTAIN(a,b) a contains b
|
||||
MBR_DISJOINT(a,b) a disjoint b
|
||||
MBR_WITHIN(a,b) a within b
|
||||
MBR_EQUAL(a,b) All coordinates of MBRs are equal
|
||||
MBR_DATA(a,b) Data reference is the same
|
||||
Returns 0 on success.
|
||||
*/
|
||||
int maria_rtree_key_cmp(HA_KEYSEG *keyseg, uchar *b, uchar *a, uint key_length,
|
||||
uint nextflag)
|
||||
{
|
||||
for (; (int) key_length > 0; keyseg += 2 )
|
||||
{
|
||||
uint32 keyseg_length;
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_CMP_KORR(int8, mi_sint1korr, 1, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_CMP_KORR(uint8, mi_uint1korr, 1, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_CMP_KORR(int16, mi_sint2korr, 2, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_CMP_KORR(uint16, mi_uint2korr, 2, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_CMP_KORR(int32, mi_sint3korr, 3, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_CMP_KORR(uint32, mi_uint3korr, 3, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_CMP_KORR(int32, mi_sint4korr, 4, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_CMP_KORR(uint32, mi_uint4korr, 4, nextflag);
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_CMP_KORR(longlong, mi_sint8korr, 8, nextflag)
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_CMP_KORR(ulonglong, mi_uint8korr, 8, nextflag)
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
/* The following should be safe, even if we compare doubles */
|
||||
RT_CMP_GET(float, mi_float4get, 4, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_CMP_GET(double, mi_float8get, 8, nextflag);
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
goto end;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
keyseg_length= keyseg->length * 2;
|
||||
key_length-= keyseg_length;
|
||||
a+= keyseg_length;
|
||||
b+= keyseg_length;
|
||||
}
|
||||
|
||||
end:
|
||||
if (nextflag & MBR_DATA)
|
||||
{
|
||||
uchar *end = a + keyseg->length;
|
||||
do
|
||||
{
|
||||
if (*a++ != *b++)
|
||||
return FCMP(a[-1], b[-1]);
|
||||
} while (a != end);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT_VOL_KORR(type, korr_func, len, cast) \
|
||||
{ \
|
||||
type amin, amax; \
|
||||
amin = korr_func(a); \
|
||||
amax = korr_func(a+len); \
|
||||
res *= (cast(amax) - cast(amin)); \
|
||||
}
|
||||
|
||||
#define RT_VOL_GET(type, get_func, len, cast) \
|
||||
{ \
|
||||
type amin, amax; \
|
||||
get_func(amin, a); \
|
||||
get_func(amax, a+len); \
|
||||
res *= (cast(amax) - cast(amin)); \
|
||||
}
|
||||
|
||||
/*
|
||||
Calculates rectangle volume
|
||||
*/
|
||||
double maria_rtree_rect_volume(HA_KEYSEG *keyseg, uchar *a, uint key_length)
|
||||
{
|
||||
double res = 1;
|
||||
for (; (int)key_length > 0; keyseg += 2)
|
||||
{
|
||||
uint32 keyseg_length;
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_VOL_KORR(int8, mi_sint1korr, 1, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_VOL_KORR(uint8, mi_uint1korr, 1, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_VOL_KORR(int16, mi_sint2korr, 2, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_VOL_KORR(uint16, mi_uint2korr, 2, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_VOL_KORR(int32, mi_sint3korr, 3, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_VOL_KORR(uint32, mi_uint3korr, 3, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_VOL_KORR(int32, mi_sint4korr, 4, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_VOL_KORR(uint32, mi_uint4korr, 4, (double));
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_VOL_KORR(longlong, mi_sint8korr, 8, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_VOL_KORR(longlong, mi_sint8korr, 8, ulonglong2double);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
RT_VOL_GET(float, mi_float4get, 4, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_VOL_GET(double, mi_float8get, 8, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
key_length = 0;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
keyseg_length= keyseg->length * 2;
|
||||
key_length-= keyseg_length;
|
||||
a+= keyseg_length;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define RT_D_MBR_KORR(type, korr_func, len, cast) \
|
||||
{ \
|
||||
type amin, amax; \
|
||||
amin = korr_func(a); \
|
||||
amax = korr_func(a+len); \
|
||||
*res++ = cast(amin); \
|
||||
*res++ = cast(amax); \
|
||||
}
|
||||
|
||||
#define RT_D_MBR_GET(type, get_func, len, cast) \
|
||||
{ \
|
||||
type amin, amax; \
|
||||
get_func(amin, a); \
|
||||
get_func(amax, a+len); \
|
||||
*res++ = cast(amin); \
|
||||
*res++ = cast(amax); \
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Creates an MBR as an array of doubles.
|
||||
*/
|
||||
|
||||
int maria_rtree_d_mbr(HA_KEYSEG *keyseg, uchar *a, uint key_length, double *res)
|
||||
{
|
||||
for (; (int)key_length > 0; keyseg += 2)
|
||||
{
|
||||
uint32 keyseg_length;
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_D_MBR_KORR(int8, mi_sint1korr, 1, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_D_MBR_KORR(uint8, mi_uint1korr, 1, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_D_MBR_KORR(int16, mi_sint2korr, 2, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_D_MBR_KORR(uint16, mi_uint2korr, 2, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_D_MBR_KORR(int32, mi_sint3korr, 3, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_D_MBR_KORR(uint32, mi_uint3korr, 3, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_D_MBR_KORR(int32, mi_sint4korr, 4, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_D_MBR_KORR(uint32, mi_uint4korr, 4, (double));
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_D_MBR_KORR(longlong, mi_sint8korr, 8, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_D_MBR_KORR(longlong, mi_sint8korr, 8, ulonglong2double);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
RT_D_MBR_GET(float, mi_float4get, 4, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_D_MBR_GET(double, mi_float8get, 8, (double));
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
key_length = 0;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
keyseg_length= keyseg->length * 2;
|
||||
key_length-= keyseg_length;
|
||||
a+= keyseg_length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT_COMB_KORR(type, korr_func, store_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
amin = korr_func(a); \
|
||||
bmin = korr_func(b); \
|
||||
amax = korr_func(a+len); \
|
||||
bmax = korr_func(b+len); \
|
||||
amin = min(amin, bmin); \
|
||||
amax = max(amax, bmax); \
|
||||
store_func(c, amin); \
|
||||
store_func(c+len, amax); \
|
||||
}
|
||||
|
||||
#define RT_COMB_GET(type, get_func, store_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
get_func(amin, a); \
|
||||
get_func(bmin, b); \
|
||||
get_func(amax, a+len); \
|
||||
get_func(bmax, b+len); \
|
||||
amin = min(amin, bmin); \
|
||||
amax = max(amax, bmax); \
|
||||
store_func(c, amin); \
|
||||
store_func(c+len, amax); \
|
||||
}
|
||||
|
||||
/*
|
||||
Creates common minimal bounding rectungle
|
||||
for two input rectagnles a and b
|
||||
Result is written to c
|
||||
*/
|
||||
|
||||
int maria_rtree_combine_rect(HA_KEYSEG *keyseg, uchar* a, uchar* b, uchar* c,
|
||||
uint key_length)
|
||||
{
|
||||
for ( ; (int) key_length > 0 ; keyseg += 2)
|
||||
{
|
||||
uint32 keyseg_length;
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_COMB_KORR(int8, mi_sint1korr, mi_int1store, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_COMB_KORR(uint8, mi_uint1korr, mi_int1store, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_COMB_KORR(int16, mi_sint2korr, mi_int2store, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_COMB_KORR(uint16, mi_uint2korr, mi_int2store, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_COMB_KORR(int32, mi_sint3korr, mi_int3store, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_COMB_KORR(uint32, mi_uint3korr, mi_int3store, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_COMB_KORR(int32, mi_sint4korr, mi_int4store, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_COMB_KORR(uint32, mi_uint4korr, mi_int4store, 4);
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_COMB_KORR(longlong, mi_sint8korr, mi_int8store, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_COMB_KORR(ulonglong, mi_uint8korr, mi_int8store, 8);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
RT_COMB_GET(float, mi_float4get, mi_float4store, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_COMB_GET(double, mi_float8get, mi_float8store, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
keyseg_length= keyseg->length * 2;
|
||||
key_length-= keyseg_length;
|
||||
a+= keyseg_length;
|
||||
b+= keyseg_length;
|
||||
c+= keyseg_length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define RT_OVL_AREA_KORR(type, korr_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
amin = korr_func(a); \
|
||||
bmin = korr_func(b); \
|
||||
amax = korr_func(a+len); \
|
||||
bmax = korr_func(b+len); \
|
||||
amin = max(amin, bmin); \
|
||||
amax = min(amax, bmax); \
|
||||
if (amin >= amax) \
|
||||
return 0; \
|
||||
res *= amax - amin; \
|
||||
}
|
||||
|
||||
#define RT_OVL_AREA_GET(type, get_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
get_func(amin, a); \
|
||||
get_func(bmin, b); \
|
||||
get_func(amax, a+len); \
|
||||
get_func(bmax, b+len); \
|
||||
amin = max(amin, bmin); \
|
||||
amax = min(amax, bmax); \
|
||||
if (amin >= amax) \
|
||||
return 0; \
|
||||
res *= amax - amin; \
|
||||
}
|
||||
|
||||
/*
|
||||
Calculates overlapping area of two MBRs a & b
|
||||
*/
|
||||
double maria_rtree_overlapping_area(HA_KEYSEG *keyseg, uchar* a, uchar* b,
|
||||
uint key_length)
|
||||
{
|
||||
double res = 1;
|
||||
for (; (int) key_length > 0 ; keyseg += 2)
|
||||
{
|
||||
uint32 keyseg_length;
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_OVL_AREA_KORR(int8, mi_sint1korr, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_OVL_AREA_KORR(uint8, mi_uint1korr, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_OVL_AREA_KORR(int16, mi_sint2korr, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_OVL_AREA_KORR(uint16, mi_uint2korr, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_OVL_AREA_KORR(int32, mi_sint3korr, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_OVL_AREA_KORR(uint32, mi_uint3korr, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_OVL_AREA_KORR(int32, mi_sint4korr, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_OVL_AREA_KORR(uint32, mi_uint4korr, 4);
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_OVL_AREA_KORR(longlong, mi_sint8korr, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_OVL_AREA_KORR(longlong, mi_sint8korr, 8);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
RT_OVL_AREA_GET(float, mi_float4get, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_OVL_AREA_GET(double, mi_float8get, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
return res;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
keyseg_length= keyseg->length * 2;
|
||||
key_length-= keyseg_length;
|
||||
a+= keyseg_length;
|
||||
b+= keyseg_length;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define RT_AREA_INC_KORR(type, korr_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
amin = korr_func(a); \
|
||||
bmin = korr_func(b); \
|
||||
amax = korr_func(a+len); \
|
||||
bmax = korr_func(b+len); \
|
||||
a_area *= (((double)amax) - ((double)amin)); \
|
||||
loc_ab_area *= ((double)max(amax, bmax) - (double)min(amin, bmin)); \
|
||||
}
|
||||
|
||||
#define RT_AREA_INC_GET(type, get_func, len)\
|
||||
{\
|
||||
type amin, amax, bmin, bmax; \
|
||||
get_func(amin, a); \
|
||||
get_func(bmin, b); \
|
||||
get_func(amax, a+len); \
|
||||
get_func(bmax, b+len); \
|
||||
a_area *= (((double)amax) - ((double)amin)); \
|
||||
loc_ab_area *= ((double)max(amax, bmax) - (double)min(amin, bmin)); \
|
||||
}
|
||||
|
||||
/*
|
||||
Calculates MBR_AREA(a+b) - MBR_AREA(a)
|
||||
*/
|
||||
double maria_rtree_area_increase(HA_KEYSEG *keyseg, uchar* a, uchar* b,
|
||||
uint key_length, double *ab_area)
|
||||
{
|
||||
double a_area= 1.0;
|
||||
double loc_ab_area= 1.0;
|
||||
|
||||
*ab_area= 1.0;
|
||||
for (; (int)key_length > 0; keyseg += 2)
|
||||
{
|
||||
uint32 keyseg_length;
|
||||
|
||||
if (keyseg->null_bit) /* Handle NULL part */
|
||||
return -1;
|
||||
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_AREA_INC_KORR(int8, mi_sint1korr, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_AREA_INC_KORR(uint8, mi_uint1korr, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_AREA_INC_KORR(int16, mi_sint2korr, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_AREA_INC_KORR(uint16, mi_uint2korr, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_AREA_INC_KORR(int32, mi_sint3korr, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_AREA_INC_KORR(int32, mi_uint3korr, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_AREA_INC_KORR(int32, mi_sint4korr, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_AREA_INC_KORR(uint32, mi_uint4korr, 4);
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_AREA_INC_KORR(longlong, mi_sint8korr, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_AREA_INC_KORR(longlong, mi_sint8korr, 8);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
RT_AREA_INC_GET(float, mi_float4get, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_AREA_INC_GET(double, mi_float8get, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
goto safe_end;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
keyseg_length= keyseg->length * 2;
|
||||
key_length-= keyseg_length;
|
||||
a+= keyseg_length;
|
||||
b+= keyseg_length;
|
||||
}
|
||||
safe_end:
|
||||
*ab_area= loc_ab_area;
|
||||
return loc_ab_area - a_area;
|
||||
}
|
||||
|
||||
#define RT_PERIM_INC_KORR(type, korr_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
amin = korr_func(a); \
|
||||
bmin = korr_func(b); \
|
||||
amax = korr_func(a+len); \
|
||||
bmax = korr_func(b+len); \
|
||||
a_perim+= (((double)amax) - ((double)amin)); \
|
||||
*ab_perim+= ((double)max(amax, bmax) - (double)min(amin, bmin)); \
|
||||
}
|
||||
|
||||
#define RT_PERIM_INC_GET(type, get_func, len)\
|
||||
{\
|
||||
type amin, amax, bmin, bmax; \
|
||||
get_func(amin, a); \
|
||||
get_func(bmin, b); \
|
||||
get_func(amax, a+len); \
|
||||
get_func(bmax, b+len); \
|
||||
a_perim+= (((double)amax) - ((double)amin)); \
|
||||
*ab_perim+= ((double)max(amax, bmax) - (double)min(amin, bmin)); \
|
||||
}
|
||||
|
||||
/*
|
||||
Calculates MBR_PERIMETER(a+b) - MBR_PERIMETER(a)
|
||||
*/
|
||||
double maria_rtree_perimeter_increase(HA_KEYSEG *keyseg, uchar* a, uchar* b,
|
||||
uint key_length, double *ab_perim)
|
||||
{
|
||||
double a_perim = 0.0;
|
||||
|
||||
*ab_perim= 0.0;
|
||||
for (; (int)key_length > 0; keyseg += 2)
|
||||
{
|
||||
uint32 keyseg_length;
|
||||
|
||||
if (keyseg->null_bit) /* Handle NULL part */
|
||||
return -1;
|
||||
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_PERIM_INC_KORR(int8, mi_sint1korr, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_PERIM_INC_KORR(uint8, mi_uint1korr, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_PERIM_INC_KORR(int16, mi_sint2korr, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_PERIM_INC_KORR(uint16, mi_uint2korr, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_PERIM_INC_KORR(int32, mi_sint3korr, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_PERIM_INC_KORR(int32, mi_uint3korr, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_PERIM_INC_KORR(int32, mi_sint4korr, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_PERIM_INC_KORR(uint32, mi_uint4korr, 4);
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_PERIM_INC_KORR(longlong, mi_sint8korr, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_PERIM_INC_KORR(longlong, mi_sint8korr, 8);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
RT_PERIM_INC_GET(float, mi_float4get, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_PERIM_INC_GET(double, mi_float8get, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
return *ab_perim - a_perim;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
keyseg_length= keyseg->length * 2;
|
||||
key_length-= keyseg_length;
|
||||
a+= keyseg_length;
|
||||
b+= keyseg_length;
|
||||
}
|
||||
return *ab_perim - a_perim;
|
||||
}
|
||||
|
||||
|
||||
#define RT_PAGE_MBR_KORR(type, korr_func, store_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
amin = korr_func(k + inc); \
|
||||
amax = korr_func(k + inc + len); \
|
||||
k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag); \
|
||||
for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag)) \
|
||||
{ \
|
||||
bmin = korr_func(k + inc); \
|
||||
bmax = korr_func(k + inc + len); \
|
||||
if (amin > bmin) \
|
||||
amin = bmin; \
|
||||
if (amax < bmax) \
|
||||
amax = bmax; \
|
||||
} \
|
||||
store_func(c, amin); \
|
||||
c += len; \
|
||||
store_func(c, amax); \
|
||||
c += len; \
|
||||
inc += 2 * len; \
|
||||
}
|
||||
|
||||
#define RT_PAGE_MBR_GET(type, get_func, store_func, len) \
|
||||
{ \
|
||||
type amin, amax, bmin, bmax; \
|
||||
get_func(amin, k + inc); \
|
||||
get_func(amax, k + inc + len); \
|
||||
k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag); \
|
||||
for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag)) \
|
||||
{ \
|
||||
get_func(bmin, k + inc); \
|
||||
get_func(bmax, k + inc + len); \
|
||||
if (amin > bmin) \
|
||||
amin = bmin; \
|
||||
if (amax < bmax) \
|
||||
amax = bmax; \
|
||||
} \
|
||||
store_func(c, amin); \
|
||||
c += len; \
|
||||
store_func(c, amax); \
|
||||
c += len; \
|
||||
inc += 2 * len; \
|
||||
}
|
||||
|
||||
/*
|
||||
Calculates key page total MBR = MBR(key1) + MBR(key2) + ...
|
||||
*/
|
||||
int maria_rtree_page_mbr(MARIA_HA *info, HA_KEYSEG *keyseg, uchar *page_buf,
|
||||
uchar *c, uint key_length)
|
||||
{
|
||||
uint inc = 0;
|
||||
uint k_len = key_length;
|
||||
uint nod_flag = _ma_test_if_nod(page_buf);
|
||||
uchar *k;
|
||||
uchar *last = rt_PAGE_END(page_buf);
|
||||
|
||||
for (; (int)key_length > 0; keyseg += 2)
|
||||
{
|
||||
key_length -= keyseg->length * 2;
|
||||
|
||||
/* Handle NULL part */
|
||||
if (keyseg->null_bit)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
|
||||
|
||||
switch ((enum ha_base_keytype) keyseg->type) {
|
||||
case HA_KEYTYPE_INT8:
|
||||
RT_PAGE_MBR_KORR(int8, mi_sint1korr, mi_int1store, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_BINARY:
|
||||
RT_PAGE_MBR_KORR(uint8, mi_uint1korr, mi_int1store, 1);
|
||||
break;
|
||||
case HA_KEYTYPE_SHORT_INT:
|
||||
RT_PAGE_MBR_KORR(int16, mi_sint2korr, mi_int2store, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_USHORT_INT:
|
||||
RT_PAGE_MBR_KORR(uint16, mi_uint2korr, mi_int2store, 2);
|
||||
break;
|
||||
case HA_KEYTYPE_INT24:
|
||||
RT_PAGE_MBR_KORR(int32, mi_sint3korr, mi_int3store, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_UINT24:
|
||||
RT_PAGE_MBR_KORR(uint32, mi_uint3korr, mi_int3store, 3);
|
||||
break;
|
||||
case HA_KEYTYPE_LONG_INT:
|
||||
RT_PAGE_MBR_KORR(int32, mi_sint4korr, mi_int4store, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONG_INT:
|
||||
RT_PAGE_MBR_KORR(uint32, mi_uint4korr, mi_int4store, 4);
|
||||
break;
|
||||
#ifdef HAVE_LONG_LONG
|
||||
case HA_KEYTYPE_LONGLONG:
|
||||
RT_PAGE_MBR_KORR(longlong, mi_sint8korr, mi_int8store, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_ULONGLONG:
|
||||
RT_PAGE_MBR_KORR(ulonglong, mi_uint8korr, mi_int8store, 8);
|
||||
break;
|
||||
#endif
|
||||
case HA_KEYTYPE_FLOAT:
|
||||
RT_PAGE_MBR_GET(float, mi_float4get, mi_float4store, 4);
|
||||
break;
|
||||
case HA_KEYTYPE_DOUBLE:
|
||||
RT_PAGE_MBR_GET(double, mi_float8get, mi_float8store, 8);
|
||||
break;
|
||||
case HA_KEYTYPE_END:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /*HAVE_RTREE_KEYS*/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user