summaryrefslogtreecommitdiff
path: root/storage/myisam
diff options
context:
space:
mode:
authorunknown <brian@zim.(none)>2005-04-26 18:19:54 -0700
committerunknown <brian@zim.(none)>2005-04-26 18:19:54 -0700
commitb7e422be1b7a8ca3f4e761e67db5e8febc701dfd (patch)
treedf9016f3d70b4657f89dcddca2ec4e188fc7fbdf /storage/myisam
parentc0333ecee42a4de499b3377cfa21d7b5af1ddd2b (diff)
downloadmariadb-git-b7e422be1b7a8ca3f4e761e67db5e8febc701dfd.tar.gz
Changes to create storage directory for storage engines.
storage/heap/.cvsignore: mvdir storage/heap/ChangeLog: mvdir storage/heap/Makefile.am: mvdir storage/heap/_check.c: mvdir storage/heap/_rectest.c: mvdir storage/heap/heapdef.h: mvdir storage/heap/hp_block.c: mvdir storage/heap/hp_clear.c: mvdir storage/heap/hp_close.c: mvdir storage/heap/hp_create.c: mvdir storage/heap/hp_delete.c: mvdir storage/heap/hp_extra.c: mvdir storage/heap/hp_hash.c: mvdir storage/heap/hp_info.c: mvdir storage/heap/hp_open.c: mvdir storage/heap/hp_panic.c: mvdir storage/heap/hp_rename.c: mvdir storage/heap/hp_rfirst.c: mvdir storage/heap/hp_rkey.c: mvdir storage/heap/hp_rlast.c: mvdir storage/heap/hp_rnext.c: mvdir storage/heap/hp_rprev.c: mvdir storage/heap/hp_rrnd.c: mvdir storage/heap/hp_rsame.c: mvdir storage/heap/hp_scan.c: mvdir storage/heap/hp_static.c: mvdir storage/heap/hp_test1.c: mvdir storage/heap/hp_test2.c: mvdir storage/heap/hp_update.c: mvdir storage/heap/hp_write.c: mvdir storage/heap/make-ccc: mvdir storage/myisam/.cvsignore: mvdir storage/myisam/ChangeLog: mvdir storage/myisam/Makefile.am: mvdir storage/myisam/NEWS: mvdir storage/myisam/TODO: mvdir storage/myisam/ft_boolean_search.c: mvdir storage/myisam/ft_eval.c: mvdir storage/myisam/ft_eval.h: mvdir storage/myisam/ft_nlq_search.c: mvdir storage/myisam/ft_parser.c: mvdir storage/myisam/ft_static.c: mvdir storage/myisam/ft_stem.c: mvdir storage/myisam/ft_stopwords.c: mvdir storage/myisam/ft_test1.c: mvdir storage/myisam/ft_test1.h: mvdir storage/myisam/ft_update.c: mvdir storage/myisam/ftdefs.h: mvdir storage/myisam/fulltext.h: mvdir storage/myisam/make-ccc: mvdir storage/myisam/mi_cache.c: mvdir storage/myisam/mi_changed.c: mvdir storage/myisam/mi_check.c: mvdir storage/myisam/mi_checksum.c: mvdir storage/myisam/mi_close.c: mvdir storage/myisam/mi_create.c: mvdir storage/myisam/mi_dbug.c: mvdir storage/myisam/mi_delete.c: mvdir storage/myisam/mi_delete_all.c: mvdir storage/myisam/mi_delete_table.c: mvdir storage/myisam/mi_dynrec.c: mvdir storage/myisam/mi_extra.c: mvdir storage/myisam/mi_info.c: mvdir storage/myisam/mi_key.c: mvdir storage/myisam/mi_keycache.c: mvdir storage/myisam/mi_locking.c: mvdir storage/myisam/mi_log.c: mvdir storage/myisam/mi_open.c: mvdir storage/myisam/mi_packrec.c: mvdir storage/myisam/mi_page.c: mvdir storage/myisam/mi_panic.c: mvdir storage/myisam/mi_preload.c: mvdir storage/myisam/mi_range.c: mvdir storage/myisam/mi_rename.c: mvdir storage/myisam/mi_rfirst.c: mvdir storage/myisam/mi_rkey.c: mvdir storage/myisam/mi_rlast.c: mvdir storage/myisam/mi_rnext.c: mvdir storage/myisam/mi_rnext_same.c: mvdir storage/myisam/mi_rprev.c: mvdir storage/myisam/mi_rrnd.c: mvdir storage/myisam/mi_rsame.c: mvdir storage/myisam/ftbench/Ecompare.pl: mvdir storage/myisam/ftbench/Ecreate.pl: mvdir storage/myisam/ftbench/Ereport.pl: mvdir storage/myisam/ftbench/README: mvdir storage/myisam/ftbench/ft-test-run.sh: mvdir storage/myisam/mi_rsamepos.c: mvdir storage/myisam/mi_scan.c: mvdir storage/myisam/mi_search.c: mvdir storage/myisam/mi_static.c: mvdir storage/myisam/mi_statrec.c: mvdir storage/myisam/mi_test1.c: mvdir storage/myisam/mi_test2.c: mvdir storage/myisam/mi_test3.c: mvdir storage/myisam/mi_test_all.res: mvdir storage/myisam/mi_test_all.sh: mvdir storage/myisam/mi_unique.c: mvdir storage/myisam/mi_update.c: mvdir storage/myisam/mi_write.c: mvdir storage/myisam/myisam_ftdump.c: mvdir storage/myisam/myisamchk.c: mvdir storage/myisam/myisamdef.h: mvdir storage/myisam/myisamlog.c: mvdir storage/myisam/myisampack.c: mvdir storage/myisam/rt_index.c: mvdir storage/myisam/rt_index.h: mvdir storage/myisam/rt_key.c: mvdir storage/myisam/rt_key.h: mvdir storage/myisam/rt_mbr.c: mvdir storage/myisam/rt_mbr.h: mvdir storage/myisam/rt_split.c: mvdir storage/myisam/rt_test.c: mvdir storage/myisam/sort.c: mvdir storage/myisam/sp_defs.h: mvdir storage/myisam/sp_key.c: mvdir storage/myisam/sp_test.c: mvdir storage/myisam/test_pack: mvdir storage/myisammrg/.cvsignore: mvdir storage/myisammrg/Makefile.am: mvdir storage/myisammrg/make-ccc: mvdir storage/myisammrg/myrg_close.c: mvdir storage/myisammrg/myrg_create.c: mvdir storage/myisammrg/myrg_def.h: mvdir storage/myisammrg/myrg_delete.c: mvdir storage/myisammrg/myrg_extra.c: mvdir storage/myisammrg/myrg_info.c: mvdir storage/myisammrg/myrg_locking.c: mvdir storage/myisammrg/myrg_open.c: mvdir storage/myisammrg/myrg_panic.c: mvdir storage/myisammrg/myrg_queue.c: mvdir storage/myisammrg/myrg_range.c: mvdir storage/myisammrg/myrg_rfirst.c: mvdir storage/myisammrg/myrg_rkey.c: mvdir storage/myisammrg/myrg_rlast.c: mvdir storage/myisammrg/myrg_rnext.c: mvdir storage/myisammrg/myrg_rnext_same.c: mvdir storage/myisammrg/myrg_rprev.c: mvdir storage/myisammrg/myrg_rrnd.c: mvdir storage/myisammrg/myrg_rsame.c: mvdir storage/myisammrg/myrg_static.c: mvdir storage/myisammrg/myrg_update.c: mvdir storage/myisammrg/myrg_write.c: mvdir storage/innobase/Makefile.am: mvdir storage/innobase/btr/Makefile.am: mvdir storage/innobase/btr/btr0btr.c: mvdir storage/innobase/btr/btr0cur.c: mvdir storage/innobase/btr/btr0pcur.c: mvdir storage/innobase/btr/btr0sea.c: mvdir storage/innobase/btr/makefilewin: mvdir storage/innobase/buf/Makefile.am: mvdir storage/innobase/buf/buf0buf.c: mvdir storage/innobase/buf/buf0flu.c: mvdir storage/innobase/buf/buf0lru.c: mvdir storage/innobase/buf/buf0rea.c: mvdir storage/innobase/buf/makefilewin: mvdir storage/innobase/configure.in: mvdir storage/innobase/data/Makefile.am: mvdir storage/innobase/data/data0data.c: mvdir storage/innobase/data/data0type.c: mvdir storage/innobase/data/makefilewin: mvdir storage/innobase/db/db0err.h: mvdir storage/innobase/dict/Makefile.am: mvdir storage/innobase/dict/dict0boot.c: mvdir storage/innobase/dict/dict0crea.c: mvdir storage/innobase/dict/dict0dict.c: mvdir storage/innobase/dict/dict0load.c: mvdir storage/innobase/makefilewin: mvdir storage/innobase/my_cnf: mvdir storage/innobase/dict/dict0mem.c: mvdir storage/innobase/dict/makefilewin: mvdir storage/innobase/dyn/Makefile.am: mvdir storage/innobase/dyn/dyn0dyn.c: mvdir storage/innobase/dyn/makefilewin: mvdir storage/innobase/eval/Makefile.am: mvdir storage/innobase/eval/eval0eval.c: mvdir storage/innobase/eval/eval0proc.c: mvdir storage/innobase/eval/makefilewin: mvdir storage/innobase/fil/Makefile.am: mvdir storage/innobase/fil/fil0fil.c: mvdir storage/innobase/fil/makefilewin: mvdir storage/innobase/fsp/Makefile.am: mvdir storage/innobase/fsp/fsp0fsp.c: mvdir storage/innobase/fsp/makefilewin: mvdir storage/innobase/fut/Makefile.am: mvdir storage/innobase/fut/fut0fut.c: mvdir storage/innobase/fut/fut0lst.c: mvdir storage/innobase/fut/makefilewin: mvdir storage/innobase/ha/Makefile.am: mvdir storage/innobase/ha/ha0ha.c: mvdir storage/innobase/ha/hash0hash.c: mvdir storage/innobase/ha/makefilewin: mvdir storage/innobase/ibuf/Makefile.am: mvdir storage/innobase/ibuf/ibuf0ibuf.c: mvdir storage/innobase/ibuf/makefilewin: mvdir storage/innobase/include/Makefile.am: mvdir storage/innobase/include/Makefile.i: mvdir storage/innobase/include/btr0btr.h: mvdir storage/innobase/include/btr0btr.ic: mvdir storage/innobase/include/btr0cur.h: mvdir storage/innobase/include/btr0cur.ic: mvdir storage/innobase/include/btr0pcur.h: mvdir storage/innobase/include/btr0pcur.ic: mvdir storage/innobase/include/btr0sea.h: mvdir storage/innobase/include/btr0sea.ic: mvdir storage/innobase/include/btr0types.h: mvdir storage/innobase/include/buf0buf.h: mvdir storage/innobase/include/buf0buf.ic: mvdir storage/innobase/include/buf0flu.h: mvdir storage/innobase/include/buf0flu.ic: mvdir storage/innobase/include/buf0lru.h: mvdir storage/innobase/include/buf0lru.ic: mvdir storage/innobase/include/buf0rea.h: mvdir storage/innobase/include/buf0types.h: mvdir storage/innobase/include/data0data.h: mvdir storage/innobase/include/data0data.ic: mvdir storage/innobase/include/data0type.h: mvdir storage/innobase/include/data0type.ic: mvdir storage/innobase/include/data0types.h: mvdir storage/innobase/include/db0err.h: mvdir storage/innobase/include/dict0boot.h: mvdir storage/innobase/include/dict0boot.ic: mvdir storage/innobase/include/dict0crea.h: mvdir storage/innobase/include/dict0crea.ic: mvdir storage/innobase/include/dict0dict.h: mvdir storage/innobase/include/dict0dict.ic: mvdir storage/innobase/include/dict0load.h: mvdir storage/innobase/include/dict0load.ic: mvdir storage/innobase/include/dict0mem.h: mvdir storage/innobase/include/dict0mem.ic: mvdir storage/innobase/include/dict0types.h: mvdir storage/innobase/include/dyn0dyn.h: mvdir storage/innobase/include/dyn0dyn.ic: mvdir storage/innobase/include/eval0eval.h: mvdir storage/innobase/include/eval0eval.ic: mvdir storage/innobase/include/eval0proc.h: mvdir storage/innobase/include/eval0proc.ic: mvdir storage/innobase/include/fil0fil.h: mvdir storage/innobase/include/fsp0fsp.h: mvdir storage/innobase/include/fsp0fsp.ic: mvdir storage/innobase/include/fut0fut.h: mvdir storage/innobase/include/fut0fut.ic: mvdir storage/innobase/include/fut0lst.h: mvdir storage/innobase/include/fut0lst.ic: mvdir storage/innobase/include/ha0ha.h: mvdir storage/innobase/include/ha0ha.ic: mvdir storage/innobase/include/hash0hash.h: mvdir storage/innobase/include/hash0hash.ic: mvdir storage/innobase/include/ibuf0ibuf.h: mvdir storage/innobase/include/ibuf0ibuf.ic: mvdir storage/innobase/include/ibuf0types.h: mvdir storage/innobase/include/lock0lock.h: mvdir storage/innobase/include/lock0lock.ic: mvdir storage/innobase/include/lock0types.h: mvdir storage/innobase/include/log0log.h: mvdir storage/innobase/include/log0log.ic: mvdir storage/innobase/include/log0recv.h: mvdir storage/innobase/include/log0recv.ic: mvdir storage/innobase/include/mach0data.h: mvdir storage/innobase/include/mach0data.ic: mvdir storage/innobase/include/makefilewin.i: mvdir storage/innobase/include/mem0dbg.h: mvdir storage/innobase/include/mem0dbg.ic: mvdir storage/innobase/include/mem0mem.h: mvdir storage/innobase/include/mem0mem.ic: mvdir storage/innobase/include/mem0pool.h: mvdir storage/innobase/include/mem0pool.ic: mvdir storage/innobase/include/mtr0log.h: mvdir storage/innobase/include/mtr0log.ic: mvdir storage/innobase/include/mtr0mtr.h: mvdir storage/innobase/include/mtr0mtr.ic: mvdir storage/innobase/include/mtr0types.h: mvdir storage/innobase/include/os0file.h: mvdir storage/innobase/include/os0proc.h: mvdir storage/innobase/include/os0proc.ic: mvdir storage/innobase/include/os0sync.h: mvdir storage/innobase/include/os0sync.ic: mvdir storage/innobase/include/os0thread.h: mvdir storage/innobase/include/os0thread.ic: mvdir storage/innobase/include/page0cur.h: mvdir storage/innobase/include/page0cur.ic: mvdir storage/innobase/include/page0page.h: mvdir storage/innobase/include/page0page.ic: mvdir storage/innobase/include/page0types.h: mvdir storage/innobase/include/pars0grm.h: mvdir storage/innobase/include/pars0opt.h: mvdir storage/innobase/include/pars0opt.ic: mvdir storage/innobase/include/pars0pars.h: mvdir storage/innobase/include/pars0pars.ic: mvdir storage/innobase/include/pars0sym.h: mvdir storage/innobase/include/pars0sym.ic: mvdir storage/innobase/include/pars0types.h: mvdir storage/innobase/include/que0que.h: mvdir storage/innobase/include/que0que.ic: mvdir storage/innobase/include/que0types.h: mvdir storage/innobase/include/read0read.h: mvdir storage/innobase/include/read0read.ic: mvdir storage/innobase/include/read0types.h: mvdir storage/innobase/include/rem0cmp.h: mvdir storage/innobase/include/rem0cmp.ic: mvdir storage/innobase/include/rem0rec.h: mvdir storage/innobase/include/rem0rec.ic: mvdir storage/innobase/include/rem0types.h: mvdir storage/innobase/include/row0ins.h: mvdir storage/innobase/include/row0ins.ic: mvdir storage/innobase/include/row0mysql.h: mvdir storage/innobase/include/row0mysql.ic: mvdir storage/innobase/include/row0purge.h: mvdir storage/innobase/include/row0purge.ic: mvdir storage/innobase/include/row0row.h: mvdir storage/innobase/include/row0row.ic: mvdir storage/innobase/include/row0sel.h: mvdir storage/innobase/include/row0sel.ic: mvdir storage/innobase/include/row0types.h: mvdir storage/innobase/include/row0uins.h: mvdir storage/innobase/include/row0uins.ic: mvdir storage/innobase/include/row0umod.h: mvdir storage/innobase/include/row0umod.ic: mvdir storage/innobase/include/row0undo.h: mvdir storage/innobase/include/row0undo.ic: mvdir storage/innobase/include/row0upd.h: mvdir storage/innobase/include/row0upd.ic: mvdir storage/innobase/include/row0vers.h: mvdir storage/innobase/include/row0vers.ic: mvdir storage/innobase/include/srv0que.h: mvdir storage/innobase/include/srv0srv.h: mvdir storage/innobase/include/srv0srv.ic: mvdir storage/innobase/include/srv0start.h: mvdir storage/innobase/include/sync0arr.h: mvdir storage/innobase/include/sync0arr.ic: mvdir storage/innobase/include/sync0rw.h: mvdir storage/innobase/include/sync0rw.ic: mvdir storage/innobase/include/sync0sync.h: mvdir storage/innobase/include/sync0sync.ic: mvdir storage/innobase/include/sync0types.h: mvdir storage/innobase/include/thr0loc.h: mvdir storage/innobase/include/thr0loc.ic: mvdir storage/innobase/include/trx0purge.h: mvdir storage/innobase/include/trx0purge.ic: mvdir storage/innobase/include/trx0rec.h: mvdir storage/innobase/include/trx0rec.ic: mvdir storage/innobase/include/trx0roll.h: mvdir storage/innobase/include/trx0roll.ic: mvdir storage/innobase/include/trx0rseg.h: mvdir storage/innobase/include/trx0rseg.ic: mvdir storage/innobase/include/trx0sys.h: mvdir storage/innobase/include/trx0sys.ic: mvdir storage/innobase/include/trx0trx.h: mvdir storage/innobase/include/trx0trx.ic: mvdir storage/innobase/include/trx0types.h: mvdir storage/innobase/include/trx0undo.h: mvdir storage/innobase/include/trx0undo.ic: mvdir storage/innobase/include/trx0xa.h: mvdir storage/innobase/include/univ.i: mvdir storage/innobase/include/usr0sess.h: mvdir storage/innobase/include/usr0sess.ic: mvdir storage/innobase/include/usr0types.h: mvdir storage/innobase/include/ut0byte.h: mvdir storage/innobase/include/ut0byte.ic: mvdir storage/innobase/include/ut0dbg.h: mvdir storage/innobase/include/ut0lst.h: mvdir storage/innobase/include/ut0mem.h: mvdir storage/innobase/include/ut0mem.ic: mvdir storage/innobase/include/ut0rnd.h: mvdir storage/innobase/include/ut0rnd.ic: mvdir storage/innobase/include/ut0sort.h: mvdir storage/innobase/include/ut0ut.h: mvdir storage/innobase/include/ut0ut.ic: mvdir storage/innobase/lock/Makefile.am: mvdir storage/innobase/lock/lock0lock.c: mvdir storage/innobase/lock/makefilewin: mvdir storage/innobase/log/Makefile.am: mvdir storage/innobase/log/log0log.c: mvdir storage/innobase/log/log0recv.c: mvdir storage/innobase/log/makefilewin: mvdir storage/innobase/mach/Makefile.am: mvdir storage/innobase/mach/mach0data.c: mvdir storage/innobase/mach/makefilewin: mvdir storage/innobase/mem/Makefile.am: mvdir storage/innobase/mem/makefilewin: mvdir storage/innobase/mem/mem0dbg.c: mvdir storage/innobase/mem/mem0mem.c: mvdir storage/innobase/mem/mem0pool.c: mvdir storage/innobase/mtr/Makefile.am: mvdir storage/innobase/mtr/makefilewin: mvdir storage/innobase/mtr/mtr0log.c: mvdir storage/innobase/mtr/mtr0mtr.c: mvdir storage/innobase/os/Makefile.am: mvdir storage/innobase/os/makefilewin: mvdir storage/innobase/os/os0file.c: mvdir storage/innobase/os/os0proc.c: mvdir storage/innobase/os/os0sync.c: mvdir storage/innobase/os/os0thread.c: mvdir storage/innobase/page/Makefile.am: mvdir storage/innobase/page/makefilewin: mvdir storage/innobase/page/page0cur.c: mvdir storage/innobase/page/page0page.c: mvdir storage/innobase/pars/Makefile.am: mvdir storage/innobase/pars/lexyy.c: mvdir storage/innobase/pars/makefilewin: mvdir storage/innobase/pars/pars0grm.c: mvdir storage/innobase/pars/pars0grm.h: mvdir storage/innobase/pars/pars0grm.y: mvdir storage/innobase/pars/pars0lex.l: mvdir storage/innobase/pars/pars0opt.c: mvdir storage/innobase/pars/pars0pars.c: mvdir storage/innobase/pars/pars0sym.c: mvdir storage/innobase/que/Makefile.am: mvdir storage/innobase/que/makefilewin: mvdir storage/innobase/que/que0que.c: mvdir storage/innobase/read/Makefile.am: mvdir storage/innobase/read/makefilewin: mvdir storage/innobase/read/read0read.c: mvdir storage/innobase/rem/Makefile.am: mvdir storage/innobase/rem/makefilewin: mvdir storage/innobase/rem/rem0cmp.c: mvdir storage/innobase/rem/rem0rec.c: mvdir storage/innobase/row/Makefile.am: mvdir storage/innobase/row/makefilewin: mvdir storage/innobase/row/row0ins.c: mvdir storage/innobase/row/row0mysql.c: mvdir storage/innobase/row/row0purge.c: mvdir storage/innobase/row/row0row.c: mvdir storage/innobase/row/row0sel.c: mvdir storage/innobase/row/row0uins.c: mvdir storage/innobase/row/row0umod.c: mvdir storage/innobase/row/row0undo.c: mvdir storage/innobase/row/row0upd.c: mvdir storage/innobase/row/row0vers.c: mvdir storage/innobase/srv/Makefile.am: mvdir storage/innobase/srv/makefilewin: mvdir storage/innobase/srv/srv0que.c: mvdir storage/innobase/srv/srv0srv.c: mvdir storage/innobase/srv/srv0start.c: mvdir storage/innobase/sync/Makefile.am: mvdir storage/innobase/sync/makefilewin: mvdir storage/innobase/sync/sync0arr.c: mvdir storage/innobase/sync/sync0rw.c: mvdir storage/innobase/sync/sync0sync.c: mvdir storage/innobase/thr/Makefile.am: mvdir storage/innobase/thr/makefilewin: mvdir storage/innobase/thr/thr0loc.c: mvdir storage/innobase/trx/Makefile.am: mvdir storage/innobase/trx/makefilewin: mvdir storage/innobase/trx/trx0purge.c: mvdir storage/innobase/trx/trx0rec.c: mvdir storage/innobase/trx/trx0roll.c: mvdir storage/innobase/trx/trx0rseg.c: mvdir storage/innobase/trx/trx0sys.c: mvdir storage/innobase/trx/trx0trx.c: mvdir storage/innobase/trx/trx0undo.c: mvdir storage/innobase/usr/Makefile.am: mvdir storage/innobase/usr/makefilewin: mvdir storage/innobase/usr/usr0sess.c: mvdir storage/innobase/ut/Makefile.am: mvdir storage/innobase/ut/makefilewin: mvdir storage/innobase/ut/ut0byte.c: mvdir storage/innobase/ut/ut0dbg.c: mvdir storage/innobase/ut/ut0mem.c: mvdir storage/innobase/ut/ut0rnd.c: mvdir storage/innobase/ut/ut0ut.c: mvdir storage/ndb/Makefile.am: mvdir storage/ndb/bin/.empty: mvdir storage/ndb/bin/check-regression.sh: mvdir storage/ndb/bin/makeTestPrograms_html.sh: mvdir storage/ndb/config/common.mk.am: mvdir storage/ndb/config/make-win-dsw.sh: mvdir storage/ndb/config/type_kernel.mk.am: mvdir storage/ndb/config/type_mgmapiclient.mk.am: mvdir storage/ndb/config/type_ndbapi.mk.am: mvdir storage/ndb/config/type_ndbapiclient.mk.am: mvdir storage/ndb/config/type_ndbapitest.mk.am: mvdir storage/ndb/config/type_ndbapitools.mk.am: mvdir storage/ndb/config/type_util.mk.am: mvdir storage/ndb/config/win-includes: mvdir storage/ndb/config/win-lib.am: mvdir storage/ndb/config/win-libraries: mvdir storage/ndb/config/win-name: mvdir storage/ndb/config/win-prg.am: mvdir storage/ndb/config/win-sources: mvdir storage/ndb/demos/1-node/1-api-3/Ndb.cfg: mvdir storage/ndb/demos/1-node/1-db-2/Ndb.cfg: mvdir storage/ndb/demos/1-node/1-mgm-1/Ndb.cfg: mvdir storage/ndb/demos/1-node/1-mgm-1/template_config.ini: mvdir storage/ndb/demos/2-node/2-api-4/Ndb.cfg: mvdir storage/ndb/demos/2-node/2-api-5/Ndb.cfg: mvdir storage/ndb/demos/2-node/2-api-6/Ndb.cfg: mvdir storage/ndb/demos/2-node/2-api-7/Ndb.cfg: mvdir storage/ndb/demos/2-node/2-db-2/Ndb.cfg: mvdir storage/ndb/demos/2-node/2-db-3/Ndb.cfg: mvdir storage/ndb/demos/2-node/2-mgm-1/Ndb.cfg: mvdir storage/ndb/demos/2-node/2-mgm-1/template_config.ini: mvdir storage/ndb/demos/config-templates/config_template-1-REP.ini: mvdir storage/ndb/demos/config-templates/config_template-4.ini: mvdir storage/ndb/demos/config-templates/config_template-install.ini: mvdir storage/ndb/demos/run_demo1-PS-SS_common.sh: mvdir storage/ndb/demos/run_demo1-PS.sh: mvdir storage/ndb/demos/run_demo1-SS.sh: mvdir storage/ndb/demos/run_demo1.sh: mvdir storage/ndb/demos/run_demo2.sh: mvdir storage/ndb/docs/Makefile.am: mvdir storage/ndb/docs/README: mvdir storage/ndb/docs/doxygen/Doxyfile.mgmapi: mvdir storage/ndb/docs/doxygen/Doxyfile.ndbapi: mvdir storage/ndb/docs/doxygen/Doxyfile.ndb: mvdir storage/ndb/docs/doxygen/Doxyfile.odbc: mvdir storage/ndb/docs/doxygen/Doxyfile.test: mvdir storage/ndb/docs/doxygen/header.mgmapi.tex: mvdir storage/ndb/docs/doxygen/header.ndbapi.tex: mvdir storage/ndb/docs/doxygen/postdoxy.pl: mvdir storage/ndb/docs/doxygen/predoxy.pl: mvdir storage/ndb/docs/wl2077.txt: mvdir storage/ndb/home/bin/Linuxmkisofs: mvdir storage/ndb/home/bin/Solarismkisofs: mvdir storage/ndb/home/bin/cvs2cl.pl: mvdir storage/ndb/home/bin/fix-cvs-root: mvdir storage/ndb/home/bin/import-from-bk.sh: mvdir storage/ndb/home/bin/ndb_deploy: mvdir storage/ndb/home/bin/ndbdoxy.pl: mvdir storage/ndb/home/bin/ngcalc: mvdir storage/ndb/home/bin/parseConfigFile.awk: mvdir storage/ndb/home/bin/setup-test.sh: mvdir storage/ndb/home/bin/signallog2html.lib/signallog2list.awk: mvdir storage/ndb/home/bin/signallog2html.lib/uniq_blocks.awk: mvdir storage/ndb/home/bin/signallog2html.sh: mvdir storage/ndb/home/bin/stripcr: mvdir storage/ndb/home/lib/funcs.sh: mvdir storage/ndb/include/Makefile.am: mvdir storage/ndb/include/debugger/DebuggerNames.hpp: mvdir storage/ndb/include/debugger/EventLogger.hpp: mvdir storage/ndb/include/debugger/GrepError.hpp: mvdir storage/ndb/include/debugger/SignalLoggerManager.hpp: mvdir storage/ndb/include/editline/editline.h: mvdir storage/ndb/include/kernel/AttributeDescriptor.hpp: mvdir storage/ndb/include/kernel/AttributeHeader.hpp: mvdir storage/ndb/include/kernel/AttributeList.hpp: mvdir storage/ndb/include/kernel/BlockNumbers.h: mvdir storage/ndb/include/kernel/GlobalSignalNumbers.h: mvdir storage/ndb/include/kernel/GrepEvent.hpp: mvdir storage/ndb/include/kernel/Interpreter.hpp: mvdir storage/ndb/include/kernel/LogLevel.hpp: mvdir storage/ndb/include/kernel/NodeBitmask.hpp: mvdir storage/ndb/include/kernel/NodeInfo.hpp: mvdir storage/ndb/include/kernel/NodeState.hpp: mvdir storage/ndb/include/kernel/RefConvert.hpp: mvdir storage/ndb/include/kernel/kernel_config_parameters.h: mvdir storage/ndb/include/kernel/kernel_types.h: mvdir storage/ndb/include/kernel/ndb_limits.h: mvdir storage/ndb/include/kernel/signaldata/AbortAll.hpp: mvdir storage/ndb/include/kernel/signaldata/AccFrag.hpp: mvdir storage/ndb/include/kernel/signaldata/AccLock.hpp: mvdir storage/ndb/include/kernel/signaldata/AccScan.hpp: mvdir storage/ndb/include/kernel/signaldata/AccSizeAltReq.hpp: mvdir storage/ndb/include/kernel/signaldata/AlterIndx.hpp: mvdir storage/ndb/include/kernel/signaldata/AlterTab.hpp: mvdir storage/ndb/include/kernel/signaldata/AlterTable.hpp: mvdir storage/ndb/include/kernel/signaldata/AlterTrig.hpp: mvdir storage/ndb/include/kernel/signaldata/ApiRegSignalData.hpp: mvdir storage/ndb/include/kernel/signaldata/ApiVersion.hpp: mvdir storage/ndb/include/kernel/signaldata/ArbitSignalData.hpp: mvdir storage/ndb/include/kernel/signaldata/AttrInfo.hpp: mvdir storage/ndb/include/kernel/trigger_definitions.h: mvdir storage/ndb/include/ndb_constants.h: mvdir storage/ndb/include/ndb_global.h.in: mvdir storage/ndb/include/ndb_init.h: mvdir storage/ndb/include/ndb_net.h: mvdir storage/ndb/include/ndb_types.h.in: mvdir storage/ndb/include/ndb_version.h.in: mvdir storage/ndb/include/kernel/signaldata/BackupContinueB.hpp: mvdir storage/ndb/include/kernel/signaldata/BackupImpl.hpp: mvdir storage/ndb/include/kernel/signaldata/BackupSignalData.hpp: mvdir storage/ndb/include/kernel/signaldata/BlockCommitOrd.hpp: mvdir storage/ndb/include/kernel/signaldata/BuildIndx.hpp: mvdir storage/ndb/include/kernel/signaldata/CheckNodeGroups.hpp: mvdir storage/ndb/include/kernel/signaldata/CloseComReqConf.hpp: mvdir storage/ndb/include/kernel/signaldata/CmInit.hpp: mvdir storage/ndb/include/kernel/signaldata/CmRegSignalData.hpp: mvdir storage/ndb/include/kernel/signaldata/CmvmiCfgConf.hpp: mvdir storage/ndb/include/kernel/signaldata/CntrMasterConf.hpp: mvdir storage/ndb/include/kernel/signaldata/CntrMasterReq.hpp: mvdir storage/ndb/include/kernel/signaldata/CntrStart.hpp: mvdir storage/ndb/include/kernel/signaldata/ConfigParamId.hpp: mvdir storage/ndb/include/kernel/signaldata/ContinueFragmented.hpp: mvdir storage/ndb/include/kernel/signaldata/CopyActive.hpp: mvdir storage/ndb/include/kernel/signaldata/CopyFrag.hpp: mvdir storage/ndb/include/kernel/signaldata/CopyGCIReq.hpp: mvdir storage/ndb/include/kernel/signaldata/CreateEvnt.hpp: mvdir storage/ndb/include/kernel/signaldata/CreateFrag.hpp: mvdir storage/ndb/include/kernel/signaldata/CreateFragmentation.hpp: mvdir storage/ndb/include/kernel/signaldata/CreateIndx.hpp: mvdir storage/ndb/include/kernel/signaldata/CreateTab.hpp: mvdir storage/ndb/include/kernel/signaldata/CreateTable.hpp: mvdir storage/ndb/include/kernel/signaldata/CreateTrig.hpp: mvdir storage/ndb/include/kernel/signaldata/DiAddTab.hpp: mvdir storage/ndb/include/kernel/signaldata/DiGetNodes.hpp: mvdir storage/ndb/include/kernel/signaldata/DictSchemaInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/DictSizeAltReq.hpp: mvdir storage/ndb/include/kernel/signaldata/DictStart.hpp: mvdir storage/ndb/include/kernel/signaldata/DictTabInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/DihAddFrag.hpp: mvdir storage/ndb/include/kernel/signaldata/DihContinueB.hpp: mvdir storage/ndb/include/kernel/signaldata/DihSizeAltReq.hpp: mvdir storage/ndb/include/kernel/signaldata/DihStartTab.hpp: mvdir storage/ndb/include/kernel/signaldata/DihSwitchReplica.hpp: mvdir storage/ndb/include/kernel/signaldata/DisconnectRep.hpp: mvdir storage/ndb/include/kernel/signaldata/DropIndx.hpp: mvdir storage/ndb/include/kernel/signaldata/DropTab.hpp: mvdir storage/ndb/include/kernel/signaldata/DropTabFile.hpp: mvdir storage/ndb/include/kernel/signaldata/DropTable.hpp: mvdir storage/ndb/include/kernel/signaldata/DropTrig.hpp: mvdir storage/ndb/include/kernel/signaldata/DumpStateOrd.hpp: mvdir storage/ndb/include/kernel/signaldata/EmptyLcp.hpp: mvdir storage/ndb/include/kernel/signaldata/EndTo.hpp: mvdir storage/ndb/include/kernel/signaldata/EventReport.hpp: mvdir storage/ndb/include/kernel/signaldata/EventSubscribeReq.hpp: mvdir storage/ndb/include/kernel/signaldata/ExecFragReq.hpp: mvdir storage/ndb/include/kernel/signaldata/FailRep.hpp: mvdir storage/ndb/include/kernel/signaldata/FireTrigOrd.hpp: mvdir storage/ndb/include/kernel/signaldata/FsAppendReq.hpp: mvdir storage/ndb/include/kernel/signaldata/FsCloseReq.hpp: mvdir storage/ndb/include/kernel/signaldata/FsConf.hpp: mvdir storage/ndb/include/kernel/signaldata/FsOpenReq.hpp: mvdir storage/ndb/include/kernel/signaldata/FsReadWriteReq.hpp: mvdir storage/ndb/include/kernel/signaldata/FsRef.hpp: mvdir storage/ndb/include/kernel/signaldata/FsRemoveReq.hpp: mvdir storage/ndb/include/kernel/signaldata/GCPSave.hpp: mvdir storage/ndb/include/kernel/signaldata/GetTabInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/GetTableId.hpp: mvdir storage/ndb/include/kernel/signaldata/GrepImpl.hpp: mvdir storage/ndb/include/kernel/signaldata/HotSpareRep.hpp: mvdir storage/ndb/include/kernel/signaldata/IndxAttrInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/IndxKeyInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/InvalidateNodeLCPConf.hpp: mvdir storage/ndb/include/kernel/signaldata/InvalidateNodeLCPReq.hpp: mvdir storage/ndb/include/kernel/signaldata/KeyInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/LCP.hpp: mvdir storage/ndb/include/kernel/signaldata/ListTables.hpp: mvdir storage/ndb/include/kernel/signaldata/LqhFrag.hpp: mvdir storage/ndb/include/kernel/signaldata/LqhKey.hpp: mvdir storage/ndb/include/kernel/signaldata/LqhSizeAltReq.hpp: mvdir storage/ndb/include/kernel/signaldata/LqhTransConf.hpp: mvdir storage/ndb/include/kernel/signaldata/ManagementServer.hpp: mvdir storage/ndb/include/kernel/signaldata/MasterGCP.hpp: mvdir storage/ndb/include/kernel/signaldata/MasterLCP.hpp: mvdir storage/ndb/include/kernel/signaldata/NFCompleteRep.hpp: mvdir storage/ndb/include/kernel/signaldata/NdbSttor.hpp: mvdir storage/ndb/include/kernel/signaldata/NdbfsContinueB.hpp: mvdir storage/ndb/include/kernel/signaldata/NextScan.hpp: mvdir storage/ndb/include/kernel/signaldata/NodeFailRep.hpp: mvdir storage/ndb/include/kernel/signaldata/NodeStateSignalData.hpp: mvdir storage/ndb/include/kernel/signaldata/PackedSignal.hpp: mvdir storage/ndb/include/kernel/signaldata/PrepDropTab.hpp: mvdir storage/ndb/include/kernel/signaldata/PrepFailReqRef.hpp: mvdir storage/ndb/include/kernel/signaldata/ReadConfig.hpp: mvdir storage/ndb/include/kernel/signaldata/ReadNodesConf.hpp: mvdir storage/ndb/include/kernel/signaldata/RelTabMem.hpp: mvdir storage/ndb/include/kernel/signaldata/RepImpl.hpp: mvdir storage/ndb/include/kernel/signaldata/ResumeReq.hpp: mvdir storage/ndb/include/kernel/signaldata/ScanFrag.hpp: mvdir storage/ndb/include/kernel/signaldata/ScanTab.hpp: mvdir storage/ndb/include/kernel/signaldata/SetLogLevelOrd.hpp: mvdir storage/ndb/include/kernel/signaldata/SetVarReq.hpp: mvdir storage/ndb/include/kernel/signaldata/SignalData.hpp: mvdir storage/ndb/include/kernel/signaldata/SignalDataPrint.hpp: mvdir storage/ndb/include/kernel/signaldata/SignalDroppedRep.hpp: mvdir storage/ndb/include/kernel/signaldata/SrFragidConf.hpp: mvdir storage/ndb/include/kernel/signaldata/StartFragReq.hpp: mvdir storage/ndb/include/kernel/signaldata/StartInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/StartMe.hpp: mvdir storage/ndb/include/kernel/signaldata/StartOrd.hpp: mvdir storage/ndb/include/kernel/signaldata/StartPerm.hpp: mvdir storage/ndb/include/kernel/signaldata/StartRec.hpp: mvdir storage/ndb/include/kernel/signaldata/StartTo.hpp: mvdir storage/ndb/include/kernel/signaldata/StopMe.hpp: mvdir storage/ndb/include/kernel/signaldata/StopPerm.hpp: mvdir storage/ndb/include/kernel/signaldata/StopReq.hpp: mvdir storage/ndb/include/kernel/signaldata/SumaImpl.hpp: mvdir storage/ndb/include/kernel/signaldata/SystemError.hpp: mvdir storage/ndb/include/kernel/signaldata/TamperOrd.hpp: mvdir storage/ndb/include/kernel/signaldata/TcCommit.hpp: mvdir storage/ndb/include/kernel/signaldata/TcContinueB.hpp: mvdir storage/ndb/include/kernel/signaldata/TcHbRep.hpp: mvdir storage/ndb/include/kernel/signaldata/TcIndx.hpp: mvdir storage/ndb/include/kernel/signaldata/TcKeyConf.hpp: mvdir storage/ndb/include/kernel/signaldata/TcKeyFailConf.hpp: mvdir storage/ndb/include/kernel/signaldata/TcKeyRef.hpp: mvdir storage/ndb/include/kernel/signaldata/TcKeyReq.hpp: mvdir storage/ndb/include/kernel/signaldata/TcRollbackRep.hpp: mvdir storage/ndb/include/kernel/signaldata/TcSizeAltReq.hpp: mvdir storage/ndb/include/kernel/signaldata/TestOrd.hpp: mvdir storage/ndb/include/kernel/signaldata/TransIdAI.hpp: mvdir storage/ndb/include/kernel/signaldata/TrigAttrInfo.hpp: mvdir storage/ndb/include/kernel/signaldata/TupCommit.hpp: mvdir storage/ndb/include/kernel/signaldata/TupFrag.hpp: mvdir storage/ndb/include/kernel/signaldata/TupKey.hpp: mvdir storage/ndb/include/kernel/signaldata/TupSizeAltReq.hpp: mvdir storage/ndb/include/kernel/signaldata/TuxBound.hpp: mvdir storage/ndb/include/kernel/signaldata/TuxContinueB.hpp: mvdir storage/ndb/include/kernel/signaldata/TuxMaint.hpp: mvdir storage/ndb/include/kernel/signaldata/TuxSizeAltReq.hpp: mvdir storage/ndb/include/kernel/signaldata/UpdateTo.hpp: mvdir storage/ndb/include/kernel/signaldata/UpgradeStartup.hpp: mvdir storage/ndb/include/kernel/signaldata/UtilDelete.hpp: mvdir storage/ndb/include/kernel/signaldata/UtilExecute.hpp: mvdir storage/ndb/include/kernel/signaldata/UtilLock.hpp: mvdir storage/ndb/include/kernel/signaldata/UtilPrepare.hpp: mvdir storage/ndb/include/kernel/signaldata/UtilRelease.hpp: mvdir storage/ndb/include/kernel/signaldata/UtilSequence.hpp: mvdir storage/ndb/include/kernel/signaldata/WaitGCP.hpp: mvdir storage/ndb/include/logger/ConsoleLogHandler.hpp: mvdir storage/ndb/include/logger/FileLogHandler.hpp: mvdir storage/ndb/include/logger/LogHandler.hpp: mvdir storage/ndb/include/logger/Logger.hpp: mvdir storage/ndb/include/logger/SysLogHandler.hpp: mvdir storage/ndb/include/mgmapi/mgmapi.h: mvdir storage/ndb/include/mgmapi/mgmapi_config_parameters.h: mvdir storage/ndb/include/mgmapi/mgmapi_config_parameters_debug.h: mvdir storage/ndb/include/mgmapi/mgmapi_debug.h: mvdir storage/ndb/include/mgmapi/ndb_logevent.h: mvdir storage/ndb/include/mgmcommon/ConfigRetriever.hpp: mvdir storage/ndb/include/mgmcommon/IPCConfig.hpp: mvdir storage/ndb/include/mgmcommon/MgmtErrorReporter.hpp: mvdir storage/ndb/include/ndbapi/Ndb.hpp: mvdir storage/ndb/include/ndbapi/NdbApi.hpp: mvdir storage/ndb/include/ndbapi/NdbBlob.hpp: mvdir storage/ndb/include/ndbapi/NdbDictionary.hpp: mvdir storage/ndb/include/ndbapi/NdbError.hpp: mvdir storage/ndb/include/ndbapi/NdbEventOperation.hpp: mvdir storage/ndb/include/ndbapi/NdbIndexOperation.hpp: mvdir storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp: mvdir storage/ndb/include/ndbapi/NdbOperation.hpp: mvdir storage/ndb/include/ndbapi/NdbPool.hpp: mvdir storage/ndb/include/ndbapi/NdbRecAttr.hpp: mvdir storage/ndb/include/ndbapi/NdbReceiver.hpp: mvdir storage/ndb/include/ndbapi/NdbScanFilter.hpp: mvdir storage/ndb/include/ndbapi/NdbScanOperation.hpp: mvdir storage/ndb/include/ndbapi/NdbTransaction.hpp: mvdir storage/ndb/include/ndbapi/ndb_cluster_connection.hpp: mvdir storage/ndb/include/ndbapi/ndb_opt_defaults.h: mvdir storage/ndb/include/ndbapi/ndbapi_limits.h: mvdir storage/ndb/include/ndbapi/ndberror.h: mvdir storage/ndb/include/newtonapi/dba.h: mvdir storage/ndb/include/newtonapi/defs/pcn_types.h: mvdir storage/ndb/include/portlib/NdbCondition.h: mvdir storage/ndb/include/portlib/NdbConfig.h: mvdir storage/ndb/include/portlib/NdbDaemon.h: mvdir storage/ndb/include/portlib/NdbEnv.h: mvdir storage/ndb/include/portlib/NdbHost.h: mvdir storage/ndb/include/portlib/NdbMain.h: mvdir storage/ndb/include/portlib/NdbMem.h: mvdir storage/ndb/include/portlib/NdbMutex.h: mvdir storage/ndb/include/portlib/NdbSleep.h: mvdir storage/ndb/include/portlib/NdbTCP.h: mvdir storage/ndb/include/portlib/NdbThread.h: mvdir storage/ndb/include/portlib/NdbTick.h: mvdir storage/ndb/include/portlib/PortDefs.h: mvdir storage/ndb/include/portlib/prefetch.h: mvdir storage/ndb/include/transporter/TransporterCallback.hpp: mvdir storage/ndb/include/transporter/TransporterDefinitions.hpp: mvdir storage/ndb/include/transporter/TransporterRegistry.hpp: mvdir storage/ndb/include/util/Base64.hpp: mvdir storage/ndb/include/util/BaseString.hpp: mvdir storage/ndb/include/util/Bitmask.hpp: mvdir storage/ndb/include/util/ConfigValues.hpp: mvdir storage/ndb/include/util/File.hpp: mvdir storage/ndb/include/util/InputStream.hpp: mvdir storage/ndb/include/util/NdbAutoPtr.hpp: mvdir storage/ndb/include/util/NdbOut.hpp: mvdir storage/ndb/include/util/NdbSqlUtil.hpp: mvdir storage/ndb/include/util/OutputStream.hpp: mvdir storage/ndb/include/util/Parser.hpp: mvdir storage/ndb/include/util/Properties.hpp: mvdir storage/ndb/include/util/SimpleProperties.hpp: mvdir storage/ndb/include/util/SocketAuthenticator.hpp: mvdir storage/ndb/include/util/SocketClient.hpp: mvdir storage/ndb/include/util/SocketServer.hpp: mvdir storage/ndb/include/util/UtilBuffer.hpp: mvdir storage/ndb/include/util/Vector.hpp: mvdir storage/ndb/include/util/basestring_vsnprintf.h: mvdir storage/ndb/include/util/md5_hash.hpp: mvdir storage/ndb/include/util/ndb_opts.h: mvdir storage/ndb/include/util/random.h: mvdir storage/ndb/include/util/socket_io.h: mvdir storage/ndb/include/util/uucode.h: mvdir storage/ndb/include/util/version.h: mvdir storage/ndb/lib/.empty: mvdir storage/ndb/ndbapi-examples/Makefile: mvdir storage/ndb/ndbapi-examples/mgmapi_logevent_example/Makefile: mvdir storage/ndb/ndbapi-examples/mgmapi_logevent_example/mgmapi_logevent.cpp: mvdir storage/ndb/ndbapi-examples/ndbapi_async_example/Makefile: mvdir storage/ndb/ndbapi-examples/ndbapi_async_example/ndbapi_async.cpp: mvdir storage/ndb/ndbapi-examples/ndbapi_async_example/readme.txt: mvdir storage/ndb/ndbapi-examples/ndbapi_async_example1/Makefile: mvdir storage/ndb/ndbapi-examples/ndbapi_async_example1/ndbapi_async1.cpp: mvdir storage/ndb/ndbapi-examples/ndbapi_event_example/Makefile: mvdir storage/ndb/ndbapi-examples/ndbapi_event_example/ndbapi_event.cpp: mvdir storage/ndb/ndbapi-examples/ndbapi_retries_example/Makefile: mvdir storage/ndb/ndbapi-examples/ndbapi_retries_example/ndbapi_retries.cpp: mvdir storage/ndb/ndbapi-examples/ndbapi_scan_example/Makefile: mvdir storage/ndb/ndbapi-examples/ndbapi_scan_example/ndbapi_scan.cpp: mvdir storage/ndb/ndbapi-examples/ndbapi_scan_example/readme.txt: mvdir storage/ndb/ndbapi-examples/ndbapi_simple_example/Makefile: mvdir storage/ndb/ndbapi-examples/ndbapi_simple_example/ndbapi_simple.cpp: mvdir storage/ndb/ndbapi-examples/ndbapi_simple_index_example/Makefile: mvdir storage/ndb/ndbapi-examples/ndbapi_simple_index_example/ndbapi_simple_index.cpp: mvdir storage/ndb/src/Makefile.am: mvdir storage/ndb/src/common/Makefile.am: mvdir storage/ndb/src/common/debugger/BlockNames.cpp: mvdir storage/ndb/src/common/debugger/DebuggerNames.cpp: mvdir storage/ndb/src/common/debugger/EventLogger.cpp: mvdir storage/ndb/src/common/debugger/GrepError.cpp: mvdir storage/ndb/src/common/debugger/Makefile.am: mvdir storage/ndb/src/common/debugger/SignalLoggerManager.cpp: mvdir storage/ndb/src/common/debugger/signaldata/AccLock.cpp: mvdir storage/ndb/src/common/debugger/signaldata/AlterIndx.cpp: mvdir storage/ndb/src/common/debugger/signaldata/AlterTab.cpp: mvdir storage/ndb/src/common/debugger/signaldata/AlterTable.cpp: mvdir storage/ndb/src/common/debugger/signaldata/AlterTrig.cpp: mvdir storage/ndb/src/common/debugger/signaldata/BackupImpl.cpp: mvdir storage/ndb/src/common/debugger/signaldata/BackupSignalData.cpp: mvdir storage/ndb/src/common/debugger/signaldata/CloseComReqConf.cpp: mvdir storage/ndb/src/common/debugger/signaldata/CntrStart.cpp: mvdir storage/ndb/src/common/debugger/signaldata/ContinueB.cpp: mvdir storage/ndb/src/common/debugger/signaldata/CopyGCI.cpp: mvdir storage/ndb/src/common/debugger/signaldata/CreateEvnt.cpp: mvdir storage/ndb/src/common/debugger/signaldata/CreateFragmentation.cpp: mvdir storage/ndb/src/common/debugger/signaldata/CreateIndx.cpp: mvdir storage/ndb/src/common/debugger/signaldata/CreateTrig.cpp: mvdir storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp: mvdir storage/ndb/src/common/debugger/signaldata/DihContinueB.cpp: mvdir storage/ndb/src/common/debugger/signaldata/DihSwitchReplicaReq.cpp: mvdir storage/ndb/src/common/debugger/signaldata/DisconnectRep.cpp: mvdir storage/ndb/src/common/debugger/signaldata/DropIndx.cpp: mvdir storage/ndb/src/common/debugger/signaldata/DropTab.cpp: mvdir storage/ndb/src/common/debugger/signaldata/DropTrig.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FailRep.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FireTrigOrd.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FsAppendReq.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FsCloseReq.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FsConf.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FsOpenReq.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp: mvdir storage/ndb/src/common/debugger/signaldata/FsRef.cpp: mvdir storage/ndb/src/common/debugger/signaldata/GCPSave.cpp: mvdir storage/ndb/src/common/debugger/signaldata/IndxAttrInfo.cpp: mvdir storage/ndb/src/common/debugger/signaldata/IndxKeyInfo.cpp: mvdir storage/ndb/src/common/debugger/signaldata/LCP.cpp: mvdir storage/ndb/src/common/debugger/signaldata/LqhFrag.cpp: mvdir storage/ndb/src/common/debugger/signaldata/LqhKey.cpp: mvdir storage/ndb/src/common/debugger/signaldata/LqhTrans.cpp: mvdir storage/ndb/src/common/debugger/signaldata/Makefile.am: mvdir storage/ndb/src/common/debugger/signaldata/MasterLCP.cpp: mvdir storage/ndb/src/common/debugger/signaldata/NFCompleteRep.cpp: mvdir storage/ndb/src/common/debugger/signaldata/NdbSttor.cpp: mvdir storage/ndb/src/common/debugger/signaldata/NdbfsContinueB.cpp: mvdir storage/ndb/src/common/debugger/signaldata/PackedSignal.cpp: mvdir storage/ndb/src/common/debugger/signaldata/PrepDropTab.cpp: mvdir storage/ndb/src/common/debugger/signaldata/PrepFailReqRef.cpp: mvdir storage/ndb/src/common/debugger/signaldata/ReadNodesConf.cpp: mvdir storage/ndb/src/common/debugger/signaldata/ScanFrag.cpp: mvdir storage/ndb/src/common/debugger/signaldata/ScanTab.cpp: mvdir storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp: mvdir storage/ndb/src/common/debugger/signaldata/SignalDroppedRep.cpp: mvdir storage/ndb/src/common/debugger/signaldata/SignalNames.cpp: mvdir storage/ndb/src/common/debugger/signaldata/StartRec.cpp: mvdir storage/ndb/src/common/debugger/signaldata/SumaImpl.cpp: mvdir storage/ndb/src/common/debugger/signaldata/SystemError.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TcIndx.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TcKeyConf.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TcKeyRef.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TcKeyReq.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TcRollbackRep.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TrigAttrInfo.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TupCommit.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TupKey.cpp: mvdir storage/ndb/src/common/debugger/signaldata/TuxMaint.cpp: mvdir storage/ndb/src/common/debugger/signaldata/UtilDelete.cpp: mvdir storage/ndb/src/common/debugger/signaldata/UtilExecute.cpp: mvdir storage/ndb/src/common/debugger/signaldata/UtilLock.cpp: mvdir storage/ndb/src/common/debugger/signaldata/UtilPrepare.cpp: mvdir storage/ndb/src/common/debugger/signaldata/UtilSequence.cpp: mvdir storage/ndb/src/common/debugger/signaldata/print.awk: mvdir storage/ndb/src/common/logger/ConsoleLogHandler.cpp: mvdir storage/ndb/src/common/logger/FileLogHandler.cpp: mvdir storage/ndb/src/common/logger/LogHandler.cpp: mvdir storage/ndb/src/common/logger/LogHandlerList.cpp: mvdir storage/ndb/src/common/logger/LogHandlerList.hpp: mvdir storage/ndb/src/common/logger/Logger.cpp: mvdir storage/ndb/src/common/logger/Makefile.am: mvdir storage/ndb/src/common/logger/SysLogHandler.cpp: mvdir storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.cpp: mvdir storage/ndb/src/common/logger/listtest/LogHandlerListUnitTest.hpp: mvdir storage/ndb/src/common/logger/listtest/Makefile: mvdir storage/ndb/src/common/logger/loggertest/LoggerUnitTest.cpp: mvdir storage/ndb/src/common/logger/loggertest/LoggerUnitTest.hpp: mvdir storage/ndb/src/common/logger/loggertest/Makefile: mvdir storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp: mvdir storage/ndb/src/common/mgmcommon/IPCConfig.cpp: mvdir storage/ndb/src/common/mgmcommon/Makefile.am: mvdir storage/ndb/src/common/mgmcommon/printConfig/Makefile: mvdir storage/ndb/src/common/mgmcommon/printConfig/printConfig.cpp: mvdir storage/ndb/src/common/portlib/Makefile.am: mvdir storage/ndb/src/common/portlib/NdbCondition.c: mvdir storage/ndb/src/common/portlib/NdbConfig.c: mvdir storage/ndb/src/common/portlib/NdbDaemon.c: mvdir storage/ndb/src/common/portlib/NdbEnv.c: mvdir storage/ndb/src/common/portlib/NdbHost.c: mvdir storage/ndb/src/common/portlib/NdbMem.c: mvdir storage/ndb/src/common/portlib/NdbMutex.c: mvdir storage/ndb/src/common/portlib/NdbPortLibTest.cpp: mvdir storage/ndb/src/common/portlib/NdbSleep.c: mvdir storage/ndb/src/common/portlib/NdbTCP.cpp: mvdir storage/ndb/src/common/portlib/NdbThread.c: mvdir storage/ndb/src/common/portlib/NdbTick.c: mvdir storage/ndb/src/common/portlib/gcc.cpp: mvdir storage/ndb/src/common/portlib/memtest.c: mvdir storage/ndb/src/common/portlib/mmslist.cpp: mvdir storage/ndb/src/common/portlib/mmstest.cpp: mvdir storage/ndb/src/common/portlib/munmaptest.cpp: mvdir storage/ndb/src/common/portlib/old_dirs/memtest/Makefile: mvdir storage/ndb/src/common/portlib/old_dirs/memtest/munmaptest/Makefile: mvdir storage/ndb/src/common/portlib/old_dirs/ose/Makefile: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbCondition.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbConditionOSE.h: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbEnv.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbHost.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbMem.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbMem_SoftOse.cpp: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbMutex.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbOut.cpp: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbSleep.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbTCP.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbThread.c: mvdir storage/ndb/src/common/portlib/old_dirs/ose/NdbTick.c: mvdir storage/ndb/src/common/portlib/old_dirs/test/Makefile: mvdir storage/ndb/src/common/portlib/old_dirs/win32/Makefile: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbCondition.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbDaemon.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbEnv.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbHost.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbMem.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbMutex.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbSleep.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbTCP.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbThread.c: mvdir storage/ndb/src/common/portlib/old_dirs/win32/NdbTick.c: mvdir storage/ndb/src/common/portlib/win32/NdbCondition.c: mvdir storage/ndb/src/common/portlib/win32/NdbDaemon.c: mvdir storage/ndb/src/common/portlib/win32/NdbEnv.c: mvdir storage/ndb/src/common/portlib/win32/NdbHost.c: mvdir storage/ndb/src/common/portlib/win32/NdbMem.c: mvdir storage/ndb/src/common/portlib/win32/NdbMutex.c: mvdir storage/ndb/src/common/portlib/win32/NdbSleep.c: mvdir storage/ndb/src/common/portlib/win32/NdbTCP.c: mvdir storage/ndb/src/common/portlib/win32/NdbThread.c: mvdir storage/ndb/src/common/portlib/win32/NdbTick.c: mvdir storage/ndb/src/common/transporter/Makefile.am: mvdir storage/ndb/src/common/transporter/OSE_Receiver.cpp: mvdir storage/ndb/src/common/transporter/OSE_Receiver.hpp: mvdir storage/ndb/src/common/transporter/OSE_Signals.hpp: mvdir storage/ndb/src/common/transporter/OSE_Transporter.cpp: mvdir storage/ndb/src/common/transporter/OSE_Transporter.hpp: mvdir storage/ndb/src/common/transporter/Packer.cpp: mvdir storage/ndb/src/common/transporter/Packer.hpp: mvdir storage/ndb/src/common/transporter/SCI_Transporter.cpp: mvdir storage/ndb/src/common/transporter/SCI_Transporter.hpp: mvdir storage/ndb/src/common/transporter/SHM_Buffer.hpp: mvdir storage/ndb/src/common/transporter/SHM_Transporter.cpp: mvdir storage/ndb/src/common/transporter/SHM_Transporter.hpp: mvdir storage/ndb/src/common/transporter/SHM_Transporter.unix.cpp: mvdir storage/ndb/src/common/transporter/SHM_Transporter.win32.cpp: mvdir storage/ndb/src/common/transporter/SendBuffer.cpp: mvdir storage/ndb/src/common/transporter/SendBuffer.hpp: mvdir storage/ndb/src/common/transporter/TCP_Transporter.cpp: mvdir storage/ndb/src/common/transporter/TCP_Transporter.hpp: mvdir storage/ndb/src/common/transporter/Transporter.cpp: mvdir storage/ndb/src/common/transporter/Transporter.hpp: mvdir storage/ndb/src/common/transporter/TransporterInternalDefinitions.hpp: mvdir storage/ndb/src/common/transporter/TransporterRegistry.cpp: mvdir storage/ndb/src/common/transporter/basictest/Makefile: mvdir storage/ndb/src/common/transporter/basictest/basicTransporterTest.cpp: mvdir storage/ndb/src/common/transporter/buddy.cpp: mvdir storage/ndb/src/common/transporter/buddy.hpp: mvdir storage/ndb/src/common/transporter/failoverSCI/Makefile: mvdir storage/ndb/src/common/transporter/failoverSCI/failoverSCI.cpp: mvdir storage/ndb/src/common/transporter/perftest/Makefile: mvdir storage/ndb/src/common/transporter/perftest/perfTransporterTest.cpp: mvdir storage/ndb/src/common/transporter/priotest/Makefile: mvdir storage/ndb/src/common/transporter/priotest/prioOSE/Makefile: mvdir storage/ndb/src/common/transporter/priotest/prioSCI/Makefile: mvdir storage/ndb/src/common/transporter/priotest/prioSCI/prioSCI.cpp: mvdir storage/ndb/src/common/transporter/priotest/prioSHM/Makefile: mvdir storage/ndb/src/common/transporter/priotest/prioSHM/prioSHM.cpp: mvdir storage/ndb/src/common/transporter/priotest/prioTCP/Makefile: mvdir storage/ndb/src/common/transporter/priotest/prioTCP/prioTCP.cpp: mvdir storage/ndb/src/common/transporter/priotest/prioTransporterTest.cpp: mvdir storage/ndb/src/common/transporter/priotest/prioTransporterTest.hpp: mvdir storage/ndb/src/common/util/Base64.cpp: mvdir storage/ndb/src/common/util/BaseString.cpp: mvdir storage/ndb/src/common/util/Bitmask.cpp: mvdir storage/ndb/src/common/util/ConfigValues.cpp: mvdir storage/ndb/src/common/util/File.cpp: mvdir storage/ndb/src/common/util/InputStream.cpp: mvdir storage/ndb/src/common/util/Makefile.am: mvdir storage/ndb/src/common/util/NdbErrHnd.cpp: mvdir storage/ndb/src/common/util/NdbOut.cpp: mvdir storage/ndb/src/common/util/NdbSqlUtil.cpp: mvdir storage/ndb/src/common/util/OutputStream.cpp: mvdir storage/ndb/src/common/util/Parser.cpp: mvdir storage/ndb/src/common/util/Properties.cpp: mvdir storage/ndb/src/common/util/SimpleProperties.cpp: mvdir storage/ndb/src/common/util/SocketAuthenticator.cpp: mvdir storage/ndb/src/common/util/SocketClient.cpp: mvdir storage/ndb/src/common/util/SocketServer.cpp: mvdir storage/ndb/src/common/util/basestring_vsnprintf.c: mvdir storage/ndb/src/common/util/filetest/FileUnitTest.cpp: mvdir storage/ndb/src/common/util/filetest/FileUnitTest.hpp: mvdir storage/ndb/src/common/util/filetest/Makefile: mvdir storage/ndb/src/common/util/getarg.cat3: mvdir storage/ndb/src/common/util/md5_hash.cpp: mvdir storage/ndb/src/common/util/ndb_init.c: mvdir storage/ndb/src/common/util/new.cpp: mvdir storage/ndb/src/common/util/random.c: mvdir storage/ndb/src/common/util/socket_io.cpp: mvdir storage/ndb/src/common/util/strdup.c: mvdir storage/ndb/src/common/util/testConfigValues/Makefile: mvdir storage/ndb/src/common/util/testConfigValues/testConfigValues.cpp: mvdir storage/ndb/src/common/util/uucode.c: mvdir storage/ndb/src/common/util/version.c: mvdir storage/ndb/src/common/util/testProperties/Makefile: mvdir storage/ndb/src/common/util/testProperties/testProperties.cpp: mvdir storage/ndb/src/common/util/testSimpleProperties/Makefile: mvdir storage/ndb/src/common/util/testSimpleProperties/sp_test.cpp: mvdir storage/ndb/src/cw/Makefile.am: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.cpp: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsp: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.dsw: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.h: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.ico: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.rc: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.sln: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.suo: mvdir storage/ndb/src/cw/cpcc-win32/C++/CPC_GUI.vcproj: mvdir storage/ndb/src/cw/cpcc-win32/C++/Closed.ICO: mvdir storage/ndb/src/cw/cpcc-win32/C++/NdbControls.cpp: mvdir storage/ndb/src/cw/cpcc-win32/C++/Open.ICO: mvdir storage/ndb/src/cw/cpcc-win32/C++/StdAfx.cpp: mvdir storage/ndb/src/cw/cpcc-win32/C++/StdAfx.h: mvdir storage/ndb/src/cw/cpcc-win32/C++/TreeView.cpp: mvdir storage/ndb/src/cw/cpcc-win32/C++/TreeView.h: mvdir storage/ndb/src/cw/cpcc-win32/C++/bmp00001.bmp: mvdir storage/ndb/src/cw/cpcc-win32/C++/resource.h: mvdir storage/ndb/src/cw/cpcc-win32/C++/small.ico: mvdir storage/ndb/src/cw/cpcc-win32/C++/toolbar.bmp: mvdir storage/ndb/src/cw/cpcc-win32/csharp/App.ico: mvdir storage/ndb/src/cw/cpcc-win32/csharp/CPC_Form.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/Computer.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/ComputerAddDialog.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/ComputerRemoveDialog.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/DATABASE.ICO: mvdir storage/ndb/src/cw/cpcc-win32/csharp/Database.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj.user: mvdir storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.csproj: mvdir storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.ncb: mvdir storage/ndb/src/cw/cpcc-win32/csharp/NDB_CPC.sln: mvdir storage/ndb/src/cw/cpcc-win32/csharp/PanelWizard.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/Process.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/ProcessDefineDialog.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/fileaccess/FileMgmt.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/simpleparser/SimpleCPCParser.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/SocketComm.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/socketcomm/myTcpClient.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/startDatabaseDlg.cs: mvdir storage/ndb/src/cw/cpcc-win32/csharp/telnetclient/telnetClient.cs: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Computer.cls: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Database.cls: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 110.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 231.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 237.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 241.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 242.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 270.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 271.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 273.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 31.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 337.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 338.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Icon 339.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/MSSCCPRJ.SCC: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Module1.bas: mvdir storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbp: mvdir storage/ndb/src/cw/cpcc-win32/vb6/NdbCPC.vbw: mvdir storage/ndb/src/cw/cpcc-win32/vb6/Process.cls: mvdir storage/ndb/src/cw/cpcc-win32/vb6/closed folder.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/computer.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmAbout.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmLogin.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmMain.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmNewComputer.frx: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase.frx: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase1.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase2.log: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmNewDatabase3.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmOptions.frm: mvdir storage/ndb/src/cw/cpcc-win32/vb6/frmSplash.frx: mvdir storage/ndb/src/cw/cpcc-win32/vb6/networking.ico: mvdir storage/ndb/src/cw/cpcc-win32/vb6/open folder.ico: mvdir storage/ndb/src/cw/cpcd/APIService.cpp: mvdir storage/ndb/src/cw/cpcd/APIService.hpp: mvdir storage/ndb/src/cw/cpcd/CPCD.cpp: mvdir storage/ndb/src/cw/cpcd/CPCD.hpp: mvdir storage/ndb/src/cw/cpcd/Makefile.am: mvdir storage/ndb/src/cw/cpcd/Monitor.cpp: mvdir storage/ndb/src/cw/cpcd/Process.cpp: mvdir storage/ndb/src/cw/cpcd/common.cpp: mvdir storage/ndb/src/cw/cpcd/common.hpp: mvdir storage/ndb/src/cw/cpcd/main.cpp: mvdir storage/ndb/src/cw/test/socketclient/Makefile: mvdir storage/ndb/src/cw/test/socketclient/socketClientTest.cpp: mvdir storage/ndb/src/cw/util/ClientInterface.cpp: mvdir storage/ndb/src/cw/util/ClientInterface.hpp: mvdir storage/ndb/src/cw/util/Makefile: mvdir storage/ndb/src/cw/util/SocketRegistry.cpp: mvdir storage/ndb/src/cw/util/SocketRegistry.hpp: mvdir storage/ndb/src/cw/util/SocketService.cpp: mvdir storage/ndb/src/cw/util/SocketService.hpp: mvdir storage/ndb/src/external/WIN32.x86/sci/lib/SISCI_LIBRARY_WIN32.TXT: mvdir storage/ndb/src/external/WIN32.x86/sci/lib/scilib.lib: mvdir storage/ndb/src/external/WIN32.x86/sci/lib/scilib_md.lib: mvdir storage/ndb/src/external/WIN32.x86/sci/lib/scilib_mt.lib: mvdir storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api.lib: mvdir storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_md.lib: mvdir storage/ndb/src/external/WIN32.x86/sci/lib/sisci_api_mt.lib: mvdir storage/ndb/src/kernel/Makefile.am: mvdir storage/ndb/src/kernel/SimBlockList.cpp: mvdir storage/ndb/src/kernel/blocks/ERROR_codes.txt: mvdir storage/ndb/src/kernel/blocks/Makefile.am: mvdir storage/ndb/src/kernel/blocks/NodeRestart.new.txt: mvdir storage/ndb/src/kernel/blocks/NodeRestart.txt: mvdir storage/ndb/src/kernel/blocks/Start.txt: mvdir storage/ndb/src/kernel/blocks/SystemRestart.new.txt: mvdir storage/ndb/src/kernel/blocks/SystemRestart.txt: mvdir storage/ndb/src/kernel/blocks/backup/Backup.cpp: mvdir storage/ndb/src/kernel/blocks/backup/Backup.hpp: mvdir storage/ndb/src/kernel/blocks/backup/Backup.txt: mvdir storage/ndb/src/kernel/blocks/backup/BackupFormat.hpp: mvdir storage/ndb/src/kernel/blocks/backup/BackupInit.cpp: mvdir storage/ndb/src/kernel/blocks/backup/FsBuffer.hpp: mvdir storage/ndb/src/kernel/blocks/backup/Makefile.am: mvdir storage/ndb/src/kernel/blocks/backup/read.cpp: mvdir storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp: mvdir storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.hpp: mvdir storage/ndb/src/kernel/blocks/cmvmi/Makefile.am: mvdir storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp: mvdir storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp: mvdir storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp: mvdir storage/ndb/src/kernel/blocks/dbacc/Makefile.am: mvdir storage/ndb/src/kernel/blocks/mutexes.hpp: mvdir storage/ndb/src/kernel/blocks/new-block.tar.gz: mvdir storage/ndb/src/kernel/main.cpp: mvdir storage/ndb/src/kernel/blocks/dbdict/CreateIndex.txt: mvdir storage/ndb/src/kernel/blocks/dbdict/CreateTable.new.txt: mvdir storage/ndb/src/kernel/blocks/dbdict/CreateTable.txt: mvdir storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp: mvdir storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp: mvdir storage/ndb/src/kernel/blocks/dbdict/Dbdict.txt: mvdir storage/ndb/src/kernel/blocks/dbdict/DropTable.txt: mvdir storage/ndb/src/kernel/blocks/dbdict/Event.txt: mvdir storage/ndb/src/kernel/blocks/dbdict/Makefile.am: mvdir storage/ndb/src/kernel/blocks/dbdict/Master_AddTable.sfl: mvdir storage/ndb/src/kernel/blocks/dbdict/SchemaFile.hpp: mvdir storage/ndb/src/kernel/blocks/dbdict/Slave_AddTable.sfl: mvdir storage/ndb/src/kernel/blocks/dbdict/printSchemaFile.cpp: mvdir storage/ndb/src/kernel/blocks/dbdih/Dbdih.hpp: mvdir storage/ndb/src/kernel/blocks/dbdih/DbdihInit.cpp: mvdir storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp: mvdir storage/ndb/src/kernel/blocks/dbdih/LCP.txt: mvdir storage/ndb/src/kernel/blocks/dbdih/Makefile.am: mvdir storage/ndb/src/kernel/blocks/dbdih/Sysfile.hpp: mvdir storage/ndb/src/kernel/blocks/dbdih/printSysfile/Makefile: mvdir storage/ndb/src/kernel/blocks/dbdih/printSysfile/printSysfile.cpp: mvdir storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp: mvdir storage/ndb/src/kernel/blocks/dblqh/DblqhInit.cpp: mvdir storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp: mvdir storage/ndb/src/kernel/blocks/dblqh/Makefile.am: mvdir storage/ndb/src/kernel/blocks/dblqh/redoLogReader/Makefile: mvdir storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp: mvdir storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp: mvdir storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp: mvdir storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp: mvdir storage/ndb/src/kernel/blocks/dbtc/DbtcInit.cpp: mvdir storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp: mvdir storage/ndb/src/kernel/blocks/dbtc/Makefile.am: mvdir storage/ndb/src/kernel/blocks/dbtup/AttributeOffset.hpp: mvdir storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupBuffer.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupLCP.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupPagMan.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupStoredProcDef.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupSystemRestart.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupTabDesMan.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/DbtupUndoLog.cpp: mvdir storage/ndb/src/kernel/blocks/dbtup/Makefile.am: mvdir storage/ndb/src/kernel/blocks/dbtup/Notes.txt: mvdir storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxSearch.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp: mvdir storage/ndb/src/kernel/blocks/dbtux/Makefile.am: mvdir storage/ndb/src/kernel/blocks/dbtux/Times.txt: mvdir storage/ndb/src/kernel/blocks/dbtux/tuxstatus.html: mvdir storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp: mvdir storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp: mvdir storage/ndb/src/kernel/blocks/dbutil/DbUtil.txt: mvdir storage/ndb/src/kernel/blocks/dbutil/Makefile.am: mvdir storage/ndb/src/kernel/blocks/grep/Grep.cpp: mvdir storage/ndb/src/kernel/blocks/grep/Grep.hpp: mvdir storage/ndb/src/kernel/blocks/grep/GrepInit.cpp: mvdir storage/ndb/src/kernel/blocks/grep/Makefile.am: mvdir storage/ndb/src/kernel/blocks/grep/systab_test/Makefile: mvdir storage/ndb/src/kernel/blocks/grep/systab_test/grep_systab_test.cpp: mvdir storage/ndb/src/kernel/blocks/ndbcntr/Makefile.am: mvdir storage/ndb/src/kernel/blocks/ndbcntr/Ndbcntr.hpp: mvdir storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrInit.cpp: mvdir storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp: mvdir storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrSysTable.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/CircularIndex.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/Filename.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/Filename.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/Makefile.am: mvdir storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/AsyncFileTest.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/AsyncFileTest/Makefile: mvdir storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelOSE.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/Makefile: mvdir storage/ndb/src/kernel/blocks/ndbfs/MemoryChannelTest/MemoryChannelTest.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.cpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/Ndbfs.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/OpenFiles.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/Pool.hpp: mvdir storage/ndb/src/kernel/blocks/ndbfs/VoidFs.cpp: mvdir storage/ndb/src/kernel/blocks/qmgr/Makefile.am: mvdir storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp: mvdir storage/ndb/src/kernel/blocks/qmgr/QmgrInit.cpp: mvdir storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp: mvdir storage/ndb/src/kernel/blocks/qmgr/timer.hpp: mvdir storage/ndb/src/kernel/blocks/suma/Makefile.am: mvdir storage/ndb/src/kernel/blocks/suma/Suma.cpp: mvdir storage/ndb/src/kernel/blocks/suma/Suma.hpp: mvdir storage/ndb/src/kernel/blocks/suma/Suma.txt: mvdir storage/ndb/src/kernel/blocks/suma/SumaInit.cpp: mvdir storage/ndb/src/kernel/blocks/trix/Makefile.am: mvdir storage/ndb/src/kernel/blocks/trix/Trix.cpp: mvdir storage/ndb/src/kernel/blocks/trix/Trix.hpp: mvdir storage/ndb/src/kernel/error/Error.hpp: mvdir storage/ndb/src/kernel/error/ErrorHandlingMacros.hpp: mvdir storage/ndb/src/kernel/error/ErrorMessages.cpp: mvdir storage/ndb/src/kernel/error/ErrorMessages.hpp: mvdir storage/ndb/src/kernel/error/ErrorReporter.cpp: mvdir storage/ndb/src/kernel/error/ErrorReporter.hpp: mvdir storage/ndb/src/kernel/error/Makefile.am: mvdir storage/ndb/src/kernel/error/TimeModule.cpp: mvdir storage/ndb/src/kernel/error/TimeModule.hpp: mvdir storage/ndb/src/kernel/vm/Array.hpp: mvdir storage/ndb/src/kernel/vm/ArrayFifoList.hpp: mvdir storage/ndb/src/kernel/vm/ArrayList.hpp: mvdir storage/ndb/src/kernel/vm/ArrayPool.hpp: mvdir storage/ndb/src/kernel/vm/CArray.hpp: mvdir storage/ndb/src/kernel/vm/Callback.hpp: mvdir storage/ndb/src/kernel/vm/ClusterConfiguration.cpp: mvdir storage/ndb/src/kernel/vm/ClusterConfiguration.hpp: mvdir storage/ndb/src/kernel/vm/Configuration.cpp: mvdir storage/ndb/src/kernel/vm/Configuration.hpp: mvdir storage/ndb/src/kernel/vm/DLFifoList.hpp: mvdir storage/ndb/src/kernel/vm/DLHashTable.hpp: mvdir storage/ndb/src/kernel/vm/DLHashTable2.hpp: mvdir storage/ndb/src/kernel/vm/DLList.hpp: mvdir storage/ndb/src/kernel/vm/DataBuffer.hpp: mvdir storage/ndb/src/kernel/vm/Emulator.cpp: mvdir storage/ndb/src/kernel/vm/Emulator.hpp: mvdir storage/ndb/src/kernel/vm/FastScheduler.cpp: mvdir storage/ndb/src/kernel/vm/FastScheduler.hpp: mvdir storage/ndb/src/kernel/vm/GlobalData.hpp: mvdir storage/ndb/src/kernel/vm/KeyTable.hpp: mvdir storage/ndb/src/kernel/vm/KeyTable2.hpp: mvdir storage/ndb/src/kernel/vm/LongSignal.hpp: mvdir storage/ndb/src/kernel/vm/Makefile.am: mvdir storage/ndb/src/kernel/vm/MetaData.cpp: mvdir storage/ndb/src/kernel/vm/MetaData.hpp: mvdir storage/ndb/src/kernel/vm/Mutex.cpp: mvdir storage/ndb/src/kernel/vm/Mutex.hpp: mvdir storage/ndb/src/kernel/vm/Prio.hpp: mvdir storage/ndb/src/kernel/vm/RequestTracker.hpp: mvdir storage/ndb/src/kernel/vm/SLList.hpp: mvdir storage/ndb/src/kernel/vm/SafeCounter.cpp: mvdir storage/ndb/src/kernel/vm/SafeCounter.hpp: mvdir storage/ndb/src/kernel/vm/SectionReader.cpp: mvdir storage/ndb/src/kernel/vm/SectionReader.hpp: mvdir storage/ndb/src/kernel/vm/SignalCounter.hpp: mvdir storage/ndb/src/kernel/vm/SimBlockList.hpp: mvdir storage/ndb/src/kernel/vm/SimplePropertiesSection.cpp: mvdir storage/ndb/src/kernel/vm/SimulatedBlock.cpp: mvdir storage/ndb/src/kernel/vm/SimulatedBlock.hpp: mvdir storage/ndb/src/kernel/vm/ThreadConfig.cpp: mvdir storage/ndb/src/kernel/vm/ThreadConfig.hpp: mvdir storage/ndb/src/kernel/vm/TimeQueue.cpp: mvdir storage/ndb/src/kernel/vm/TimeQueue.hpp: mvdir storage/ndb/src/kernel/vm/TransporterCallback.cpp: mvdir storage/ndb/src/kernel/vm/VMSignal.cpp: mvdir storage/ndb/src/kernel/vm/VMSignal.hpp: mvdir storage/ndb/src/kernel/vm/WaitQueue.hpp: mvdir storage/ndb/src/kernel/vm/WatchDog.cpp: mvdir storage/ndb/src/kernel/vm/WatchDog.hpp: mvdir storage/ndb/src/kernel/vm/al_test/Makefile: mvdir storage/ndb/src/kernel/vm/al_test/arrayListTest.cpp: mvdir storage/ndb/src/kernel/vm/al_test/arrayPoolTest.cpp: mvdir storage/ndb/src/kernel/vm/al_test/main.cpp: mvdir storage/ndb/src/kernel/vm/pc.hpp: mvdir storage/ndb/src/kernel/vm/testCopy/Makefile: mvdir storage/ndb/src/kernel/vm/testCopy/rr.cpp: mvdir storage/ndb/src/kernel/vm/testCopy/testCopy.cpp: mvdir storage/ndb/src/kernel/vm/testDataBuffer/Makefile: mvdir storage/ndb/src/kernel/vm/testDataBuffer/testDataBuffer.cpp: mvdir storage/ndb/src/kernel/vm/testLongSig/Makefile: mvdir storage/ndb/src/kernel/vm/testLongSig/testLongSig.cpp: mvdir storage/ndb/src/kernel/vm/testSimplePropertiesSection/Makefile: mvdir storage/ndb/src/kernel/vm/testSimplePropertiesSection/test.cpp: mvdir storage/ndb/src/mgmapi/LocalConfig.cpp: mvdir storage/ndb/src/mgmapi/LocalConfig.hpp: mvdir storage/ndb/src/mgmapi/Makefile.am: mvdir storage/ndb/src/mgmapi/mgmapi.cpp: mvdir storage/ndb/src/mgmapi/mgmapi_configuration.cpp: mvdir storage/ndb/src/mgmapi/mgmapi_configuration.hpp: mvdir storage/ndb/src/mgmapi/mgmapi_internal.h: mvdir storage/ndb/src/mgmapi/ndb_logevent.cpp: mvdir storage/ndb/src/mgmapi/ndb_logevent.hpp: mvdir storage/ndb/src/mgmapi/test/Makefile: mvdir storage/ndb/src/mgmapi/test/keso.c: mvdir storage/ndb/src/mgmapi/test/mgmSrvApi.cpp: mvdir storage/ndb/src/mgmclient/CommandInterpreter.cpp: mvdir storage/ndb/src/mgmclient/Makefile.am: mvdir storage/ndb/src/mgmclient/main.cpp: mvdir storage/ndb/src/mgmclient/ndb_mgmclient.hpp: mvdir storage/ndb/src/mgmclient/ndb_mgmclient.h: mvdir storage/ndb/src/mgmclient/test_cpcd/Makefile: mvdir storage/ndb/src/mgmclient/test_cpcd/test_cpcd.cpp: mvdir storage/ndb/src/mgmsrv/Config.cpp: mvdir storage/ndb/src/mgmsrv/Config.hpp: mvdir storage/ndb/src/mgmsrv/ConfigInfo.cpp: mvdir storage/ndb/src/mgmsrv/ConfigInfo.hpp: mvdir storage/ndb/src/mgmsrv/InitConfigFileParser.cpp: mvdir storage/ndb/src/mgmsrv/InitConfigFileParser.hpp: mvdir storage/ndb/src/mgmsrv/Makefile.am: mvdir storage/ndb/src/mgmsrv/MgmtSrvr.cpp: mvdir storage/ndb/src/mgmsrv/MgmtSrvr.hpp: mvdir storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp: mvdir storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp: mvdir storage/ndb/src/mgmsrv/Services.cpp: mvdir storage/ndb/src/mgmsrv/Services.hpp: mvdir storage/ndb/src/mgmsrv/SignalQueue.cpp: mvdir storage/ndb/src/mgmsrv/SignalQueue.hpp: mvdir storage/ndb/src/mgmsrv/convertStrToInt.cpp: mvdir storage/ndb/src/mgmsrv/convertStrToInt.hpp: mvdir storage/ndb/src/mgmsrv/main.cpp: mvdir storage/ndb/src/mgmsrv/mkconfig/Makefile: mvdir storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp: mvdir storage/ndb/src/ndbapi/API.hpp: mvdir storage/ndb/src/ndbapi/ClusterMgr.cpp: mvdir storage/ndb/src/ndbapi/ClusterMgr.hpp: mvdir storage/ndb/src/ndbapi/DictCache.cpp: mvdir storage/ndb/src/ndbapi/DictCache.hpp: mvdir storage/ndb/src/ndbapi/Makefile.am: mvdir storage/ndb/src/ndbapi/Ndb.cpp: mvdir storage/ndb/src/ndbapi/NdbApiSignal.cpp: mvdir storage/ndb/src/ndbapi/NdbApiSignal.hpp: mvdir storage/ndb/src/ndbapi/NdbBlob.cpp: mvdir storage/ndb/src/ndbapi/NdbBlobImpl.hpp: mvdir storage/ndb/src/ndbapi/NdbDictionary.cpp: mvdir storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp: mvdir storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp: mvdir storage/ndb/src/ndbapi/NdbErrorOut.cpp: mvdir storage/ndb/src/ndbapi/NdbEventOperation.cpp: mvdir storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp: mvdir storage/ndb/src/ndbapi/NdbEventOperationImpl.hpp: mvdir storage/ndb/src/ndbapi/NdbImpl.hpp: mvdir storage/ndb/src/ndbapi/NdbIndexOperation.cpp: mvdir storage/ndb/src/ndbapi/NdbLinHash.hpp: mvdir storage/ndb/src/ndbapi/NdbOperation.cpp: mvdir storage/ndb/src/ndbapi/NdbOperationDefine.cpp: mvdir storage/ndb/src/ndbapi/NdbOperationExec.cpp: mvdir storage/ndb/src/ndbapi/NdbOperationInt.cpp: mvdir storage/ndb/src/ndbapi/NdbOperationScan.cpp: mvdir storage/ndb/src/ndbapi/NdbOperationSearch.cpp: mvdir storage/ndb/src/ndbapi/NdbPool.cpp: mvdir storage/ndb/src/ndbapi/NdbPoolImpl.cpp: mvdir storage/ndb/src/ndbapi/NdbPoolImpl.hpp: mvdir storage/ndb/src/ndbapi/NdbRecAttr.cpp: mvdir storage/ndb/src/ndbapi/NdbReceiver.cpp: mvdir storage/ndb/src/ndbapi/NdbScanFilter.cpp: mvdir storage/ndb/src/ndbapi/NdbScanOperation.cpp: mvdir storage/ndb/src/ndbapi/NdbTransaction.cpp: mvdir storage/ndb/src/ndbapi/NdbTransactionScan.cpp: mvdir storage/ndb/src/ndbapi/NdbUtil.cpp: mvdir storage/ndb/src/ndbapi/NdbUtil.hpp: mvdir storage/ndb/src/ndbapi/NdbWaiter.hpp: mvdir storage/ndb/src/ndbapi/Ndberr.cpp: mvdir storage/ndb/src/ndbapi/Ndbif.cpp: mvdir storage/ndb/src/ndbapi/Ndbinit.cpp: mvdir storage/ndb/src/ndbapi/Ndblist.cpp: mvdir storage/ndb/src/ndbapi/ObjectMap.hpp: mvdir storage/ndb/src/ndbapi/ScanOperation.txt: mvdir storage/ndb/src/ndbapi/TransporterFacade.cpp: mvdir storage/ndb/src/ndbapi/TransporterFacade.hpp: mvdir storage/ndb/src/ndbapi/ndb_cluster_connection.cpp: mvdir storage/ndb/src/ndbapi/ndb_cluster_connection_impl.hpp: mvdir storage/ndb/src/ndbapi/ndberror.c: mvdir storage/ndb/src/ndbapi/signal-sender/Makefile: mvdir storage/ndb/src/ndbapi/signal-sender/SignalSender.cpp: mvdir storage/ndb/src/ndbapi/signal-sender/SignalSender.hpp: mvdir storage/ndb/src/old_files/client/Makefile: mvdir storage/ndb/src/old_files/client/odbc/Extra.mk: mvdir storage/ndb/src/old_files/client/odbc/Makefile: mvdir storage/ndb/src/old_files/client/odbc/NdbOdbc.cpp: mvdir storage/ndb/src/old_files/client/odbc/NdbOdbc.def: mvdir storage/ndb/src/old_files/client/odbc/codegen/CodeGen.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/CodeGen.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_base.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_base.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_column.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_column.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_comp_op.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_create_index.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_create_row.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_create_table.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_data_type.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_column.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_constr.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_ddl_row.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete_index.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete_lookup.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_delete_scan.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_dml.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_dml.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_dml_column.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_dml_row.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_drop_index.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_drop_table.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_column.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_const.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_conv.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_func.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_op.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_param.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_expr_row.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_idx_column.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_insert.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_insert.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_pred.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_pred.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_pred_op.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_count.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_distinct.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_filter.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_group.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_index.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_join.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_lookup.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_project.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_range.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_repeat.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_scan.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_sort.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_query_sys.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_root.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_root.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_select.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_select.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_set_row.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_stmt.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_table.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_table.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_table_list.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update_index.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update_lookup.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Code_update_scan.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/Makefile: mvdir storage/ndb/src/old_files/client/odbc/codegen/SimpleGram.ypp: mvdir storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.cpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/SimpleParser.hpp: mvdir storage/ndb/src/old_files/client/odbc/codegen/SimpleScan.lpp: mvdir storage/ndb/src/old_files/client/odbc/common/AttrArea.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/AttrArea.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/CodeTree.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/CodeTree.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/ConnArea.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/ConnArea.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/Ctx.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/Ctx.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/DataField.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/DataField.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/DataRow.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/DataRow.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/DataType.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/DataType.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/DescArea.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/DescArea.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/DiagArea.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/DiagArea.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/Makefile: mvdir storage/ndb/src/old_files/client/odbc/common/OdbcData.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/OdbcData.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/ResultArea.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/ResultArea.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/Sqlstate.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/Sqlstate.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/StmtArea.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/StmtArea.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/StmtInfo.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/StmtInfo.hpp: mvdir storage/ndb/src/old_files/client/odbc/common/common.cpp: mvdir storage/ndb/src/old_files/client/odbc/common/common.hpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.cpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictCatalog.hpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.cpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictColumn.hpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.cpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictIndex.hpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.cpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictSchema.hpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictSys.cpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictSys.hpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictTable.cpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/DictTable.hpp: mvdir storage/ndb/src/old_files/client/odbc/dictionary/Makefile: mvdir storage/ndb/src/old_files/client/odbc/docs/class.fig: mvdir storage/ndb/src/old_files/client/odbc/docs/descfield.pl: mvdir storage/ndb/src/old_files/client/odbc/docs/diag.txt: mvdir storage/ndb/src/old_files/client/odbc/docs/getinfo.pl: mvdir storage/ndb/src/old_files/client/odbc/docs/gettypeinfo.pl: mvdir storage/ndb/src/old_files/client/odbc/docs/handleattr.pl: mvdir storage/ndb/src/old_files/client/odbc/docs/main.hpp: mvdir storage/ndb/src/old_files/client/odbc/docs/ndbodbc.html: mvdir storage/ndb/src/old_files/client/odbc/docs/select.fig: mvdir storage/ndb/src/old_files/client/odbc/docs/systables.pl: mvdir storage/ndb/src/old_files/client/odbc/docs/type.txt: mvdir storage/ndb/src/old_files/client/odbc/driver/Func.data: mvdir storage/ndb/src/old_files/client/odbc/driver/Func.pl: mvdir storage/ndb/src/old_files/client/odbc/driver/Makefile: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLAllocConnect.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLAllocEnv.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandle.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLAllocHandleStd.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLAllocStmt.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLBindCol.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLBindParam.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLBindParameter.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLBrowseConnect.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLBulkOperations.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLCancel.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLCloseCursor.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLColAttribute.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLColAttributes.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLColumnPrivileges.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLColumns.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLConnect.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLCopyDesc.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLDataSources.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLDescribeCol.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLDescribeParam.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLDisconnect.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLDriverConnect.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLDrivers.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLEndTran.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLError.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLExecDirect.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLExecute.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLExtendedFetch.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLFetch.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLFetchScroll.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLForeignKeys.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLFreeConnect.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLFreeEnv.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLFreeHandle.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLFreeStmt.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectAttr.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetConnectOption.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetCursorName.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetData.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetDescField.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetDescRec.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagField.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetDiagRec.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetEnvAttr.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetFunctions.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetInfo.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtAttr.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetStmtOption.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLGetTypeInfo.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLMoreResults.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLNativeSql.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLNumParams.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLNumResultCols.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLParamData.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLParamOptions.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLPrepare.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLPrimaryKeys.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLProcedureColumns.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLProcedures.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLPutData.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLRowCount.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectAttr.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetConnectOption.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetCursorName.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetDescField.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetDescRec.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetEnvAttr.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetParam.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetPos.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetScrollOptions.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtAttr.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSetStmtOption.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLSpecialColumns.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLStatistics.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLTablePrivileges.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLTables.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/SQLTransact.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/driver.cpp: mvdir storage/ndb/src/old_files/client/odbc/driver/driver.hpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_comp_op.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_create_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_create_table.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_delete_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_delete_lookup.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_delete_scan.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_drop_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_drop_table.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_expr_conv.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_expr_func.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_expr_op.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_insert.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_pred_op.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_query_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_query_lookup.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_query_range.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_query_scan.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_query_sys.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_update_index.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_update_lookup.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Exec_update_scan.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Executor.cpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Executor.hpp: mvdir storage/ndb/src/old_files/client/odbc/executor/Makefile: mvdir storage/ndb/src/old_files/client/odbc/handles/AttrDbc.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/AttrEnv.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/AttrRoot.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/AttrStmt.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/DescSpec.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/FuncTab.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleBase.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleBase.hpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleDbc.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleDbc.hpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleDesc.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleDesc.hpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleEnv.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleEnv.hpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleRoot.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleRoot.hpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/HandleStmt.hpp: mvdir storage/ndb/src/old_files/client/odbc/handles/InfoTab.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/Makefile: mvdir storage/ndb/src/old_files/client/odbc/handles/PoolNdb.cpp: mvdir storage/ndb/src/old_files/client/odbc/handles/PoolNdb.hpp: mvdir storage/ndb/src/old_files/client/odbc/handles/handles.hpp: mvdir storage/ndb/src/old_files/ndbbaseclient/Makefile: mvdir storage/ndb/src/old_files/ndbbaseclient/ndbbaseclient_dummy.cpp: mvdir storage/ndb/src/old_files/ndbclient/Makefile: mvdir storage/ndb/src/old_files/ndbclient/ndbclient_dummy.cpp: mvdir storage/ndb/src/old_files/newtonapi/Makefile: mvdir storage/ndb/src/old_files/newtonapi/dba_binding.cpp: mvdir storage/ndb/src/old_files/newtonapi/dba_bulkread.cpp: mvdir storage/ndb/src/old_files/newtonapi/dba_config.cpp: mvdir storage/ndb/src/old_files/newtonapi/dba_dac.cpp: mvdir storage/ndb/src/old_files/newtonapi/dba_error.cpp: mvdir storage/ndb/src/old_files/newtonapi/dba_init.cpp: mvdir storage/ndb/src/old_files/newtonapi/dba_internal.hpp: mvdir storage/ndb/src/old_files/newtonapi/dba_process.cpp: mvdir storage/ndb/src/old_files/newtonapi/dba_process.hpp: mvdir storage/ndb/src/old_files/newtonapi/dba_schema.cpp: mvdir storage/ndb/src/old_files/rep/ExtSender.cpp: mvdir storage/ndb/src/old_files/rep/ExtSender.hpp: mvdir storage/ndb/src/old_files/rep/Makefile: mvdir storage/ndb/src/old_files/rep/NodeConnectInfo.hpp: mvdir storage/ndb/src/old_files/rep/README: mvdir storage/ndb/src/old_files/rep/RepApiInterpreter.cpp: mvdir storage/ndb/src/old_files/rep/RepApiInterpreter.hpp: mvdir storage/ndb/src/old_files/rep/RepApiService.cpp: mvdir storage/ndb/src/old_files/rep/RepApiService.hpp: mvdir storage/ndb/src/old_files/rep/RepCommandInterpreter.cpp: mvdir storage/ndb/src/old_files/rep/RepCommandInterpreter.hpp: mvdir storage/ndb/src/old_files/rep/RepComponents.cpp: mvdir storage/ndb/src/old_files/rep/RepComponents.hpp: mvdir storage/ndb/src/old_files/rep/RepMain.cpp: mvdir storage/ndb/src/old_files/rep/Requestor.cpp: mvdir storage/ndb/src/old_files/rep/Requestor.hpp: mvdir storage/ndb/src/old_files/rep/RequestorSubscriptions.cpp: mvdir storage/ndb/src/old_files/rep/SignalQueue.cpp: mvdir storage/ndb/src/old_files/rep/SignalQueue.hpp: mvdir storage/ndb/src/old_files/rep/TODO: mvdir storage/ndb/src/old_files/rep/adapters/AppNDB.cpp: mvdir storage/ndb/src/old_files/rep/adapters/AppNDB.hpp: mvdir storage/ndb/src/old_files/rep/adapters/ExtAPI.cpp: mvdir storage/ndb/src/old_files/rep/adapters/ExtAPI.hpp: mvdir storage/ndb/src/old_files/rep/adapters/ExtNDB.cpp: mvdir storage/ndb/src/old_files/rep/adapters/ExtNDB.hpp: mvdir storage/ndb/src/old_files/rep/adapters/Makefile: mvdir storage/ndb/src/old_files/rep/adapters/TableInfoPs.hpp: mvdir storage/ndb/src/old_files/rep/dbug_hack.cpp: mvdir storage/ndb/src/old_files/rep/rep_version.hpp: mvdir storage/ndb/src/old_files/rep/repapi/Makefile: mvdir storage/ndb/src/old_files/rep/repapi/repapi.cpp: mvdir storage/ndb/src/old_files/rep/repapi/repapi.h: mvdir storage/ndb/src/old_files/rep/state/Channel.cpp: mvdir storage/ndb/src/old_files/rep/state/Channel.hpp: mvdir storage/ndb/src/old_files/rep/state/Interval.cpp: mvdir storage/ndb/src/old_files/rep/state/Interval.hpp: mvdir storage/ndb/src/old_files/rep/state/Makefile: mvdir storage/ndb/src/old_files/rep/state/RepState.cpp: mvdir storage/ndb/src/old_files/rep/state/RepState.hpp: mvdir storage/ndb/src/old_files/rep/state/RepStateEvent.cpp: mvdir storage/ndb/src/old_files/rep/state/RepStateRequests.cpp: mvdir storage/ndb/src/old_files/rep/state/testInterval/Makefile: mvdir storage/ndb/src/old_files/rep/state/testInterval/testInterval.cpp: mvdir storage/ndb/src/old_files/rep/state/testRepState/Makefile: mvdir storage/ndb/src/old_files/rep/state/testRepState/testRequestor.cpp: mvdir storage/ndb/src/old_files/rep/state/testRepState/testRequestor.hpp: mvdir storage/ndb/src/old_files/rep/storage/GCIBuffer.cpp: mvdir storage/ndb/src/old_files/rep/storage/GCIBuffer.hpp: mvdir storage/ndb/src/old_files/rep/storage/GCIContainer.cpp: mvdir storage/ndb/src/old_files/rep/storage/GCIContainer.hpp: mvdir storage/ndb/src/old_files/rep/storage/GCIContainerPS.cpp: mvdir storage/ndb/src/old_files/rep/storage/GCIContainerPS.hpp: mvdir storage/ndb/src/old_files/rep/storage/GCIPage.cpp: mvdir storage/ndb/src/old_files/rep/storage/GCIPage.hpp: mvdir storage/ndb/src/old_files/rep/storage/LogRecord.hpp: mvdir storage/ndb/src/old_files/rep/storage/Makefile: mvdir storage/ndb/src/old_files/rep/storage/NodeConnectInfo.hpp: mvdir storage/ndb/src/old_files/rep/storage/NodeGroup.cpp: mvdir storage/ndb/src/old_files/rep/storage/NodeGroup.hpp: mvdir storage/ndb/src/old_files/rep/storage/NodeGroupInfo.cpp: mvdir storage/ndb/src/old_files/rep/storage/NodeGroupInfo.hpp: mvdir storage/ndb/src/old_files/rep/transfer/Makefile: mvdir storage/ndb/src/old_files/rep/transfer/TransPS.cpp: mvdir storage/ndb/src/old_files/rep/transfer/TransPS.hpp: mvdir storage/ndb/src/old_files/rep/transfer/TransSS.cpp: mvdir storage/ndb/src/old_files/rep/transfer/TransSS.hpp: mvdir storage/ndb/src/old_files/rep/transfer/TransSSSubscriptions.cpp: mvdir storage/ndb/test/Makefile.am: mvdir storage/ndb/test/include/CpcClient.hpp: mvdir storage/ndb/test/include/HugoAsynchTransactions.hpp: mvdir storage/ndb/test/include/HugoCalculator.hpp: mvdir storage/ndb/test/include/HugoOperations.hpp: mvdir storage/ndb/test/include/HugoTransactions.hpp: mvdir storage/ndb/test/include/NDBT.hpp: mvdir storage/ndb/test/include/NDBT_DataSet.hpp: mvdir storage/ndb/test/include/NDBT_DataSetTransaction.hpp: mvdir storage/ndb/test/include/NDBT_Error.hpp: mvdir storage/ndb/test/include/NDBT_Output.hpp: mvdir storage/ndb/test/include/NDBT_ResultRow.hpp: mvdir storage/ndb/test/include/NDBT_ReturnCodes.h: mvdir storage/ndb/test/include/NDBT_Stats.hpp: mvdir storage/ndb/test/include/NDBT_Table.hpp: mvdir storage/ndb/test/include/NDBT_Tables.hpp: mvdir storage/ndb/test/include/NDBT_Test.hpp: mvdir storage/ndb/test/include/NdbBackup.hpp: mvdir storage/ndb/test/include/NdbConfig.hpp: mvdir storage/ndb/test/include/NdbGrep.hpp: mvdir storage/ndb/test/include/NdbRestarter.hpp: mvdir storage/ndb/test/include/NdbRestarts.hpp: mvdir storage/ndb/test/include/NdbSchemaCon.hpp: mvdir storage/ndb/test/include/NdbSchemaOp.hpp: mvdir storage/ndb/test/include/NdbTest.hpp: mvdir storage/ndb/test/include/NdbTimer.hpp: mvdir storage/ndb/test/include/TestNdbEventOperation.hpp: mvdir storage/ndb/test/include/UtilTransactions.hpp: mvdir storage/ndb/test/include/getarg.h: mvdir storage/ndb/test/ndbapi/InsertRecs.cpp: mvdir storage/ndb/test/ndbapi/Makefile.am: mvdir storage/ndb/test/ndbapi/ScanFilter.hpp: mvdir storage/ndb/test/ndbapi/ScanFunctions.hpp: mvdir storage/ndb/test/ndbapi/ScanInterpretTest.hpp: mvdir storage/ndb/test/ndbapi/TraceNdbApi.cpp: mvdir storage/ndb/test/ndbapi/VerifyNdbApi.cpp: mvdir storage/ndb/test/ndbapi/acid.cpp: mvdir storage/ndb/test/ndbapi/acid2.cpp: mvdir storage/ndb/test/ndbapi/adoInsertRecs.cpp: mvdir storage/ndb/test/ndbapi/asyncGenerator.cpp: mvdir storage/ndb/test/ndbapi/benchronja.cpp: mvdir storage/ndb/test/ndbapi/bulk_copy.cpp: mvdir storage/ndb/test/ndbapi/cdrserver.cpp: mvdir storage/ndb/test/ndbapi/celloDb.cpp: mvdir storage/ndb/test/ndbapi/create_all_tabs.cpp: mvdir storage/ndb/test/ndbapi/create_tab.cpp: mvdir storage/ndb/test/ndbapi/drop_all_tabs.cpp: mvdir storage/ndb/test/ndbapi/flexAsynch.cpp: mvdir storage/ndb/test/ndbapi/flexBench.cpp: mvdir storage/ndb/test/ndbapi/flexHammer.cpp: mvdir storage/ndb/test/ndbapi/flexScan.cpp: mvdir storage/ndb/test/ndbapi/flexTT.cpp: mvdir storage/ndb/test/ndbapi/flexTimedAsynch.cpp: mvdir storage/ndb/test/ndbapi/flex_bench_mysql.cpp: mvdir storage/ndb/test/ndbapi/index.cpp: mvdir storage/ndb/test/ndbapi/index2.cpp: mvdir storage/ndb/test/ndbapi/initronja.cpp: mvdir storage/ndb/test/ndbapi/interpreterInTup.cpp: mvdir storage/ndb/test/ndbapi/mainAsyncGenerator.cpp: mvdir storage/ndb/test/ndbapi/msa.cpp: mvdir storage/ndb/test/ndbapi/ndb_async1.cpp: mvdir storage/ndb/test/ndbapi/ndb_async2.cpp: mvdir storage/ndb/test/ndbapi/ndb_user_populate.cpp: mvdir storage/ndb/test/ndbapi/ndb_user_transaction.cpp: mvdir storage/ndb/test/ndbapi/ndb_user_transaction2.cpp: mvdir storage/ndb/test/ndbapi/ndb_user_transaction3.cpp: mvdir storage/ndb/test/ndbapi/ndb_user_transaction4.cpp: mvdir storage/ndb/test/ndbapi/ndb_user_transaction5.cpp: mvdir storage/ndb/test/ndbapi/ndb_user_transaction6.cpp: mvdir storage/ndb/test/ndbapi/restarter.cpp: mvdir storage/ndb/test/ndbapi/restarter2.cpp: mvdir storage/ndb/test/ndbapi/restarts.cpp: mvdir storage/ndb/test/ndbapi/size.cpp: mvdir storage/ndb/test/ndbapi/slow_select.cpp: mvdir storage/ndb/test/ndbapi/testBackup.cpp: mvdir storage/ndb/test/ndbapi/testBasic.cpp: mvdir storage/ndb/test/ndbapi/testBasicAsynch.cpp: mvdir storage/ndb/test/ndbapi/testBitfield.cpp: mvdir storage/ndb/test/ndbapi/testBlobs.cpp: mvdir storage/ndb/test/ndbapi/testDataBuffers.cpp: mvdir storage/ndb/test/ndbapi/testDeadlock.cpp: mvdir storage/ndb/test/ndbapi/testDict.cpp: mvdir storage/ndb/test/ndbapi/testGrep.cpp: mvdir storage/ndb/test/ndbapi/testGrepVerify.cpp: mvdir storage/ndb/test/ndbapi/testIndex.cpp: mvdir storage/ndb/test/ndbapi/testInterpreter.cpp: mvdir storage/ndb/test/ndbapi/testLcp.cpp: mvdir storage/ndb/test/ndbapi/testMgm.cpp: mvdir storage/ndb/test/ndbapi/testNdbApi.cpp: mvdir storage/ndb/test/ndbapi/testNodeRestart.cpp: mvdir storage/ndb/test/ndbapi/testOIBasic.cpp: mvdir storage/ndb/test/ndbapi/testOperations.cpp: mvdir storage/ndb/test/ndbapi/testOrderedIndex.cpp: mvdir storage/ndb/test/ndbapi/testPartitioning.cpp: mvdir storage/ndb/test/ndbapi/testReadPerf.cpp: mvdir storage/ndb/test/ndbapi/testRestartGci.cpp: mvdir storage/ndb/test/ndbapi/bank/Bank.cpp: mvdir storage/ndb/test/ndbapi/bank/Bank.hpp: mvdir storage/ndb/test/ndbapi/bank/BankLoad.cpp: mvdir storage/ndb/test/ndbapi/bank/Makefile.am: mvdir storage/ndb/test/ndbapi/bank/bankCreator.cpp: mvdir storage/ndb/test/ndbapi/bank/bankMakeGL.cpp: mvdir storage/ndb/test/ndbapi/bank/bankSumAccounts.cpp: mvdir storage/ndb/test/ndbapi/bank/bankTimer.cpp: mvdir storage/ndb/test/ndbapi/bank/bankTransactionMaker.cpp: mvdir storage/ndb/test/ndbapi/bank/bankValidateAllGLs.cpp: mvdir storage/ndb/test/ndbapi/bank/testBank.cpp: mvdir storage/ndb/test/ndbapi/bench/asyncGenerator.cpp: mvdir storage/ndb/test/ndbapi/bench/dbGenerator.h: mvdir storage/ndb/test/ndbapi/bench/dbPopulate.cpp: mvdir storage/ndb/test/ndbapi/bench/dbPopulate.h: mvdir storage/ndb/test/ndbapi/bench/macros.h: mvdir storage/ndb/test/ndbapi/bench/mainAsyncGenerator.cpp: mvdir storage/ndb/test/ndbapi/bench/mainPopulate.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_async1.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_async2.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_error.hpp: mvdir storage/ndb/test/ndbapi/bench/ndb_schema.hpp: mvdir storage/ndb/test/ndbapi/bench/ndb_user_transaction.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_user_transaction2.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_user_transaction3.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_user_transaction4.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_user_transaction5.cpp: mvdir storage/ndb/test/ndbapi/testScan.cpp: mvdir storage/ndb/test/ndbapi/testScanInterpreter.cpp: mvdir storage/ndb/test/ndbapi/testScanPerf.cpp: mvdir storage/ndb/test/ndbapi/testSystemRestart.cpp: mvdir storage/ndb/test/ndbapi/testTimeout.cpp: mvdir storage/ndb/test/ndbapi/testTransactions.cpp: mvdir storage/ndb/test/ndbapi/test_event.cpp: mvdir storage/ndb/test/ndbapi/test_event_multi_table.cpp: mvdir storage/ndb/test/ndbapi/userInterface.cpp: mvdir storage/ndb/test/ndbapi/bench/ndb_user_transaction6.cpp: mvdir storage/ndb/test/ndbapi/bench/testData.h: mvdir storage/ndb/test/ndbapi/bench/testDefinitions.h: mvdir storage/ndb/test/ndbapi/bench/userInterface.cpp: mvdir storage/ndb/test/ndbapi/bench/userInterface.h: mvdir storage/ndb/test/ndbapi/old_dirs/acid/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/acid2/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/acid2/TraceNdbApi.hpp: mvdir storage/ndb/test/ndbapi/old_dirs/acid2/VerifyNdbApi.hpp: mvdir storage/ndb/test/ndbapi/old_dirs/basicAsynch/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/bulk_copy/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/create_all_tabs/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/create_tab/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/drop_all_tabs/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/flexAsynch/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/flexBench/Makefile.am: mvdir storage/ndb/test/ndbapi/old_dirs/flexBench/ndbplot.pl: mvdir storage/ndb/test/ndbapi/old_dirs/flexHammer/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/flexHammer/README: mvdir storage/ndb/test/ndbapi/old_dirs/flexScan/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/flexScan/README: mvdir storage/ndb/test/ndbapi/old_dirs/flexTT/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/flexTimedAsynch/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/flex_bench_mysql/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/indexTest/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/indexTest2/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/interpreterInTup/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/generator/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/dbGenerator.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/testData.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/include/userInterface.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/macros.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/async-src/user/ndb_error.hpp: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/bin/.empty: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/ndb_schema.hpp: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/include/testDefinitions.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/lib/.empty: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l-p10.sh: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-l.sh: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench-p10.sh: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/script/async-lmc-bench.sh: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/README: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.c: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/dbGenerator.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/generator/mainGenerator.c: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/testData.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/include/userInterface.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.linux: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/makevars.sparc: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.c: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/dbPopulate.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/populator/mainPopulate.c: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/localDbPrepare.c: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/macros.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/ndb_error.hpp: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userHandle.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userInterface.c: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userHandle.h: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userInterface.cpp: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/userTransaction.c: mvdir storage/ndb/test/ndbapi/old_dirs/lmc-bench/src/user/old/userTransaction.c: mvdir storage/ndb/test/ndbapi/old_dirs/restarter/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/restarter2/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/restarts/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/ronja/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/ronja/benchronja/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/ronja/initronja/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/telco/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/telco/readme: mvdir storage/ndb/test/ndbapi/old_dirs/testBackup/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testBasic/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testBlobs/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testDataBuffers/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testDict/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testGrep/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testGrep/verify/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testIndex/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testInterpreter/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testMgm/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testNdbApi/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testNodeRestart/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testOIBasic/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testOIBasic/times.txt: mvdir storage/ndb/test/ndbapi/old_dirs/testOperations/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testOrderedIndex/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testRestartGci/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testScan/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testScanInterpreter/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testSystemRestart/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testTimeout/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/testTransactions/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/test_event/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/vw_test/Makefile: mvdir storage/ndb/test/ndbapi/old_dirs/vw_test/bcd.h: mvdir storage/ndb/test/ndbapi/old_dirs/vw_test/script/client_start: mvdir storage/ndb/test/ndbapi/old_dirs/vw_test/utv.h: mvdir storage/ndb/test/ndbapi/old_dirs/vw_test/vcdrfunc.h: mvdir storage/ndb/test/ndbnet/test.run: mvdir storage/ndb/test/ndbnet/testError.run: mvdir storage/ndb/test/ndbnet/testMNF.run: mvdir storage/ndb/test/ndbnet/testNR.run: mvdir storage/ndb/test/ndbnet/testNR1.run: mvdir storage/ndb/test/ndbnet/testNR4.run: mvdir storage/ndb/test/ndbnet/testSRhang.run: mvdir storage/ndb/test/ndbnet/testTR295.run: mvdir storage/ndb/test/newtonapi/basic_test/Makefile: mvdir storage/ndb/test/newtonapi/basic_test/basic/Makefile: mvdir storage/ndb/test/newtonapi/basic_test/basic/basic.cpp: mvdir storage/ndb/test/newtonapi/basic_test/bulk_read/Makefile: mvdir storage/ndb/test/newtonapi/basic_test/bulk_read/br_test.cpp: mvdir storage/ndb/test/newtonapi/basic_test/common.cpp: mvdir storage/ndb/test/newtonapi/basic_test/common.hpp: mvdir storage/ndb/test/newtonapi/basic_test/ptr_binding/Makefile: mvdir storage/ndb/test/newtonapi/basic_test/ptr_binding/ptr_binding_test.cpp: mvdir storage/ndb/test/newtonapi/basic_test/too_basic.cpp: mvdir storage/ndb/test/newtonapi/perf_test/Makefile: mvdir storage/ndb/test/newtonapi/perf_test/perf.cpp: mvdir storage/ndb/test/odbc/SQL99_test/Makefile: mvdir storage/ndb/test/odbc/SQL99_test/SQL99_test.cpp: mvdir storage/ndb/test/odbc/SQL99_test/SQL99_test.h: mvdir storage/ndb/test/odbc/client/Makefile: mvdir storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE.cpp: mvdir storage/ndb/test/odbc/client/NDBT_ALLOCHANDLE_HDBC.cpp: mvdir storage/ndb/test/odbc/client/NDBT_SQLConnect.cpp: mvdir storage/ndb/test/odbc/client/NDBT_SQLPrepare.cpp: mvdir storage/ndb/test/odbc/client/SQLAllocEnvTest.cpp: mvdir storage/ndb/test/odbc/client/SQLAllocHandleTest.cpp: mvdir storage/ndb/test/odbc/client/SQLAllocHandleTest_bf.cpp: mvdir storage/ndb/test/odbc/client/SQLBindColTest.cpp: mvdir storage/ndb/test/odbc/client/SQLBindParameterTest.cpp: mvdir storage/ndb/test/odbc/client/SQLCancelTest.cpp: mvdir storage/ndb/test/odbc/client/SQLCloseCursorTest.cpp: mvdir storage/ndb/test/odbc/client/SQLColAttributeTest.cpp: mvdir storage/ndb/test/odbc/client/SQLColAttributeTest1.cpp: mvdir storage/ndb/test/odbc/client/SQLColAttributeTest2.cpp: mvdir storage/ndb/test/odbc/client/SQLColAttributeTest3.cpp: mvdir storage/ndb/test/odbc/client/SQLConnectTest.cpp: mvdir storage/ndb/test/odbc/client/SQLCopyDescTest.cpp: mvdir storage/ndb/test/odbc/client/SQLDescribeColTest.cpp: mvdir storage/ndb/test/odbc/client/SQLDisconnectTest.cpp: mvdir storage/ndb/test/odbc/client/SQLDriverConnectTest.cpp: mvdir storage/ndb/test/odbc/client/SQLEndTranTest.cpp: mvdir storage/ndb/test/odbc/client/SQLErrorTest.cpp: mvdir storage/ndb/test/odbc/client/SQLExecDirectTest.cpp: mvdir storage/ndb/test/odbc/client/SQLExecuteTest.cpp: mvdir storage/ndb/test/odbc/client/SQLFetchScrollTest.cpp: mvdir storage/ndb/test/odbc/client/SQLFetchTest.cpp: mvdir storage/ndb/test/odbc/client/SQLFreeHandleTest.cpp: mvdir storage/ndb/test/odbc/client/SQLFreeStmtTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetConnectAttrTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetCursorNameTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetDataTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetDescFieldTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetDescRecTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetDiagFieldTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetDiagRecSimpleTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetDiagRecTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetEnvAttrTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetFunctionsTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetInfoTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetStmtAttrTest.cpp: mvdir storage/ndb/test/odbc/client/SQLGetTypeInfoTest.cpp: mvdir storage/ndb/test/odbc/client/SQLMoreResultsTest.cpp: mvdir storage/ndb/test/odbc/client/SQLNumResultColsTest.cpp: mvdir storage/ndb/test/odbc/client/SQLParamDataTest.cpp: mvdir storage/ndb/test/odbc/client/SQLPrepareTest.cpp: mvdir storage/ndb/test/odbc/client/SQLPutDataTest.cpp: mvdir storage/ndb/test/odbc/client/SQLRowCountTest.cpp: mvdir storage/ndb/test/odbc/client/SQLSetConnectAttrTest.cpp: mvdir storage/ndb/test/odbc/client/SQLSetCursorNameTest.cpp: mvdir storage/ndb/test/odbc/client/SQLSetDescFieldTest.cpp: mvdir storage/ndb/test/odbc/client/SQLSetDescRecTest.cpp: mvdir storage/ndb/test/odbc/client/SQLSetEnvAttrTest.cpp: mvdir storage/ndb/test/odbc/client/SQLSetStmtAttrTest.cpp: mvdir storage/ndb/test/odbc/client/SQLTablesTest.cpp: mvdir storage/ndb/test/odbc/client/SQLTransactTest.cpp: mvdir storage/ndb/test/odbc/client/common.hpp: mvdir storage/ndb/test/odbc/client/main.cpp: mvdir storage/ndb/test/odbc/dm-iodbc/Makefile: mvdir storage/ndb/test/odbc/dm-unixodbc/Makefile: mvdir storage/ndb/test/odbc/driver/Makefile: mvdir storage/ndb/test/odbc/driver/testOdbcDriver.cpp: mvdir storage/ndb/test/odbc/test_compiler/Makefile: mvdir storage/ndb/test/odbc/test_compiler/test_compiler.cpp: mvdir storage/ndb/test/run-test/16node-tests.txt: mvdir storage/ndb/test/run-test/Makefile.am: mvdir storage/ndb/test/run-test/README.ATRT: mvdir storage/ndb/test/run-test/README: mvdir storage/ndb/test/run-test/atrt-analyze-result.sh: mvdir storage/ndb/test/run-test/atrt-clear-result.sh: mvdir storage/ndb/test/run-test/atrt-example.tgz: mvdir storage/ndb/test/run-test/atrt-gather-result.sh: mvdir storage/ndb/test/run-test/atrt-mysql-test-run: mvdir storage/ndb/test/run-test/atrt-setup.sh: mvdir storage/ndb/test/run-test/atrt-testBackup: mvdir storage/ndb/test/run-test/basic.txt: mvdir storage/ndb/test/run-test/daily-basic-tests.txt: mvdir storage/ndb/test/run-test/daily-devel-tests.txt: mvdir storage/ndb/test/run-test/example.conf: mvdir storage/ndb/test/run-test/main.cpp: mvdir storage/ndb/test/run-test/make-config.sh: mvdir storage/ndb/test/run-test/make-html-reports.sh: mvdir storage/ndb/test/run-test/make-index.sh: mvdir storage/ndb/test/run-test/ndb-autotest.sh: mvdir storage/ndb/test/run-test/run-test.hpp: mvdir storage/ndb/test/src/CpcClient.cpp: mvdir storage/ndb/test/src/HugoAsynchTransactions.cpp: mvdir storage/ndb/test/src/HugoCalculator.cpp: mvdir storage/ndb/test/src/HugoOperations.cpp: mvdir storage/ndb/test/src/HugoTransactions.cpp: mvdir storage/ndb/test/src/Makefile.am: mvdir storage/ndb/test/src/NDBT_Error.cpp: mvdir storage/ndb/test/src/NDBT_Output.cpp: mvdir storage/ndb/test/src/NDBT_ResultRow.cpp: mvdir storage/ndb/test/src/NDBT_ReturnCodes.cpp: mvdir storage/ndb/test/src/NDBT_Table.cpp: mvdir storage/ndb/test/src/NDBT_Tables.cpp: mvdir storage/ndb/test/src/NDBT_Test.cpp: mvdir storage/ndb/test/src/NdbBackup.cpp: mvdir storage/ndb/test/src/NdbConfig.cpp: mvdir storage/ndb/test/src/NdbGrep.cpp: mvdir storage/ndb/test/src/NdbRestarter.cpp: mvdir storage/ndb/test/src/NdbRestarts.cpp: mvdir storage/ndb/test/src/NdbSchemaCon.cpp: mvdir storage/ndb/test/src/NdbSchemaOp.cpp: mvdir storage/ndb/test/src/UtilTransactions.cpp: mvdir storage/ndb/test/src/getarg.c: mvdir storage/ndb/test/tools/Makefile.am: mvdir storage/ndb/test/tools/copy_tab.cpp: mvdir storage/ndb/test/tools/cpcc.cpp: mvdir storage/ndb/test/tools/create_index.cpp: mvdir storage/ndb/test/tools/hugoCalculator.cpp: mvdir storage/ndb/test/tools/hugoFill.cpp: mvdir storage/ndb/test/tools/hugoLoad.cpp: mvdir storage/ndb/test/tools/hugoLockRecords.cpp: mvdir storage/ndb/test/tools/hugoPkDelete.cpp: mvdir storage/ndb/test/tools/hugoPkRead.cpp: mvdir storage/ndb/test/tools/hugoPkReadRecord.cpp: mvdir storage/ndb/test/tools/hugoPkUpdate.cpp: mvdir storage/ndb/test/tools/hugoScanRead.cpp: mvdir storage/ndb/test/tools/hugoScanUpdate.cpp: mvdir storage/ndb/test/tools/old_dirs/hugoCalculator/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoFill/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoLoad/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoLockRecords/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoPkDelete/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoPkRead/Makefile: mvdir storage/ndb/test/tools/restart.cpp: mvdir storage/ndb/test/tools/transproxy.cpp: mvdir storage/ndb/test/tools/verify_index.cpp: mvdir storage/ndb/test/tools/old_dirs/hugoPkReadRecord/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoPkUpdate/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoScanRead/Makefile: mvdir storage/ndb/test/tools/old_dirs/hugoScanUpdate/Makefile: mvdir storage/ndb/test/tools/old_dirs/restart/Makefile: mvdir storage/ndb/test/tools/old_dirs/transproxy/Makefile: mvdir storage/ndb/test/tools/old_dirs/verify_index/Makefile: mvdir storage/ndb/test/tools/old_dirs/waiter/waiter.cpp: mvdir storage/ndb/tools/Makefile.am: mvdir storage/ndb/tools/clean-links.sh: mvdir storage/ndb/tools/delete_all.cpp: mvdir storage/ndb/tools/desc.cpp: mvdir storage/ndb/tools/drop_index.cpp: mvdir storage/ndb/tools/drop_tab.cpp: mvdir storage/ndb/tools/listTables.cpp: mvdir storage/ndb/tools/make-errors.pl: mvdir storage/ndb/tools/make-links.sh: mvdir storage/ndb/tools/ndb_test_platform.cpp: mvdir storage/ndb/tools/ndbsql.cpp: mvdir storage/ndb/tools/old_dirs/copy_tab/Makefile: mvdir storage/ndb/tools/old_dirs/cpcc/Makefile: mvdir storage/ndb/tools/old_dirs/create_index/Makefile: mvdir storage/ndb/tools/old_dirs/delete_all/Makefile: mvdir storage/ndb/tools/old_dirs/desc/Makefile: mvdir storage/ndb/tools/old_dirs/drop_index/Makefile: mvdir storage/ndb/tools/old_dirs/drop_tab/Makefile: mvdir storage/ndb/tools/old_dirs/list_tables/Makefile: mvdir storage/ndb/tools/old_dirs/ndbnet/Makefile.PL: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/ndbnet.pl: mvdir storage/ndb/tools/old_dirs/ndbnet/ndbnetd.pl: mvdir storage/ndb/tools/old_dirs/ndbnet/ndbrun: mvdir storage/ndb/tools/rgrep: mvdir storage/ndb/tools/select_all.cpp: mvdir storage/ndb/tools/select_count.cpp: mvdir storage/ndb/tools/waiter.cpp: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Base.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Client.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Command.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Config.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Database.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Env.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Node.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeApi.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeDb.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/NodeMgmt.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/Server.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerINET.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Net/ServerUNIX.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Base.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Database.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Env.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Run/Node.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Base.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Dir.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Event.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/File.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/IO.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Lock.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Log.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/Socket.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketINET.pm: mvdir storage/ndb/tools/old_dirs/ndbnet/lib/NDB/Util/SocketUNIX.pm: mvdir storage/ndb/tools/old_dirs/ndbsql/Makefile: mvdir storage/ndb/tools/old_dirs/select_all/Makefile: mvdir storage/ndb/tools/old_dirs/select_count/Makefile: mvdir storage/ndb/tools/old_dirs/src/counterviewer/CounterViewer.java: mvdir storage/ndb/tools/restore/Restore.cpp: mvdir storage/ndb/tools/restore/Restore.hpp: mvdir storage/ndb/tools/restore/consumer.cpp: mvdir storage/ndb/tools/restore/consumer.hpp: mvdir storage/ndb/tools/restore/consumer_printer.cpp: mvdir storage/ndb/tools/restore/consumer_printer.hpp: mvdir storage/ndb/tools/restore/consumer_restore.cpp: mvdir storage/ndb/tools/restore/consumer_restore.hpp: mvdir storage/ndb/tools/restore/consumer_restorem.cpp: mvdir storage/ndb/tools/restore/restore_main.cpp: mvdir storage/bdb/LICENSE: mvdir storage/bdb/Makefile.in: mvdir storage/bdb/btree/bt_compare.c: mvdir storage/bdb/btree/bt_conv.c: mvdir storage/bdb/btree/bt_curadj.c: mvdir storage/bdb/btree/bt_cursor.c: mvdir storage/bdb/btree/bt_delete.c: mvdir storage/bdb/btree/bt_method.c: mvdir storage/bdb/btree/bt_open.c: mvdir storage/bdb/btree/bt_put.c: mvdir storage/bdb/btree/bt_rec.c: mvdir storage/bdb/btree/bt_reclaim.c: mvdir storage/bdb/btree/bt_recno.c: mvdir storage/bdb/btree/bt_rsearch.c: mvdir storage/bdb/btree/bt_search.c: mvdir storage/bdb/btree/bt_split.c: mvdir storage/bdb/btree/bt_stat.c: mvdir storage/bdb/btree/bt_upgrade.c: mvdir storage/bdb/btree/bt_verify.c: mvdir storage/bdb/btree/btree.src: mvdir storage/bdb/build_unix/.IGNORE_ME: mvdir storage/bdb/build_vxworks/BerkeleyDB.wsp: mvdir storage/bdb/build_vxworks/dbdemo/README: mvdir storage/bdb/build_win32/Berkeley_DB.dsw: mvdir storage/bdb/build_win32/app_dsp.src: mvdir storage/bdb/build_win32/build_all.dsp: mvdir storage/bdb/build_win32/db_java_xa.dsp: mvdir storage/bdb/build_win32/db_java_xaj.mak: mvdir storage/bdb/build_win32/db_lib.dsp: mvdir storage/bdb/build_win32/db_test.src: mvdir storage/bdb/build_win32/dbkill.cpp: mvdir storage/bdb/build_win32/dllmain.c: mvdir storage/bdb/build_win32/dynamic_dsp.src: mvdir storage/bdb/build_win32/java_dsp.src: mvdir storage/bdb/build_win32/libdb_tcl.def: mvdir storage/bdb/build_win32/libdbrc.src: mvdir storage/bdb/build_win32/srcfile_dsp.src: mvdir storage/bdb/build_win32/static_dsp.src: mvdir storage/bdb/build_win32/tcl_dsp.src: mvdir storage/bdb/clib/getcwd.c: mvdir storage/bdb/clib/getopt.c: mvdir storage/bdb/clib/memcmp.c: mvdir storage/bdb/clib/memmove.c: mvdir storage/bdb/clib/raise.c: mvdir storage/bdb/clib/snprintf.c: mvdir storage/bdb/clib/strcasecmp.c: mvdir storage/bdb/clib/strdup.c: mvdir storage/bdb/clib/strerror.c: mvdir storage/bdb/clib/vsnprintf.c: mvdir storage/bdb/common/db_byteorder.c: mvdir storage/bdb/common/db_err.c: mvdir storage/bdb/common/db_getlong.c: mvdir storage/bdb/common/db_idspace.c: mvdir storage/bdb/common/db_log2.c: mvdir storage/bdb/common/util_arg.c: mvdir storage/bdb/common/util_cache.c: mvdir storage/bdb/common/util_log.c: mvdir storage/bdb/common/util_sig.c: mvdir storage/bdb/cxx/cxx_db.cpp: mvdir storage/bdb/cxx/cxx_dbc.cpp: mvdir storage/bdb/cxx/cxx_dbt.cpp: mvdir storage/bdb/cxx/cxx_env.cpp: mvdir storage/bdb/cxx/cxx_except.cpp: mvdir storage/bdb/cxx/cxx_lock.cpp: mvdir storage/bdb/cxx/cxx_logc.cpp: mvdir storage/bdb/cxx/cxx_mpool.cpp: mvdir storage/bdb/cxx/cxx_txn.cpp: mvdir storage/bdb/db/crdel.src: mvdir storage/bdb/db/crdel_rec.c: mvdir storage/bdb/db/db.c: mvdir storage/bdb/db/db.src: mvdir storage/bdb/db/db_am.c: mvdir storage/bdb/db/db_cam.c: mvdir storage/bdb/db/db_conv.c: mvdir storage/bdb/db/db_dispatch.c: mvdir storage/bdb/db/db_dup.c: mvdir storage/bdb/db/db_iface.c: mvdir storage/bdb/db/db_join.c: mvdir storage/bdb/db/db_meta.c: mvdir storage/bdb/db/db_method.c: mvdir storage/bdb/db/db_open.c: mvdir storage/bdb/db/db_overflow.c: mvdir storage/bdb/db/db_pr.c: mvdir storage/bdb/db/db_rec.c: mvdir storage/bdb/db/db_reclaim.c: mvdir storage/bdb/db/db_remove.c: mvdir storage/bdb/db/db_rename.c: mvdir storage/bdb/db/db_ret.c: mvdir storage/bdb/db/db_truncate.c: mvdir storage/bdb/db/db_upg.c: mvdir storage/bdb/db/db_upg_opd.c: mvdir storage/bdb/db/db_vrfy.c: mvdir storage/bdb/db/db_vrfyutil.c: mvdir storage/bdb/db185/db185.c: mvdir storage/bdb/db185/db185_int.in: mvdir storage/bdb/db_archive/db_archive.c: mvdir storage/bdb/db_checkpoint/db_checkpoint.c: mvdir storage/bdb/db_deadlock/db_deadlock.c: mvdir storage/bdb/db_dump/db_dump.c: mvdir storage/bdb/db_dump185/db_dump185.c: mvdir storage/bdb/db_load/db_load.c: mvdir storage/bdb/db_printlog/README: mvdir storage/bdb/db_printlog/commit.awk: mvdir storage/bdb/db_printlog/count.awk: mvdir storage/bdb/db_printlog/db_printlog.c: mvdir storage/bdb/db_printlog/dbname.awk: mvdir storage/bdb/db_printlog/fileid.awk: mvdir storage/bdb/db_printlog/logstat.awk: mvdir storage/bdb/db_printlog/pgno.awk: mvdir storage/bdb/db_printlog/range.awk: mvdir storage/bdb/db_printlog/rectype.awk: mvdir storage/bdb/db_printlog/status.awk: mvdir storage/bdb/db_printlog/txn.awk: mvdir storage/bdb/db_recover/db_recover.c: mvdir storage/bdb/db_stat/db_stat.c: mvdir storage/bdb/db_upgrade/db_upgrade.c: mvdir storage/bdb/db_verify/db_verify.c: mvdir storage/bdb/dbinc/btree.h: mvdir storage/bdb/dbinc/crypto.h: mvdir storage/bdb/dbinc/cxx_common.h: mvdir storage/bdb/dbinc/cxx_except.h: mvdir storage/bdb/dbinc/cxx_int.h: mvdir storage/bdb/dbinc/db.in: mvdir storage/bdb/dbinc/db_185.in: mvdir storage/bdb/dbinc/db_am.h: mvdir storage/bdb/dbinc/db_cxx.in: mvdir storage/bdb/dbinc/db_dispatch.h: mvdir storage/bdb/dbinc/db_int.in: mvdir storage/bdb/dbinc/db_join.h: mvdir storage/bdb/dbinc/db_page.h: mvdir storage/bdb/dbinc/db_server_int.h: mvdir storage/bdb/dbinc/db_shash.h: mvdir storage/bdb/dbinc/db_swap.h: mvdir storage/bdb/dbinc/db_upgrade.h: mvdir storage/bdb/dbinc/db_verify.h: mvdir storage/bdb/dbinc/debug.h: mvdir storage/bdb/dbinc/fop.h: mvdir storage/bdb/dbinc/globals.h: mvdir storage/bdb/dbinc/hash.h: mvdir storage/bdb/dbinc/hmac.h: mvdir storage/bdb/dbinc/lock.h: mvdir storage/bdb/dbinc/log.h: mvdir storage/bdb/dbinc/mp.h: mvdir storage/bdb/dbinc/mutex.h: mvdir storage/bdb/dbinc/os.h: mvdir storage/bdb/dbinc/qam.h: mvdir storage/bdb/dbinc/queue.h: mvdir storage/bdb/dbinc/region.h: mvdir storage/bdb/dbinc/rep.h: mvdir storage/bdb/dbinc/shqueue.h: mvdir storage/bdb/dbinc/tcl_db.h: mvdir storage/bdb/dbinc/txn.h: mvdir storage/bdb/dbinc/xa.h: mvdir storage/bdb/dbm/dbm.c: mvdir storage/bdb/dbreg/dbreg.c: mvdir storage/bdb/dbreg/dbreg.src: mvdir storage/bdb/dbreg/dbreg_rec.c: mvdir storage/bdb/dbreg/dbreg_util.c: mvdir storage/bdb/dist/Makefile.in: mvdir storage/bdb/dist/RELEASE: mvdir storage/bdb/dist/buildrel: mvdir storage/bdb/dist/config.guess: mvdir storage/bdb/dist/config.sub: mvdir storage/bdb/dist/configure.ac: mvdir storage/bdb/dist/db.ecd.in: mvdir storage/bdb/dist/db.spec.in: mvdir storage/bdb/dist/gen_inc.awk: mvdir storage/bdb/dist/gen_rec.awk: mvdir storage/bdb/dist/gen_rpc.awk: mvdir storage/bdb/dist/install-sh: mvdir storage/bdb/dist/ltmain.sh: mvdir storage/bdb/dist/pubdef.in: mvdir storage/bdb/dist/s_all: mvdir storage/bdb/dist/s_config: mvdir storage/bdb/dist/aclocal/config.ac: mvdir storage/bdb/dist/aclocal/cxx.ac: mvdir storage/bdb/dist/aclocal/gcc.ac: mvdir storage/bdb/dist/aclocal/libtool.ac: mvdir storage/bdb/dist/s_crypto: mvdir storage/bdb/dist/s_dir: mvdir storage/bdb/dist/s_include: mvdir storage/bdb/dist/s_javah: mvdir storage/bdb/dist/s_java: mvdir storage/bdb/dist/s_perm: mvdir storage/bdb/dist/s_readme: mvdir storage/bdb/dist/s_recover: mvdir storage/bdb/dist/s_rpc: mvdir storage/bdb/dist/s_symlink: mvdir storage/bdb/dist/s_tags: mvdir storage/bdb/dist/s_test: mvdir storage/bdb/dist/s_vxworks: mvdir storage/bdb/dist/s_win32_dsp: mvdir storage/bdb/dist/s_win32: mvdir storage/bdb/dist/srcfiles.in: mvdir storage/bdb/dist/vx_buildcd: mvdir storage/bdb/dist/vx_config.in: mvdir storage/bdb/dist/win_config.in: mvdir storage/bdb/dist/win_exports.in: mvdir storage/bdb/dist/aclocal/mutex.ac: mvdir storage/bdb/dist/aclocal/options.ac: mvdir storage/bdb/dist/aclocal/programs.ac: mvdir storage/bdb/dist/aclocal/sosuffix.ac: mvdir storage/bdb/dist/aclocal/tcl.ac: mvdir storage/bdb/dist/aclocal/types.ac: mvdir storage/bdb/dist/aclocal_java/ac_check_class.ac: mvdir storage/bdb/dist/aclocal_java/ac_check_classpath.ac: mvdir storage/bdb/dist/aclocal_java/ac_check_junit.ac: mvdir storage/bdb/dist/aclocal_java/ac_check_rqrd_class.ac: mvdir storage/bdb/dist/aclocal_java/ac_java_options.ac: mvdir storage/bdb/dist/aclocal_java/ac_jni_include_dirs.ac: mvdir storage/bdb/dist/aclocal_java/ac_prog_jar.ac: mvdir storage/bdb/dist/aclocal_java/ac_prog_java.ac: mvdir storage/bdb/dist/aclocal_java/ac_prog_java_works.ac: mvdir storage/bdb/dist/aclocal_java/ac_prog_javac.ac: mvdir storage/bdb/dist/aclocal_java/ac_prog_javac_works.ac: mvdir storage/bdb/dist/aclocal_java/ac_prog_javadoc.ac: mvdir storage/bdb/dist/aclocal_java/ac_prog_javah.ac: mvdir storage/bdb/dist/aclocal_java/ac_try_compile_java.ac: mvdir storage/bdb/dist/aclocal_java/ac_try_run_javac.ac: mvdir storage/bdb/dist/template/rec_ctemp: mvdir storage/bdb/dist/vx_2.0/BerkeleyDB.wpj: mvdir storage/bdb/dist/vx_2.0/wpj.in: mvdir storage/bdb/dist/vx_3.1/Makefile.custom: mvdir storage/bdb/dist/vx_3.1/cdf.1: mvdir storage/bdb/dist/vx_3.1/cdf.2: mvdir storage/bdb/dist/vx_3.1/cdf.3: mvdir storage/bdb/dist/vx_3.1/component.cdf: mvdir storage/bdb/dist/vx_3.1/component.wpj: mvdir storage/bdb/dist/vx_3.1/wpj.1: mvdir storage/bdb/dist/vx_3.1/wpj.2: mvdir storage/bdb/dist/vx_3.1/wpj.3: mvdir storage/bdb/dist/vx_3.1/wpj.4: mvdir storage/bdb/dist/vx_3.1/wpj.5: mvdir storage/bdb/dist/vx_setup/CONFIG.in: mvdir storage/bdb/dist/vx_setup/LICENSE.TXT: mvdir storage/bdb/dist/vx_setup/MESSAGES.TCL: mvdir storage/bdb/dist/vx_setup/README.in: mvdir storage/bdb/dist/vx_setup/SETUP.BMP: mvdir storage/bdb/dist/vx_setup/vx_allfile.in: mvdir storage/bdb/dist/vx_setup/vx_demofile.in: mvdir storage/bdb/dist/vx_setup/vx_setup.in: mvdir storage/bdb/env/db_salloc.c: mvdir storage/bdb/env/db_shash.c: mvdir storage/bdb/env/env_file.c: mvdir storage/bdb/env/env_method.c.b: mvdir storage/bdb/env/env_method.c: mvdir storage/bdb/env/env_open.c: mvdir storage/bdb/env/env_recover.c: mvdir storage/bdb/env/env_region.c: mvdir storage/bdb/fileops/fileops.src: mvdir storage/bdb/fileops/fop_basic.c: mvdir storage/bdb/fileops/fop_rec.c: mvdir storage/bdb/fileops/fop_util.c: mvdir storage/bdb/hash/hash.c: mvdir storage/bdb/hash/hash.src: mvdir storage/bdb/hash/hash_conv.c: mvdir storage/bdb/hash/hash_dup.c: mvdir storage/bdb/hash/hash_func.c: mvdir storage/bdb/hash/hash_meta.c: mvdir storage/bdb/hash/hash_method.c: mvdir storage/bdb/hash/hash_open.c: mvdir storage/bdb/hash/hash_page.c: mvdir storage/bdb/hash/hash_rec.c: mvdir storage/bdb/hash/hash_reclaim.c: mvdir storage/bdb/hash/hash_stat.c: mvdir storage/bdb/hash/hash_upgrade.c: mvdir storage/bdb/hash/hash_verify.c: mvdir storage/bdb/hmac/hmac.c: mvdir storage/bdb/hmac/sha1.c: mvdir storage/bdb/hsearch/hsearch.c: mvdir storage/bdb/libdb_java/checkapi.prl: mvdir storage/bdb/libdb_java/com_sleepycat_db_Db.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_DbEnv.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_DbLock.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_DbLogc.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_DbLsn.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_DbTxn.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_DbUtil.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_Dbc.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_Dbt.h: mvdir storage/bdb/libdb_java/com_sleepycat_db_xa_DbXAResource.h: mvdir storage/bdb/libdb_java/java_Db.c: mvdir storage/bdb/libdb_java/java_DbEnv.c: mvdir storage/bdb/libdb_java/java_DbLock.c: mvdir storage/bdb/libdb_java/java_DbLogc.c: mvdir storage/bdb/libdb_java/java_DbLsn.c: mvdir storage/bdb/libdb_java/java_DbTxn.c: mvdir storage/bdb/libdb_java/java_DbUtil.c: mvdir storage/bdb/libdb_java/java_DbXAResource.c: mvdir storage/bdb/libdb_java/java_Dbc.c: mvdir storage/bdb/libdb_java/java_Dbt.c: mvdir storage/bdb/libdb_java/java_info.c: mvdir storage/bdb/libdb_java/java_info.h: mvdir storage/bdb/libdb_java/java_locked.c: mvdir storage/bdb/libdb_java/java_locked.h: mvdir storage/bdb/libdb_java/java_util.c: mvdir storage/bdb/libdb_java/java_util.h: mvdir storage/bdb/lock/Design: mvdir storage/bdb/lock/lock.c: mvdir storage/bdb/lock/lock_deadlock.c: mvdir storage/bdb/lock/lock_method.c: mvdir storage/bdb/lock/lock_region.c: mvdir storage/bdb/lock/lock_stat.c: mvdir storage/bdb/lock/lock_util.c: mvdir storage/bdb/log/log.c: mvdir storage/bdb/log/log_archive.c: mvdir storage/bdb/log/log_compare.c: mvdir storage/bdb/log/log_get.c: mvdir storage/bdb/log/log_method.c: mvdir storage/bdb/log/log_put.c: mvdir storage/bdb/mp/mp_alloc.c: mvdir storage/bdb/mp/mp_bh.c: mvdir storage/bdb/mp/mp_fget.c: mvdir storage/bdb/mp/mp_fopen.c: mvdir storage/bdb/mp/mp_fput.c: mvdir storage/bdb/mp/mp_fset.c: mvdir storage/bdb/mp/mp_method.c: mvdir storage/bdb/mp/mp_region.c: mvdir storage/bdb/mp/mp_register.c: mvdir storage/bdb/mp/mp_stat.c: mvdir storage/bdb/mp/mp_sync.c: mvdir storage/bdb/mp/mp_trickle.c: mvdir storage/bdb/mutex/README: mvdir storage/bdb/mutex/mut_fcntl.c: mvdir storage/bdb/mutex/mut_pthread.c: mvdir storage/bdb/mutex/mut_tas.c: mvdir storage/bdb/mutex/mut_win32.c: mvdir storage/bdb/mutex/mutex.c: mvdir storage/bdb/mutex/tm.c: mvdir storage/bdb/mutex/uts4_cc.s: mvdir storage/bdb/os/os_abs.c: mvdir storage/bdb/os/os_alloc.c: mvdir storage/bdb/os/os_clock.c: mvdir storage/bdb/os/os_config.c: mvdir storage/bdb/os/os_dir.c: mvdir storage/bdb/os/os_errno.c: mvdir storage/bdb/os/os_fid.c: mvdir storage/bdb/os/os_fsync.c: mvdir storage/bdb/os/os_handle.c: mvdir storage/bdb/os/os_id.c: mvdir storage/bdb/os/os_map.c: mvdir storage/bdb/os/os_method.c: mvdir storage/bdb/os/os_oflags.c: mvdir storage/bdb/os/os_open.c: mvdir storage/bdb/os/os_region.c: mvdir storage/bdb/os/os_rename.c: mvdir storage/bdb/os/os_root.c: mvdir storage/bdb/os/os_rpath.c: mvdir storage/bdb/os/os_rw.c: mvdir storage/bdb/os/os_seek.c: mvdir storage/bdb/os/os_sleep.c: mvdir storage/bdb/os/os_spin.c: mvdir storage/bdb/os/os_stat.c: mvdir storage/bdb/os/os_tmpdir.c: mvdir storage/bdb/os/os_unlink.c: mvdir storage/bdb/os_vxworks/os_vx_abs.c: mvdir storage/bdb/os_vxworks/os_vx_config.c: mvdir storage/bdb/os_vxworks/os_vx_map.c: mvdir storage/bdb/os_win32/os_abs.c: mvdir storage/bdb/os_win32/os_clock.c: mvdir storage/bdb/os_win32/os_config.c: mvdir storage/bdb/os_win32/os_dir.c: mvdir storage/bdb/os_win32/os_errno.c: mvdir storage/bdb/os_win32/os_fid.c: mvdir storage/bdb/os_win32/os_fsync.c: mvdir storage/bdb/os_win32/os_handle.c: mvdir storage/bdb/os_win32/os_map.c: mvdir storage/bdb/os_win32/os_open.c: mvdir storage/bdb/os_win32/os_rename.c: mvdir storage/bdb/os_win32/os_rw.c: mvdir storage/bdb/os_win32/os_seek.c: mvdir storage/bdb/os_win32/os_sleep.c: mvdir storage/bdb/os_win32/os_spin.c: mvdir storage/bdb/os_win32/os_stat.c: mvdir storage/bdb/os_win32/os_type.c: mvdir storage/bdb/perl/BerkeleyDB/BerkeleyDB.pm: mvdir storage/bdb/perl/BerkeleyDB/BerkeleyDB.pod.P: mvdir storage/bdb/perl/BerkeleyDB/BerkeleyDB.pod: mvdir storage/bdb/perl/BerkeleyDB/BerkeleyDB.xs: mvdir storage/bdb/perl/BerkeleyDB/Changes: mvdir storage/bdb/perl/BerkeleyDB/MANIFEST: mvdir storage/bdb/perl/BerkeleyDB/Makefile.PL: mvdir storage/bdb/perl/BerkeleyDB/README: mvdir storage/bdb/perl/BerkeleyDB/Todo: mvdir storage/bdb/perl/BerkeleyDB/config.in: mvdir storage/bdb/perl/BerkeleyDB/constants.h: mvdir storage/bdb/perl/BerkeleyDB/constants.xs: mvdir storage/bdb/perl/BerkeleyDB/dbinfo: mvdir storage/bdb/perl/BerkeleyDB/mkconsts: mvdir storage/bdb/perl/BerkeleyDB/mkpod: mvdir storage/bdb/perl/BerkeleyDB/BerkeleyDB/Btree.pm: mvdir storage/bdb/perl/BerkeleyDB/BerkeleyDB/Hash.pm: mvdir storage/bdb/perl/BerkeleyDB/hints/dec_osf.pl: mvdir storage/bdb/perl/BerkeleyDB/hints/irix_6_5.pl: mvdir storage/bdb/perl/BerkeleyDB/hints/solaris.pl: mvdir storage/bdb/perl/BerkeleyDB/patches/5.004_01: mvdir storage/bdb/perl/BerkeleyDB/patches/5.004_02: mvdir storage/bdb/perl/BerkeleyDB/patches/5.004_03: mvdir storage/bdb/perl/BerkeleyDB/patches/5.004_04: mvdir storage/bdb/perl/BerkeleyDB/patches/5.004_05: mvdir storage/bdb/perl/BerkeleyDB/patches/5.004: mvdir storage/bdb/perl/BerkeleyDB/patches/5.005_01: mvdir storage/bdb/perl/BerkeleyDB/patches/5.005_02: mvdir storage/bdb/perl/BerkeleyDB/patches/5.005_03: mvdir storage/bdb/perl/BerkeleyDB/patches/5.005: mvdir storage/bdb/perl/BerkeleyDB/patches/5.6.0: mvdir storage/bdb/perl/BerkeleyDB/ppport.h: mvdir storage/bdb/perl/BerkeleyDB/scan: mvdir storage/bdb/perl/BerkeleyDB/t/btree.t: mvdir storage/bdb/perl/BerkeleyDB/t/destroy.t: mvdir storage/bdb/perl/BerkeleyDB/t/env.t: mvdir storage/bdb/perl/BerkeleyDB/t/examples.t.T: mvdir storage/bdb/perl/BerkeleyDB/t/examples.t: mvdir storage/bdb/perl/BerkeleyDB/t/examples3.t.T: mvdir storage/bdb/perl/BerkeleyDB/t/examples3.t: mvdir storage/bdb/perl/BerkeleyDB/t/filter.t: mvdir storage/bdb/perl/BerkeleyDB/t/hash.t: mvdir storage/bdb/perl/BerkeleyDB/t/join.t: mvdir storage/bdb/perl/BerkeleyDB/t/mldbm.t: mvdir storage/bdb/perl/BerkeleyDB/t/queue.t: mvdir storage/bdb/perl/BerkeleyDB/t/recno.t: mvdir storage/bdb/perl/BerkeleyDB/t/strict.t: mvdir storage/bdb/perl/BerkeleyDB/t/subdb.t: mvdir storage/bdb/perl/BerkeleyDB/t/txn.t: mvdir storage/bdb/perl/BerkeleyDB/typemap: mvdir storage/bdb/perl/BerkeleyDB/t/unknown.t: mvdir storage/bdb/perl/BerkeleyDB/t/util.pm: mvdir storage/bdb/perl/DB_File/Changes: mvdir storage/bdb/perl/DB_File/DB_File.pm: mvdir storage/bdb/perl/DB_File/DB_File.xs: mvdir storage/bdb/perl/DB_File/DB_File_BS: mvdir storage/bdb/perl/DB_File/MANIFEST: mvdir storage/bdb/perl/DB_File/Makefile.PL: mvdir storage/bdb/perl/DB_File/README: mvdir storage/bdb/perl/DB_File/config.in: mvdir storage/bdb/perl/DB_File/dbinfo: mvdir storage/bdb/perl/DB_File/fallback.h: mvdir storage/bdb/perl/DB_File/fallback.xs: mvdir storage/bdb/perl/DB_File/hints/dynixptx.pl: mvdir storage/bdb/perl/DB_File/hints/sco.pl: mvdir storage/bdb/perl/DB_File/patches/5.004_01: mvdir storage/bdb/perl/DB_File/patches/5.004_02: mvdir storage/bdb/perl/DB_File/patches/5.004_03: mvdir storage/bdb/perl/DB_File/patches/5.004_04: mvdir storage/bdb/perl/DB_File/patches/5.004_05: mvdir storage/bdb/perl/DB_File/patches/5.004: mvdir storage/bdb/perl/DB_File/patches/5.005_01: mvdir storage/bdb/perl/DB_File/patches/5.005_02: mvdir storage/bdb/perl/DB_File/patches/5.005_03: mvdir storage/bdb/perl/DB_File/patches/5.005: mvdir storage/bdb/perl/DB_File/patches/5.6.0: mvdir storage/bdb/perl/DB_File/ppport.h: mvdir storage/bdb/perl/DB_File/t/db-btree.t: mvdir storage/bdb/perl/DB_File/t/db-hash.t: mvdir storage/bdb/perl/DB_File/t/db-recno.t: mvdir storage/bdb/perl/DB_File/typemap: mvdir storage/bdb/perl/DB_File/version.c: mvdir storage/bdb/qam/qam.c: mvdir storage/bdb/qam/qam.src: mvdir storage/bdb/qam/qam_conv.c: mvdir storage/bdb/qam/qam_files.c: mvdir storage/bdb/qam/qam_method.c: mvdir storage/bdb/qam/qam_open.c: mvdir storage/bdb/qam/qam_rec.c: mvdir storage/bdb/qam/qam_stat.c: mvdir storage/bdb/qam/qam_upgrade.c: mvdir storage/bdb/qam/qam_verify.c: mvdir storage/bdb/rep/rep_method.c: mvdir storage/bdb/rep/rep_record.c: mvdir storage/bdb/rep/rep_region.c: mvdir storage/bdb/rep/rep_util.c: mvdir storage/bdb/rpc_client/client.c: mvdir storage/bdb/rpc_client/gen_client_ret.c: mvdir storage/bdb/rpc_server/c/db_server_proc.c.in: mvdir storage/bdb/rpc_server/c/db_server_util.c: mvdir storage/bdb/rpc_server/clsrv.html: mvdir storage/bdb/rpc_server/cxx/db_server_cxxproc.cpp: mvdir storage/bdb/rpc_server/cxx/db_server_cxxutil.cpp: mvdir storage/bdb/rpc_server/java/DbDispatcher.java: mvdir storage/bdb/rpc_server/java/DbServer.java: mvdir storage/bdb/rpc_server/java/FreeList.java: mvdir storage/bdb/rpc_server/java/LocalIterator.java: mvdir storage/bdb/rpc_server/java/README: mvdir storage/bdb/rpc_server/java/RpcDb.java: mvdir storage/bdb/rpc_server/java/RpcDbEnv.java: mvdir storage/bdb/rpc_server/java/RpcDbTxn.java: mvdir storage/bdb/rpc_server/java/RpcDbc.java: mvdir storage/bdb/rpc_server/java/Timer.java: mvdir storage/bdb/rpc_server/java/jrpcgen.jar: mvdir storage/bdb/rpc_server/java/oncrpc.jar: mvdir storage/bdb/rpc_server/rpc.src: mvdir storage/bdb/rpc_server/java/gen/DbServerStub.java: mvdir storage/bdb/rpc_server/java/gen/__db_associate_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_associate_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_bt_maxkey_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_bt_maxkey_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_bt_minkey_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_bt_minkey_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_close_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_close_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_create_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_create_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_cursor_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_cursor_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_del_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_del_reply.java: mvdir storage/bdb/rpc_server/java/s_jrpcgen: mvdir storage/bdb/rpc_server/java/gen/__db_encrypt_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_encrypt_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_extentsize_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_extentsize_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_flags_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_flags_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_get_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_get_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_h_ffactor_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_h_ffactor_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_h_nelem_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_h_nelem_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_join_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_join_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_key_range_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_key_range_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_lorder_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_lorder_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_open_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_open_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_pagesize_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_pagesize_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_pget_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_pget_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_put_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_put_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_re_delim_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_re_delim_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_re_len_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_re_len_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_re_pad_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_re_pad_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_remove_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_remove_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_rename_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_rename_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_stat_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_stat_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_sync_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_sync_reply.java: mvdir storage/bdb/rpc_server/java/gen/__db_truncate_msg.java: mvdir storage/bdb/rpc_server/java/gen/__db_truncate_reply.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_close_msg.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_close_reply.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_count_msg.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_count_reply.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_del_msg.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_del_reply.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_dup_msg.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_dup_reply.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_get_msg.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_get_reply.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_pget_msg.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_pget_reply.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_put_msg.java: mvdir storage/bdb/rpc_server/java/gen/__dbc_put_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_cachesize_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_cachesize_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_close_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_close_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_create_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_create_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_dbremove_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_dbremove_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_dbrename_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_dbrename_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_encrypt_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_encrypt_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_flags_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_flags_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_open_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_open_reply.java: mvdir storage/bdb/rpc_server/java/gen/__env_remove_msg.java: mvdir storage/bdb/rpc_server/java/gen/__env_remove_reply.java: mvdir storage/bdb/rpc_server/java/gen/__txn_abort_msg.java: mvdir storage/bdb/rpc_server/java/gen/__txn_abort_reply.java: mvdir storage/bdb/rpc_server/java/gen/__txn_begin_msg.java: mvdir storage/bdb/rpc_server/java/gen/__txn_begin_reply.java: mvdir storage/bdb/rpc_server/java/gen/__txn_commit_msg.java: mvdir storage/bdb/rpc_server/java/gen/__txn_commit_reply.java: mvdir storage/bdb/rpc_server/java/gen/__txn_discard_msg.java: mvdir storage/bdb/rpc_server/java/gen/__txn_discard_reply.java: mvdir storage/bdb/rpc_server/java/gen/__txn_prepare_msg.java: mvdir storage/bdb/rpc_server/java/gen/__txn_prepare_reply.java: mvdir storage/bdb/rpc_server/java/gen/__txn_recover_msg.java: mvdir storage/bdb/rpc_server/java/gen/__txn_recover_reply.java: mvdir storage/bdb/rpc_server/java/gen/db_server.java: mvdir storage/bdb/tcl/tcl_compat.c: mvdir storage/bdb/tcl/tcl_db.c: mvdir storage/bdb/tcl/tcl_db_pkg.c: mvdir storage/bdb/tcl/docs/db.html: mvdir storage/bdb/tcl/docs/env.html: mvdir storage/bdb/tcl/docs/historic.html: mvdir storage/bdb/tcl/docs/index.html: mvdir storage/bdb/tcl/docs/library.html: mvdir storage/bdb/tcl/docs/lock.html: mvdir storage/bdb/tcl/docs/log.html: mvdir storage/bdb/tcl/docs/mpool.html: mvdir storage/bdb/tcl/docs/rep.html: mvdir storage/bdb/tcl/docs/test.html: mvdir storage/bdb/tcl/docs/txn.html: mvdir storage/bdb/tcl/tcl_dbcursor.c: mvdir storage/bdb/tcl/tcl_env.c: mvdir storage/bdb/tcl/tcl_internal.c: mvdir storage/bdb/tcl/tcl_lock.c: mvdir storage/bdb/tcl/tcl_log.c: mvdir storage/bdb/tcl/tcl_mp.c: mvdir storage/bdb/tcl/tcl_rep.c: mvdir storage/bdb/tcl/tcl_txn.c: mvdir storage/bdb/tcl/tcl_util.c: mvdir storage/bdb/test/archive.tcl: mvdir storage/bdb/test/bigfile001.tcl: mvdir storage/bdb/test/bigfile002.tcl: mvdir storage/bdb/test/byteorder.tcl: mvdir storage/bdb/test/conscript.tcl: mvdir storage/bdb/test/dbm.tcl: mvdir storage/bdb/test/dbscript.tcl: mvdir storage/bdb/test/ddoyscript.tcl: mvdir storage/bdb/test/ddscript.tcl: mvdir storage/bdb/test/dead001.tcl: mvdir storage/bdb/test/dead002.tcl: mvdir storage/bdb/test/dead003.tcl: mvdir storage/bdb/test/dead004.tcl: mvdir storage/bdb/test/dead005.tcl: mvdir storage/bdb/test/dead006.tcl: mvdir storage/bdb/test/dead007.tcl: mvdir storage/bdb/test/env001.tcl: mvdir storage/bdb/test/env002.tcl: mvdir storage/bdb/test/env003.tcl: mvdir storage/bdb/test/env004.tcl: mvdir storage/bdb/test/env005.tcl: mvdir storage/bdb/test/env006.tcl: mvdir storage/bdb/test/env007.tcl: mvdir storage/bdb/test/env008.tcl: mvdir storage/bdb/test/env009.tcl: mvdir storage/bdb/test/env010.tcl: mvdir storage/bdb/test/env011.tcl: mvdir storage/bdb/test/hsearch.tcl: mvdir storage/bdb/test/join.tcl: mvdir storage/bdb/test/lock001.tcl: mvdir storage/bdb/test/lock002.tcl: mvdir storage/bdb/test/lock003.tcl: mvdir storage/bdb/test/lock004.tcl: mvdir storage/bdb/test/lock005.tcl: mvdir storage/bdb/test/lockscript.tcl: mvdir storage/bdb/test/log001.tcl: mvdir storage/bdb/test/log002.tcl: mvdir storage/bdb/test/log003.tcl: mvdir storage/bdb/test/log004.tcl: mvdir storage/bdb/test/log005.tcl: mvdir storage/bdb/test/logtrack.tcl: mvdir storage/bdb/test/mdbscript.tcl: mvdir storage/bdb/test/memp001.tcl: mvdir storage/bdb/test/memp002.tcl: mvdir storage/bdb/test/memp003.tcl: mvdir storage/bdb/test/mpoolscript.tcl: mvdir storage/bdb/test/mutex001.tcl: mvdir storage/bdb/test/mutex002.tcl: mvdir storage/bdb/test/mutex003.tcl: mvdir storage/bdb/test/mutexscript.tcl: mvdir storage/bdb/test/ndbm.tcl: mvdir storage/bdb/test/parallel.tcl: mvdir storage/bdb/test/recd001.tcl: mvdir storage/bdb/test/recd002.tcl: mvdir storage/bdb/test/recd003.tcl: mvdir storage/bdb/test/recd004.tcl: mvdir storage/bdb/test/recd005.tcl: mvdir storage/bdb/test/recd006.tcl: mvdir storage/bdb/test/recd007.tcl: mvdir storage/bdb/test/recd008.tcl: mvdir storage/bdb/test/recd009.tcl: mvdir storage/bdb/test/recd010.tcl: mvdir storage/bdb/test/recd011.tcl: mvdir storage/bdb/test/recd012.tcl: mvdir storage/bdb/test/recd013.tcl: mvdir storage/bdb/test/recd014.tcl: mvdir storage/bdb/test/recd015.tcl: mvdir storage/bdb/test/recd016.tcl: mvdir storage/bdb/test/recd017.tcl: mvdir storage/bdb/test/recd018.tcl: mvdir storage/bdb/test/recd019.tcl: mvdir storage/bdb/test/recd020.tcl: mvdir storage/bdb/test/recd15scr.tcl: mvdir storage/bdb/test/recdscript.tcl: mvdir storage/bdb/test/rep001.tcl: mvdir storage/bdb/test/rep002.tcl: mvdir storage/bdb/test/rep003.tcl: mvdir storage/bdb/test/rep004.tcl: mvdir storage/bdb/test/rep005.tcl: mvdir storage/bdb/test/reputils.tcl: mvdir storage/bdb/test/rpc001.tcl: mvdir storage/bdb/test/rpc002.tcl: mvdir storage/bdb/test/rpc003.tcl: mvdir storage/bdb/test/rpc004.tcl: mvdir storage/bdb/test/rpc005.tcl: mvdir storage/bdb/test/rsrc001.tcl: mvdir storage/bdb/test/rsrc002.tcl: mvdir storage/bdb/test/rsrc003.tcl: mvdir storage/bdb/test/rsrc004.tcl: mvdir storage/bdb/test/sdb001.tcl: mvdir storage/bdb/test/sdb002.tcl: mvdir storage/bdb/test/sdb003.tcl: mvdir storage/bdb/test/sdb004.tcl: mvdir storage/bdb/test/sdb005.tcl: mvdir storage/bdb/test/sdb006.tcl: mvdir storage/bdb/test/sdb007.tcl: mvdir storage/bdb/test/sdb008.tcl: mvdir storage/bdb/test/sdb009.tcl: mvdir storage/bdb/test/sdb010.tcl: mvdir storage/bdb/test/sdb011.tcl: mvdir storage/bdb/test/sdb012.tcl: mvdir storage/bdb/test/sdbscript.tcl: mvdir storage/bdb/test/sdbtest001.tcl: mvdir storage/bdb/test/sdbtest002.tcl: mvdir storage/bdb/test/sdbutils.tcl: mvdir storage/bdb/test/sec001.tcl: mvdir storage/bdb/test/sec002.tcl: mvdir storage/bdb/test/shelltest.tcl: mvdir storage/bdb/test/si001.tcl: mvdir storage/bdb/test/si002.tcl: mvdir storage/bdb/test/si003.tcl: mvdir storage/bdb/test/si004.tcl: mvdir storage/bdb/test/si005.tcl: mvdir storage/bdb/test/si006.tcl: mvdir storage/bdb/test/sindex.tcl: mvdir storage/bdb/test/sysscript.tcl: mvdir storage/bdb/test/test.tcl: mvdir storage/bdb/test/test001.tcl: mvdir storage/bdb/test/test002.tcl: mvdir storage/bdb/test/test003.tcl: mvdir storage/bdb/test/test004.tcl: mvdir storage/bdb/test/test005.tcl: mvdir storage/bdb/test/test006.tcl: mvdir storage/bdb/test/test007.tcl: mvdir storage/bdb/test/test008.tcl: mvdir storage/bdb/test/test009.tcl: mvdir storage/bdb/test/test010.tcl: mvdir storage/bdb/test/test011.tcl: mvdir storage/bdb/test/test012.tcl: mvdir storage/bdb/test/test013.tcl: mvdir storage/bdb/test/test014.tcl: mvdir storage/bdb/test/test015.tcl: mvdir storage/bdb/test/test016.tcl: mvdir storage/bdb/test/test017.tcl: mvdir storage/bdb/test/test018.tcl: mvdir storage/bdb/test/test019.tcl: mvdir storage/bdb/test/test020.tcl: mvdir storage/bdb/test/test021.tcl: mvdir storage/bdb/test/test022.tcl: mvdir storage/bdb/test/test023.tcl: mvdir storage/bdb/test/test024.tcl: mvdir storage/bdb/test/test025.tcl: mvdir storage/bdb/test/test026.tcl: mvdir storage/bdb/test/test027.tcl: mvdir storage/bdb/test/test028.tcl: mvdir storage/bdb/test/test029.tcl: mvdir storage/bdb/test/test030.tcl: mvdir storage/bdb/test/test031.tcl: mvdir storage/bdb/test/test032.tcl: mvdir storage/bdb/test/test033.tcl: mvdir storage/bdb/test/test034.tcl: mvdir storage/bdb/test/test035.tcl: mvdir storage/bdb/test/test036.tcl: mvdir storage/bdb/test/test037.tcl: mvdir storage/bdb/test/test038.tcl: mvdir storage/bdb/test/test039.tcl: mvdir storage/bdb/test/test040.tcl: mvdir storage/bdb/test/test041.tcl: mvdir storage/bdb/test/test042.tcl: mvdir storage/bdb/test/test043.tcl: mvdir storage/bdb/test/test044.tcl: mvdir storage/bdb/test/test045.tcl: mvdir storage/bdb/test/test046.tcl: mvdir storage/bdb/test/test047.tcl: mvdir storage/bdb/test/test048.tcl: mvdir storage/bdb/test/test049.tcl: mvdir storage/bdb/test/test050.tcl: mvdir storage/bdb/test/test051.tcl: mvdir storage/bdb/test/test052.tcl: mvdir storage/bdb/test/test053.tcl: mvdir storage/bdb/test/test054.tcl: mvdir storage/bdb/test/test055.tcl: mvdir storage/bdb/test/test056.tcl: mvdir storage/bdb/test/test057.tcl: mvdir storage/bdb/test/test058.tcl: mvdir storage/bdb/test/test059.tcl: mvdir storage/bdb/test/test060.tcl: mvdir storage/bdb/test/test061.tcl: mvdir storage/bdb/test/test062.tcl: mvdir storage/bdb/test/test063.tcl: mvdir storage/bdb/test/test064.tcl: mvdir storage/bdb/test/test065.tcl: mvdir storage/bdb/test/test066.tcl: mvdir storage/bdb/test/test067.tcl: mvdir storage/bdb/test/test068.tcl: mvdir storage/bdb/test/test069.tcl: mvdir storage/bdb/test/test070.tcl: mvdir storage/bdb/test/test071.tcl: mvdir storage/bdb/test/test072.tcl: mvdir storage/bdb/test/test073.tcl: mvdir storage/bdb/test/test074.tcl: mvdir storage/bdb/test/test075.tcl: mvdir storage/bdb/test/test076.tcl: mvdir storage/bdb/test/test077.tcl: mvdir storage/bdb/test/test078.tcl: mvdir storage/bdb/test/test079.tcl: mvdir storage/bdb/test/test080.tcl: mvdir storage/bdb/test/test081.tcl: mvdir storage/bdb/test/test082.tcl: mvdir storage/bdb/test/test083.tcl: mvdir storage/bdb/test/test084.tcl: mvdir storage/bdb/test/test085.tcl: mvdir storage/bdb/test/test086.tcl: mvdir storage/bdb/test/test087.tcl: mvdir storage/bdb/test/test088.tcl: mvdir storage/bdb/test/test089.tcl: mvdir storage/bdb/test/test090.tcl: mvdir storage/bdb/test/test091.tcl: mvdir storage/bdb/test/test092.tcl: mvdir storage/bdb/test/test093.tcl: mvdir storage/bdb/test/test094.tcl: mvdir storage/bdb/test/test095.tcl: mvdir storage/bdb/test/test096.tcl: mvdir storage/bdb/test/test097.tcl: mvdir storage/bdb/test/test098.tcl: mvdir storage/bdb/test/test099.tcl: mvdir storage/bdb/test/test100.tcl: mvdir storage/bdb/test/test101.tcl: mvdir storage/bdb/test/testparams.tcl: mvdir storage/bdb/test/testutils.tcl: mvdir storage/bdb/test/txn001.tcl: mvdir storage/bdb/test/txn002.tcl: mvdir storage/bdb/test/txn003.tcl: mvdir storage/bdb/test/txn004.tcl: mvdir storage/bdb/test/txn005.tcl: mvdir storage/bdb/test/txn006.tcl: mvdir storage/bdb/test/txn007.tcl: mvdir storage/bdb/test/txn008.tcl: mvdir storage/bdb/test/txn009.tcl: mvdir storage/bdb/test/txnscript.tcl: mvdir storage/bdb/test/update.tcl: mvdir storage/bdb/test/scr001/chk.code: mvdir storage/bdb/test/scr002/chk.def: mvdir storage/bdb/test/scr003/chk.define: mvdir storage/bdb/test/scr004/chk.javafiles: mvdir storage/bdb/test/scr005/chk.nl: mvdir storage/bdb/test/scr006/chk.offt: mvdir storage/bdb/test/scr007/chk.proto: mvdir storage/bdb/test/scr008/chk.pubdef: mvdir storage/bdb/test/scr009/chk.srcfiles: mvdir storage/bdb/test/scr010/chk.str: mvdir storage/bdb/test/scr010/spell.ok: mvdir storage/bdb/test/scr011/chk.tags: mvdir storage/bdb/test/scr012/chk.vx_code: mvdir storage/bdb/test/scr013/chk.stats: mvdir storage/bdb/test/scr014/chk.err: mvdir storage/bdb/test/scr015/README: mvdir storage/bdb/test/scr015/TestConstruct01.cpp: mvdir storage/bdb/test/scr015/TestConstruct01.testerr: mvdir storage/bdb/test/scr015/TestConstruct01.testout: mvdir storage/bdb/test/scr015/TestExceptInclude.cpp: mvdir storage/bdb/test/scr015/TestGetSetMethods.cpp: mvdir storage/bdb/test/scr015/TestKeyRange.cpp: mvdir storage/bdb/test/scr015/TestKeyRange.testin: mvdir storage/bdb/test/scr015/TestKeyRange.testout: mvdir storage/bdb/test/upgrade.tcl: mvdir storage/bdb/test/wordlist: mvdir storage/bdb/test/wrap.tcl: mvdir storage/bdb/test/scr015/TestLogc.cpp: mvdir storage/bdb/test/scr015/TestLogc.testout: mvdir storage/bdb/test/scr015/TestSimpleAccess.cpp: mvdir storage/bdb/test/scr015/TestSimpleAccess.testout: mvdir storage/bdb/test/scr015/TestTruncate.cpp: mvdir storage/bdb/test/scr015/TestTruncate.testout: mvdir storage/bdb/test/scr015/chk.cxxtests: mvdir storage/bdb/test/scr015/ignore: mvdir storage/bdb/test/scr015/testall: mvdir storage/bdb/test/scr015/testone: mvdir storage/bdb/test/scr016/CallbackTest.java: mvdir storage/bdb/test/scr016/CallbackTest.testout: mvdir storage/bdb/test/scr016/README: mvdir storage/bdb/test/scr016/TestAppendRecno.java: mvdir storage/bdb/test/scr016/TestAppendRecno.testout: mvdir storage/bdb/test/scr016/TestAssociate.java: mvdir storage/bdb/test/scr016/TestAssociate.testout: mvdir storage/bdb/test/scr016/TestClosedDb.java: mvdir storage/bdb/test/scr016/TestClosedDb.testout: mvdir storage/bdb/test/scr016/TestConstruct01.java: mvdir storage/bdb/test/scr016/TestConstruct01.testerr: mvdir storage/bdb/test/scr016/TestConstruct01.testout: mvdir storage/bdb/test/scr016/TestConstruct02.java: mvdir storage/bdb/test/scr016/TestConstruct02.testout: mvdir storage/bdb/test/scr016/TestDbtFlags.java: mvdir storage/bdb/test/scr016/TestDbtFlags.testerr: mvdir storage/bdb/test/scr016/TestDbtFlags.testout: mvdir storage/bdb/test/scr016/TestGetSetMethods.java: mvdir storage/bdb/test/scr016/TestKeyRange.java: mvdir storage/bdb/test/scr016/TestKeyRange.testout: mvdir storage/bdb/test/scr016/TestLockVec.java: mvdir storage/bdb/test/scr016/TestLockVec.testout: mvdir storage/bdb/test/scr016/TestLogc.java: mvdir storage/bdb/test/scr016/TestLogc.testout: mvdir storage/bdb/test/scr016/TestOpenEmpty.java: mvdir storage/bdb/test/scr016/TestOpenEmpty.testerr: mvdir storage/bdb/test/scr016/TestReplication.java: mvdir storage/bdb/test/scr016/TestRpcServer.java: mvdir storage/bdb/test/scr016/TestSameDbt.java: mvdir storage/bdb/test/scr016/TestSameDbt.testout: mvdir storage/bdb/test/scr016/TestSimpleAccess.java: mvdir storage/bdb/test/scr016/TestSimpleAccess.testout: mvdir storage/bdb/test/scr016/TestStat.java: mvdir storage/bdb/test/scr016/TestStat.testout: mvdir storage/bdb/test/scr016/TestTruncate.java: mvdir storage/bdb/test/scr016/TestTruncate.testout: mvdir storage/bdb/test/scr016/TestUtil.java: mvdir storage/bdb/test/scr016/TestXAServlet.java: mvdir storage/bdb/test/scr016/chk.javatests: mvdir storage/bdb/test/scr016/ignore: mvdir storage/bdb/test/scr016/testall: mvdir storage/bdb/test/scr016/testone: mvdir storage/bdb/test/scr017/O.BH: mvdir storage/bdb/test/scr017/O.R: mvdir storage/bdb/test/scr017/chk.db185: mvdir storage/bdb/test/scr017/t.c: mvdir storage/bdb/test/scr018/chk.comma: mvdir storage/bdb/test/scr018/t.c: mvdir storage/bdb/test/scr019/chk.include: mvdir storage/bdb/test/scr020/chk.inc: mvdir storage/bdb/test/scr021/chk.flags: mvdir storage/bdb/test/scr022/chk.rr: mvdir storage/bdb/txn/txn.c: mvdir storage/bdb/txn/txn.src: mvdir storage/bdb/txn/txn_method.c: mvdir storage/bdb/txn/txn_rec.c: mvdir storage/bdb/txn/txn_recover.c: mvdir storage/bdb/txn/txn_region.c: mvdir storage/bdb/txn/txn_stat.c: mvdir storage/bdb/txn/txn_util.c: mvdir storage/bdb/xa/xa.c: mvdir storage/bdb/xa/xa_db.c: mvdir storage/bdb/xa/xa_map.c: mvdir
Diffstat (limited to 'storage/myisam')
-rw-r--r--storage/myisam/.cvsignore14
-rw-r--r--storage/myisam/ChangeLog150
-rw-r--r--storage/myisam/Makefile.am108
-rw-r--r--storage/myisam/NEWS66
-rw-r--r--storage/myisam/TODO7
-rw-r--r--storage/myisam/ft_boolean_search.c735
-rw-r--r--storage/myisam/ft_eval.c253
-rw-r--r--storage/myisam/ft_eval.h42
-rw-r--r--storage/myisam/ft_nlq_search.c360
-rw-r--r--storage/myisam/ft_parser.c249
-rw-r--r--storage/myisam/ft_static.c628
-rw-r--r--storage/myisam/ft_stem.c19
-rw-r--r--storage/myisam/ft_stopwords.c129
-rw-r--r--storage/myisam/ft_test1.c316
-rw-r--r--storage/myisam/ft_test1.h421
-rw-r--r--storage/myisam/ft_update.c350
-rwxr-xr-xstorage/myisam/ftbench/Ecompare.pl96
-rwxr-xr-xstorage/myisam/ftbench/Ecreate.pl44
-rwxr-xr-xstorage/myisam/ftbench/Ereport.pl49
-rw-r--r--storage/myisam/ftbench/README43
-rwxr-xr-xstorage/myisam/ftbench/ft-test-run.sh98
-rw-r--r--storage/myisam/ftdefs.h152
-rw-r--r--storage/myisam/fulltext.h38
-rwxr-xr-xstorage/myisam/make-ccc5
-rw-r--r--storage/myisam/mi_cache.c108
-rw-r--r--storage/myisam/mi_changed.c34
-rw-r--r--storage/myisam/mi_check.c4082
-rw-r--r--storage/myisam/mi_checksum.c65
-rw-r--r--storage/myisam/mi_close.c118
-rw-r--r--storage/myisam/mi_create.c792
-rw-r--r--storage/myisam/mi_dbug.c193
-rw-r--r--storage/myisam/mi_delete.c887
-rw-r--r--storage/myisam/mi_delete_all.c72
-rw-r--r--storage/myisam/mi_delete_table.c58
-rw-r--r--storage/myisam/mi_dynrec.c1641
-rw-r--r--storage/myisam/mi_extra.c405
-rw-r--r--storage/myisam/mi_info.c140
-rw-r--r--storage/myisam/mi_key.c574
-rw-r--r--storage/myisam/mi_keycache.c162
-rw-r--r--storage/myisam/mi_locking.c510
-rw-r--r--storage/myisam/mi_log.c164
-rw-r--r--storage/myisam/mi_open.c1270
-rw-r--r--storage/myisam/mi_packrec.c1358
-rw-r--r--storage/myisam/mi_page.c160
-rw-r--r--storage/myisam/mi_panic.c114
-rw-r--r--storage/myisam/mi_preload.c118
-rw-r--r--storage/myisam/mi_range.c244
-rw-r--r--storage/myisam/mi_rename.c61
-rw-r--r--storage/myisam/mi_rfirst.c27
-rw-r--r--storage/myisam/mi_rkey.c141
-rw-r--r--storage/myisam/mi_rlast.c27
-rw-r--r--storage/myisam/mi_rnext.c122
-rw-r--r--storage/myisam/mi_rnext_same.c105
-rw-r--r--storage/myisam/mi_rprev.c88
-rw-r--r--storage/myisam/mi_rrnd.c60
-rw-r--r--storage/myisam/mi_rsame.c66
-rw-r--r--storage/myisam/mi_rsamepos.c56
-rw-r--r--storage/myisam/mi_scan.c46
-rw-r--r--storage/myisam/mi_search.c1829
-rw-r--r--storage/myisam/mi_static.c63
-rw-r--r--storage/myisam/mi_statrec.c301
-rw-r--r--storage/myisam/mi_test1.c674
-rw-r--r--storage/myisam/mi_test2.c1048
-rw-r--r--storage/myisam/mi_test3.c502
-rw-r--r--storage/myisam/mi_test_all.res53
-rwxr-xr-xstorage/myisam/mi_test_all.sh147
-rw-r--r--storage/myisam/mi_unique.c217
-rw-r--r--storage/myisam/mi_update.c228
-rw-r--r--storage/myisam/mi_write.c1003
-rw-r--r--storage/myisam/myisam_ftdump.c277
-rw-r--r--storage/myisam/myisamchk.c1771
-rw-r--r--storage/myisam/myisamdef.h736
-rw-r--r--storage/myisam/myisamlog.c845
-rw-r--r--storage/myisam/myisampack.c2180
-rw-r--r--storage/myisam/rt_index.c1082
-rw-r--r--storage/myisam/rt_index.h47
-rw-r--r--storage/myisam/rt_key.c100
-rw-r--r--storage/myisam/rt_key.h33
-rw-r--r--storage/myisam/rt_mbr.c801
-rw-r--r--storage/myisam/rt_mbr.h38
-rw-r--r--storage/myisam/rt_split.c352
-rw-r--r--storage/myisam/rt_test.c471
-rw-r--r--storage/myisam/sort.c1017
-rw-r--r--storage/myisam/sp_defs.h48
-rw-r--r--storage/myisam/sp_key.c296
-rw-r--r--storage/myisam/sp_test.c565
-rwxr-xr-xstorage/myisam/test_pack9
87 files changed, 35173 insertions, 0 deletions
diff --git a/storage/myisam/.cvsignore b/storage/myisam/.cvsignore
new file mode 100644
index 00000000000..ef6d92c6e18
--- /dev/null
+++ b/storage/myisam/.cvsignore
@@ -0,0 +1,14 @@
+.deps
+.libs
+Makefile
+Makefile.in
+ft_eval
+ft_test1
+mi_test1
+mi_test2
+mi_test3
+rt_test
+sp_test
+myisamchk
+myisamlog
+myisampack
diff --git a/storage/myisam/ChangeLog b/storage/myisam/ChangeLog
new file mode 100644
index 00000000000..504202be43f
--- /dev/null
+++ b/storage/myisam/ChangeLog
@@ -0,0 +1,150 @@
+2000-11-27 Michael Widenius <monty@mysql.com>
+
+* Changed mi_create.c to use less stack.
+
+2000-08-23 Michael Widenius <monty@mysql.com>
+
+* Fixed bug when comparing DECIMAL/NUMERIC key parts.
+
+2000-08-17 Michael Widenius <monty@mysql.com>
+
+* Add a new flag in share.staus so that we can quickly check if a table
+ is analyzed or not!
+
+2000-07-02 Michael Widenius <monty@mysql.com>
+
+* Added safety margin to guard against full index file.
+
+2000-05-22 Michael Widenius <monty@mysql.com>
+
+* Fixed that --join works with myisampack.
+
+2000-05-14 Michael Widenius <monty@mysql.com>
+
+* Don't lock datafile during myisamchk (only indexfile is locked; This is good
+ enough for all MyISAM functions); This made it possible to close datafile
+ in rep_by_sort().
+
+2000-05-04 Michael Widenius <monty@mysql.com>
+
+* Fixed bug in code that scanned after rows in a crashed table.
+ This could cause an infinite loop when repairing tables.
+
+2000-04-26 Michael Widenius <monty@mysql.com>
+
+* Fixed bug when doing read_next after a delete/insert which balanced key
+ pages (In this case one internal buffer was wrongly reused)
+
+2000-04-21 Michael Widenius <monty@tik.pp.sci.fi>
+
+* Changed mi_find_halfpos() to return key, key_length and pos after key.
+* Don't join or split key buffers in the middle when inserting a key
+ that is bigger than all other keys; This will improve inserts when
+ doing these in sorted order.
+
+2000-04-04 Michael Widenius <monty@mysql.com>
+
+* Added support for different languages on key part level.
+
+2000-02-23 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Fixed that myisamchk works properly with RAID.
+
+2000-02-07 Michael Widenius <monty@tik.pp.sci.fi>
+
+* Added delete and rename of tables (works with RAID tables)
+
+2000-01-29 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Fixed the sorting of index works with prefix-packed keys.
+
+1999-11-24 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Fixed that DECIMAL() keys are sorted correct for negative numbers.
+
+1999-11-22 Michael Widenius <monty@monty.pp.sci.fi>
+
+* removed 'NO_LOCKING' macros.
+* Added function mi_rnext_same
+* Added support for concurrent reads.
+
+1999-11-05 Michael Widenius <monty@tik.pp.sci.fi>
+
+* Added function mi_scan().
+* Changed all functions to return error number in case of errors.
+
+1999-08-17 Michael Widenius <monty@tik.pp.sci.fi>
+
+* Added option DELAY_KEY_WRITE to tables and mi_open()
+
+1999-08-10 Michael Widenius <monty@tik.pp.sci.fi>
+
+* Added support of HA_READ_PREFIX_LAST to mi_rkey(). This finds the last
+ row with the given prefix.
+
+Mon Aug 2 13:54:35 1999 Michael Widenius <monty@bitch.pp.sci.fi>
+
+* Added data- and key-file-length to myisamchk.
+* Fixed some problems with null and space packed keys.
+
+1999-07-15 Michael Widenius <monty@tik.pp.sci.fi>
+
+* The following options are for COUNT(DISTINCT ..)
+* Added option HA_EXTRA_NO_ROWS; In this case only the index tree is updated
+* Added mi_delete_all_rows()
+
+1999-07-13 Michael Widenius <monty@tik.pp.sci.fi>
+
+* Added special handling of tempoary tables
+
+1999-06-12 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Added optional checksum for file and for each dynamic-length row
+* Added unique constraint checking
+
+1999-05-06 Michael Widenius <monty@tik.pp.sci.fi>
+
+* All index blocks of the same size now share the same key block delete link
+
+1999-03-17 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Different key packing code depending on if the first key part
+ is a variable length column (space packed, BLOB or VARCHAR)
+* The create interface allows one to specify a key segment to start and
+ end one a specific bit. (The bit handling isn't yet implemented)
+* Added more tests to 'test1'
+
+1999-03-16 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Added option -m to myisamchk as an alternative to -e (-m is faster but
+ not as quite as safe as -e).
+* Added option --fast to not check not changed tables.
+* The first update will set a bit that the table has been changed.
+* The first update to a table increments a 'open_count' field. This will
+ be reset on close. This will allow myisamchk to find tables that hasn't
+ been properly closed!
+* Support for true VARCHAR columns
+
+1999-03-01 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Dynamic length blocks are double linked to allow easy reallocation
+ of block lengths. This will help that the dynamic length data will not be
+ as fragmented as with ISAM.
+* Extended mypack_isam to compress BLOB/TEXT columns.
+* Allow keys on BLOB.
+
+1999-02-06 Michael Widenius <monty@monty.pp.sci.fi>
+
+* Keys, key pointers and all varibles in the index file are stored in
+ high-endian-order to get better compression.
+* Allow NULL on keys
+
+1998-10-29 Michael Widenius <monty@monty.pp.sci.fi>
+
+* All data is stored in low-endian order
+ (This means that the files will be architecture and OS independent)
+* All record numbers are now of type 'ha_rows' and file pointer are now of type
+ my_off_t. One can use files with 32-64 bit pointers with a 32bit or 64bit
+ record handling.
+ Currently the code is limited to 5 bytes pointers, but this is real easy
+ to change.
diff --git a/storage/myisam/Makefile.am b/storage/myisam/Makefile.am
new file mode 100644
index 00000000000..e77e46cb7a3
--- /dev/null
+++ b/storage/myisam/Makefile.am
@@ -0,0 +1,108 @@
+# 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
+
+EXTRA_DIST = mi_test_all.sh mi_test_all.res
+pkgdata_DATA = mi_test_all mi_test_all.res
+
+INCLUDES = -I$(top_srcdir)/include
+LDADD = @CLIENT_EXTRA_LDFLAGS@ libmyisam.a \
+ $(top_builddir)/mysys/libmysys.a \
+ $(top_builddir)/dbug/libdbug.a \
+ $(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@
+pkglib_LIBRARIES = libmyisam.a
+bin_PROGRAMS = myisamchk myisamlog myisampack myisam_ftdump
+myisamchk_DEPENDENCIES= $(LIBRARIES)
+myisamlog_DEPENDENCIES= $(LIBRARIES)
+myisampack_DEPENDENCIES=$(LIBRARIES)
+noinst_PROGRAMS = mi_test1 mi_test2 mi_test3 rt_test sp_test #ft_test1 ft_eval
+noinst_HEADERS = myisamdef.h rt_index.h rt_key.h rt_mbr.h sp_defs.h fulltext.h ftdefs.h ft_test1.h ft_eval.h
+mi_test1_DEPENDENCIES= $(LIBRARIES)
+mi_test2_DEPENDENCIES= $(LIBRARIES)
+mi_test3_DEPENDENCIES= $(LIBRARIES)
+#ft_test1_DEPENDENCIES= $(LIBRARIES)
+#ft_eval_DEPENDENCIES= $(LIBRARIES)
+myisam_ftdump_DEPENDENCIES= $(LIBRARIES)
+rt_test_DEPENDENCIES= $(LIBRARIES)
+sp_test_DEPENDENCIES= $(LIBRARIES)
+libmyisam_a_SOURCES = mi_open.c mi_extra.c mi_info.c mi_rkey.c \
+ mi_rnext.c mi_rnext_same.c \
+ mi_search.c mi_page.c mi_key.c mi_locking.c \
+ mi_rrnd.c mi_scan.c mi_cache.c \
+ mi_statrec.c mi_packrec.c mi_dynrec.c \
+ mi_update.c mi_write.c mi_unique.c \
+ mi_delete.c \
+ mi_rprev.c mi_rfirst.c mi_rlast.c mi_rsame.c \
+ mi_rsamepos.c mi_panic.c mi_close.c mi_create.c\
+ mi_range.c mi_dbug.c mi_checksum.c mi_log.c \
+ mi_changed.c mi_static.c mi_delete_all.c \
+ mi_delete_table.c mi_rename.c mi_check.c \
+ mi_keycache.c mi_preload.c \
+ ft_parser.c ft_stopwords.c ft_static.c \
+ ft_update.c ft_boolean_search.c ft_nlq_search.c sort.c \
+ rt_index.c rt_key.c rt_mbr.c rt_split.c sp_key.c
+CLEANFILES = test?.MY? FT?.MY? isam.log mi_test_all rt_test.MY? sp_test.MY?
+DEFS = -DMAP_TO_USE_RAID
+
+# Move to automake rules ?
+prolint:; plparse -b -u -hF1 "-width(0,0)" "-format=%f:%l:\s%t:%n\s%m" \
+ "-elib(????)" "+elib(?3??)" my.lnt $(nisam_SOURCES)
+
+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!@''IS_LINUX''@!@IS_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.%
diff --git a/storage/myisam/NEWS b/storage/myisam/NEWS
new file mode 100644
index 00000000000..bb1f141610b
--- /dev/null
+++ b/storage/myisam/NEWS
@@ -0,0 +1,66 @@
+New features compared to NISAM:
+
+- All file positions have type my_off_t; This enables one to use big
+ files (2^63 byte) by defining my_off_t to be longlong on OS that supports
+ big files.
+- When creating a table, one can now specify the maximum data file length.
+ This will be used to calculate the length of row pointers.
+- All key segments have their own language definition.
+- Some changes to support more types:
+ The biggest change is that the interface allows MY_ISAM will support
+ variable length integer types. (Only the interface is implemented)
+- All data is stored with low byte first; This makes the data machine
+ independent.
+- All number keys are stored with high byte first to give better packing.
+- Support for a true VARCHAR type; A VARCHAR column starts with a length
+ stored on 2 bytes.
+- Tables with VARCHAR may have fixed or dynamic record length.
+- There are now 2 different ways to pack keys:
+ - If the first key part is a space stripped CHAR, a VARCHAR or a BLOB the
+ 'packed' method is used. This only prefix-compresses the first
+ key part.
+ - In other cases prefix packing is used (This also includes the record
+ pointer into the prefix packing). A key may in the best case be
+ packed on 2 bytes.
+- VARCHAR and CHAR may be up to 65K
+- Index on BLOB and VARCHAR.
+- One can now have NULL in an index. This takes 0-1 bytes / key.
+- MYISAM will allow one to specify one AUTO_INCREMENT column; MYISAM will
+ automaticly update this on INSERT/UPDATE. The AUTO_INCREMENT value can be
+ reset with myisamchk.
+- Max key length will be 500 by default; In cases of longer keys than 250,
+ a bigger key block size than the default of 1024 byes is used for this key.
+- Max number of keys enlarged to 32 as default. This can be enlarged to 64
+ without having to recompile myisamchk.
+- There is a flag in the MYISAM header that tells if the index file (.MYI)
+ was closed correctly.
+- myisamchk will now mark tables as checked. 'myisamchk --fast' will only
+ check those tables that doesn't have this mark.
+- 'myisamchk -a' stores statistic for key parts (and not only for whole keys
+ as in NISAM).
+- Dynamic size rows will now be much less fragmented when mixing deletes with
+ update and insert. This is done by automaticly combine adjacent deleted
+ blocks and by extending blocks if the next block is deleted.
+- For dynamic size rows, the delete link contains a pointer to itself
+ (to make repairs easier).
+- myisampack (called pack_isam in NISAM) can pack BLOB and VARCHAR
+ columns.
+- One can now disable any key from update; In NISAM one could only disable
+ the last x keys.
+- One can have a UNIQUE constraint on anything (including BLOBS).
+ This is implemented by a key that contains a hashed number of the whole
+ record and before inserting a new record, MyISAM will check all records
+ with the same hash for dupplicates.
+- When creating the table, one can define that MyISAM should maintain
+ a CRC for the whole table (to make isamchk even better). In the case of
+ dynamic size rows, one will in this case get 1 byte checksum for each row.
+ (This is a great help for debugging, but it can also be used to keep
+ MyISAM table 'extra' safe.
+- Temporary tables will not write not flushed keys to file on close and
+ not wait on 'disk full' conditions.
+
+Interface changes compared to NISAM:
+
+- mi_create()
+ - keyinfo->seg must be allocated explicitely.
+ - One must put number of key segments in keyinfo
diff --git a/storage/myisam/TODO b/storage/myisam/TODO
new file mode 100644
index 00000000000..cad9486e1bb
--- /dev/null
+++ b/storage/myisam/TODO
@@ -0,0 +1,7 @@
+TODO:
+
+- Let packisam find the optimal way to store keys.
+- kill when using 'myisamchk' should remove all temporary files.
+- Text search index
+ (Sergei A. Golub is working on this)
+- Add '%' packed to myisamchk for compressed tables with blobs.
diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c
new file mode 100644
index 00000000000..530f0d56c4c
--- /dev/null
+++ b/storage/myisam/ft_boolean_search.c
@@ -0,0 +1,735 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+/* TODO: add caching - pre-read several index entries at once */
+
+#define FT_CORE
+#include "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];
+ float weight;
+ float cur_weight;
+ LIST *phrase; /* phrase words */
+ 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;
+ MI_KEYDEF *keyinfo;
+ float weight;
+ uint ndepth;
+ uint len;
+ uchar off;
+ byte word[1];
+} FTB_WORD;
+
+typedef struct st_ft_info
+{
+ struct _ft_vft *please;
+ MI_INFO *info;
+ CHARSET_INFO *charset;
+ FTB_EXPR *root;
+ FTB_WORD **list;
+ 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= mi_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;
+}
+
+static void _ftb_parse_query(FTB *ftb, byte **start, byte *end,
+ FTB_EXPR *up, uint depth, byte *up_quot)
+{
+ byte res;
+ FTB_PARAM param;
+ FT_WORD w;
+ FTB_WORD *ftbw;
+ FTB_EXPR *ftbe;
+ FT_WORD *phrase_word;
+ LIST *phrase_list;
+ uint extra=HA_FT_WLEN+ftb->info->s->rec_reflength; /* just a shortcut */
+
+ if (ftb->state != UNINITIALIZED)
+ return;
+
+ param.prev=' ';
+ param.quot= up_quot;
+ while ((res=ft_get_word(ftb->charset,start,end,&w,&param)))
+ {
+ int r=param.plusminus;
+ float weight= (float) (param.pmsign ? nwghts : wghts)[(r>5)?5:((r<-5)?-5:r)];
+ switch (res) {
+ case 1: /* word found */
+ ftbw=(FTB_WORD *)alloc_root(&ftb->mem_root,
+ sizeof(FTB_WORD) +
+ (param.trunc ? MI_MAX_KEY_BUFF :
+ w.len*ftb->charset->mbmaxlen+extra));
+ ftbw->len=w.len+1;
+ ftbw->flags=0;
+ ftbw->off=0;
+ if (param.yesno>0) ftbw->flags|=FTB_FLAG_YES;
+ if (param.yesno<0) ftbw->flags|=FTB_FLAG_NO;
+ if (param.trunc) ftbw->flags|=FTB_FLAG_TRUNC;
+ ftbw->weight=weight;
+ ftbw->up=up;
+ ftbw->docid[0]=ftbw->docid[1]=HA_OFFSET_ERROR;
+ ftbw->ndepth= (param.yesno<0) + depth;
+ ftbw->key_root=HA_OFFSET_ERROR;
+ memcpy(ftbw->word+1, w.pos, w.len);
+ ftbw->word[0]=w.len;
+ if (param.yesno > 0) up->ythresh++;
+ queue_insert(& ftb->queue, (byte *)ftbw);
+ ftb->with_scan|=(param.trunc & FTB_FLAG_TRUNC);
+ case 4: /* not indexed word (stopword or too short/long) */
+ if (! up_quot) break;
+ phrase_word= (FT_WORD *)alloc_root(&ftb->mem_root, sizeof(FT_WORD));
+ phrase_list= (LIST *)alloc_root(&ftb->mem_root, sizeof(LIST));
+ phrase_word->pos= w.pos;
+ phrase_word->len= w.len;
+ phrase_list->data= (void *)phrase_word;
+ up->phrase= list_add(up->phrase, phrase_list);
+ break;
+ case 2: /* left bracket */
+ ftbe=(FTB_EXPR *)alloc_root(&ftb->mem_root, sizeof(FTB_EXPR));
+ ftbe->flags=0;
+ if (param.yesno>0) ftbe->flags|=FTB_FLAG_YES;
+ if (param.yesno<0) ftbe->flags|=FTB_FLAG_NO;
+ ftbe->weight=weight;
+ ftbe->up=up;
+ ftbe->ythresh=ftbe->yweaks=0;
+ ftbe->docid[0]=ftbe->docid[1]=HA_OFFSET_ERROR;
+ ftbe->phrase= NULL;
+ if (param.quot) ftb->with_scan|=2;
+ if (param.yesno > 0) up->ythresh++;
+ _ftb_parse_query(ftb, start, end, ftbe, depth+1, param.quot);
+ param.quot=0;
+ break;
+ case 3: /* right bracket */
+ if (up_quot) up->phrase= list_reverse(up->phrase);
+ return;
+ }
+ }
+ 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;
+ MI_INFO *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=_mi_search(info, ftbw->keyinfo, (uchar*) ftbw->word, ftbw->len,
+ SEARCH_FIND | SEARCH_BIGGER, ftbw->key_root);
+ }
+ else
+ {
+ r=_mi_search(info, ftbw->keyinfo, (uchar*) lastkey_buf,
+ USE_WHOLE_KEY, SEARCH_BIGGER, 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= _mi_search_next(info, ftbw->keyinfo, info->lastkey,
+ info->lastkey_length,
+ SEARCH_BIGGER, ftbw->key_root);
+ }
+
+ if (!r && !ftbw->off)
+ {
+ r= mi_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 */
+ _mi_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=_mi_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;
+ 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 * ft_init_boolean_search(MI_INFO *info, uint keynr, byte *query,
+ uint query_len, CHARSET_INFO *cs)
+{
+ FTB *ftb;
+ FTB_EXPR *ftbe;
+ uint res;
+
+ if (!(ftb=(FTB *)my_malloc(sizeof(FTB), MYF(MY_WME))))
+ return 0;
+ ftb->please= (struct _ft_vft *) & _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));
+
+ init_alloc_root(&ftb->mem_root, 1024, 1024);
+
+ /*
+ Hack: instead of init_queue, we'll use reinit queue to be able
+ to alloc queue with alloc_root()
+ */
+ res=ftb->queue.max_elements=1+query_len/(min(ft_min_word_len,2)+1);
+ if (!(ftb->queue.root=
+ (byte **)alloc_root(&ftb->mem_root, (res+1)*sizeof(void*))))
+ goto err;
+ reinit_queue(& ftb->queue, res, 0, 0,
+ (int (*)(void*,byte*,byte*))FTB_WORD_cmp, 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->ythresh=ftbe->yweaks=0;
+ ftbe->docid[0]=ftbe->docid[1]=HA_OFFSET_ERROR;
+ ftbe->phrase= NULL;
+ ftb->root=ftbe;
+ _ftb_parse_query(ftb, &query, query+query_len, ftbe, 0, NULL);
+ 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;
+}
+
+
+/*
+ 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(const byte *s0, const byte *e0,
+ LIST *phrase, CHARSET_INFO *cs)
+{
+ FT_WORD h_word;
+ const byte *h_start= s0;
+ DBUG_ENTER("_ftb_strstr");
+ DBUG_ASSERT(phrase);
+
+ while (ft_simple_get_word(cs, (byte **)&h_start, e0, &h_word, FALSE))
+ {
+ FT_WORD *n_word;
+ LIST *phrase_element= phrase;
+ const byte *h_start1= h_start;
+ for (;;)
+ {
+ n_word= (FT_WORD *)phrase_element->data;
+ if (my_strnncoll(cs, h_word.pos, h_word.len, n_word->pos, n_word->len))
+ break;
+ if (! (phrase_element= phrase_element->next))
+ DBUG_RETURN(1);
+ if (! ft_simple_get_word(cs, (byte **)&h_start1, e0, &h_word, FALSE))
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_RETURN(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];
+
+ 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 (_mi_ft_segiterator(&ftsi) && not_found)
+ {
+ if (!ftsi.pos)
+ continue;
+ not_found = ! _ftb_check_phrase(ftsi.pos, ftsi.pos+ftsi.len,
+ ftbe->phrase, ftb->charset);
+ }
+ 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 ft_boolean_read_next(FT_INFO *ftb, char *record)
+{
+ FTB_EXPR *ftbe;
+ FTB_WORD *ftbw;
+ MI_INFO *info=ftb->info;
+ my_off_t curdoc;
+
+ if (ftb->state != INDEX_SEARCH && ftb->state != INDEX_DONE)
+ return -1;
+
+ /* black magic ON */
+ if ((int) _mi_check_index(info, ftb->keynr) < 0)
+ return my_errno;
+ if (_mi_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 && 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;
+}
+
+
+float ft_boolean_find_relevance(FT_INFO *ftb, byte *record, uint length)
+{
+ FT_WORD word;
+ FTB_WORD *ftbw;
+ FTB_EXPR *ftbe;
+ FT_SEG_ITERATOR ftsi, ftsi2;
+ const byte *end;
+ my_off_t docid=ftb->info->lastpos;
+
+ if (docid == HA_OFFSET_ERROR)
+ return -2.0;
+ if (!ftb->queue.elements)
+ 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)
+ _mi_ft_segiterator_dummy_init(record, length, &ftsi);
+ else
+ _mi_ft_segiterator_init(ftb->info, ftb->keynr, record, &ftsi);
+ memcpy(&ftsi2, &ftsi, sizeof(ftsi));
+
+ while (_mi_ft_segiterator(&ftsi))
+ {
+ if (!ftsi.pos)
+ continue;
+
+ end=ftsi.pos+ftsi.len;
+ while (ft_simple_get_word(ftb->charset, (byte **) &ftsi.pos,
+ (byte *) end, &word, TRUE))
+ {
+ 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 (mi_compare_text(ftb->charset, (uchar*) word.pos, 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 (mi_compare_text(ftb->charset, (uchar*) word.pos, word.len,
+ (uchar*) ftbw->word+1,ftbw->len-1,
+ (my_bool) (ftbw->flags&FTB_FLAG_TRUNC),0))
+ break;
+ if (ftbw->docid[1] == docid)
+ continue;
+ ftbw->docid[1]=docid;
+ _ftb_climb_the_tree(ftb, ftbw, &ftsi2);
+ }
+ }
+ }
+
+ 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 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 ft_boolean_get_relevance(FT_INFO *ftb)
+{
+ return ftb->root->cur_weight;
+}
+
+
+void ft_boolean_reinit_search(FT_INFO *ftb)
+{
+ _ftb_init_index_search(ftb);
+}
+
diff --git a/storage/myisam/ft_eval.c b/storage/myisam/ft_eval.c
new file mode 100644
index 00000000000..34248c69f20
--- /dev/null
+++ b/storage/myisam/ft_eval.c
@@ -0,0 +1,253 @@
+/* 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 */
+
+/* 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 "ftdefs.h"
+#include "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[])
+{
+ MI_INFO *file;
+ int i,j;
+
+ MY_INIT(argv[0]);
+ get_options(argc,argv);
+ bzero((char*)recinfo,sizeof(recinfo));
+
+ /* First define 2 columns */
+ recinfo[0].type=FIELD_SKIP_ENDSPACE;
+ recinfo[0].length=docid_length;
+ recinfo[1].type=FIELD_BLOB;
+ recinfo[1].length= 4+mi_portable_sizeof_char_ptr;
+
+ /* Define a key over the first column */
+ keyinfo[0].seg=keyseg;
+ keyinfo[0].keysegs=1;
+ 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 (mi_create(filename,1,keyinfo,2,recinfo,0,NULL,(MI_CREATE_INFO*) 0,0))
+ goto err;
+ if (!(file=mi_open(filename,2,0)))
+ goto err;
+ if (!silent)
+ printf("Initializing stopwords\n");
+ ft_init_stopwords(stopwordlist);
+
+ if (!silent)
+ printf("- Writing key:s\n");
+
+ my_errno=0;
+ i=0;
+ while (create_record(record,df))
+ {
+ error=mi_write(file,record);
+ if (error)
+ printf("I= %2d mi_write: %d errno: %d\n",i,error,my_errno);
+ i++;
+ }
+ fclose(df);
+
+ if (mi_close(file)) goto err;
+ if (!silent)
+ printf("- Reopening file\n");
+ if (!(file=mi_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=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=ft_nlq_read_next(result, read_record))==0;j++)
+ {
+ t=uint2korr(read_record);
+ w=ft_nlq_get_relevance(result);
+ printf("%d %.*s %f\n",i,t,read_record+2,w);
+ }
+ if (err != HA_ERR_END_OF_FILE)
+ {
+ printf("ft_read_next %d failed with errno %3d\n",j,my_errno);
+ goto err;
+ }
+ ft_nlq_close_search(result);
+ }
+
+ if (mi_close(file)) goto err;
+ my_end(MY_CHECK_ERROR);
+
+ return (0);
+
+ err:
+ printf("got error: %3d when using myisam-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 != 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==ft_precompiled_stopwords) stopwordlist=NULL; break;
+ case '#':
+ DEBUGGER_ON;
+ 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);
+}
diff --git a/storage/myisam/ft_eval.h b/storage/myisam/ft_eval.h
new file mode 100644
index 00000000000..5501fe9d34b
--- /dev/null
+++ b/storage/myisam/ft_eval.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2000 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=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;
+
+MI_COLUMNDEF recinfo[3];
+MI_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];
diff --git a/storage/myisam/ft_nlq_search.c b/storage/myisam/ft_nlq_search.c
new file mode 100644
index 00000000000..7a506fd11c6
--- /dev/null
+++ b/storage/myisam/ft_nlq_search.c
@@ -0,0 +1,360 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+#define FT_CORE
+#include "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;
+ MI_INFO *info;
+ int ndocs;
+ int curdoc;
+ FT_DOC doc[1];
+};
+
+typedef struct st_all_in_one
+{
+ MI_INFO *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;
+ MI_INFO *info=aio->info;
+ uchar *keybuff=aio->keybuff;
+ MI_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=_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=_mi_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= _mi_search_next(info, keyinfo, info->lastkey,
+ info->lastkey_length, SEARCH_BIGGER, key_root))
+ ;
+
+ info->update|= HA_STATE_AKTIV; /* for _mi_test_if_changed() */
+
+ /* The following should be safe, even if we compare doubles */
+ while (!r && gweight)
+ {
+
+ if (keylen &&
+ mi_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=_mi_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 (_mi_test_if_changed(info) == 0)
+ r=_mi_search_next(info, keyinfo, info->lastkey, info->lastkey_length,
+ SEARCH_BIGGER, key_root);
+ else
+ r=_mi_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= _mi_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 *ft_init_nlq_search(MI_INFO *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;
+ DBUG_ENTER("ft_init_nlq_search");
+
+/* black magic ON */
+ if ((int) (keynr = _mi_check_index(info,keynr)) < 0)
+ DBUG_RETURN(NULL);
+ if (_mi_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;
+
+ bzero(&wtree,sizeof(wtree));
+
+ init_tree(&aio.dtree,0,0,sizeof(FT_SUPERDOC),(qsort_cmp2)&FT_SUPERDOC_cmp,0,
+ NULL, NULL);
+
+ ft_parse_init(&wtree, aio.charset);
+ if (ft_parse(&wtree,query,query_len,0))
+ 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;
+ _mi_ft_parse(&wtree, info, keynr, record,1);
+ }
+ }
+ 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)*(aio.dtree.elements_in_tree-1),
+ MYF(0));
+ if (!dlist)
+ goto err;
+
+ dlist->please= (struct _ft_vft *) & _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 ft_nlq_read_next(FT_INFO *handler, char *record)
+{
+ MI_INFO *info= (MI_INFO *) 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 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 ft_nlq_close_search(FT_INFO *handler)
+{
+ my_free((gptr)handler,MYF(0));
+}
+
+
+float ft_nlq_get_relevance(FT_INFO *handler)
+{
+ return (float) handler->doc[handler->curdoc].weight;
+}
+
+
+void ft_nlq_reinit_search(FT_INFO *handler)
+{
+ handler->curdoc=-1;
+}
+
diff --git a/storage/myisam/ft_parser.c b/storage/myisam/ft_parser.c
new file mode 100644
index 00000000000..2fad2363ae2
--- /dev/null
+++ b/storage/myisam/ft_parser.c
@@ -0,0 +1,249 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+#include "ftdefs.h"
+
+typedef struct st_ft_docstat {
+ FT_WORD *list;
+ uint uniq;
+ double sum;
+} FT_DOCSTAT;
+
+static int FT_WORD_cmp(CHARSET_INFO* cs, FT_WORD *w1, FT_WORD *w2)
+{
+ return mi_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 * ft_linearize(TREE *wtree)
+{
+ FT_WORD *wlist,*p;
+ FT_DOCSTAT docstat;
+ DBUG_ENTER("ft_linearize");
+
+ if ((wlist=(FT_WORD *) my_malloc(sizeof(FT_WORD)*
+ (1+wtree->elements_in_tree),MYF(0))))
+ {
+ 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 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 ft_get_word(CHARSET_INFO *cs, byte **start, byte *end,
+ FT_WORD *word, FTB_PARAM *param)
+{
+ byte *doc=*start;
+ uint mwc, length, mbl;
+
+ param->yesno=(FTB_YES==' ') ? 1 : (param->quot != 0);
+ param->plusminus=param->pmsign=0;
+
+ while (doc<end)
+ {
+ for (;doc<end;doc++)
+ {
+ if (true_word_char(cs,*doc)) break;
+ if (*doc == FTB_RQUOT && param->quot)
+ {
+ param->quot=doc;
+ *start=doc+1;
+ return 3; /* FTB_RBR */
+ }
+ 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;
+ return (*doc == FTB_RBR)+2;
+ }
+ 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->plusminus++; continue; } else
+ if (*doc == FTB_DEC ) { param->plusminus--; continue; } else
+ if (*doc == FTB_NEG ) { param->pmsign=!param->pmsign; continue; }
+ }
+ }
+ param->prev=*doc;
+ param->yesno=(FTB_YES==' ') ? 1 : (param->quot != 0);
+ param->plusminus=param->pmsign=0;
+ }
+
+ mwc=length=0;
+ for (word->pos=doc; doc<end; length++, mbl=my_mbcharlen(cs, *(uchar *)doc), doc+=(mbl ? mbl : 1))
+ if (true_word_char(cs,*doc))
+ mwc=0;
+ else if (!misc_word_char(*doc) || mwc++)
+ break;
+
+ 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;
+ return 1;
+ }
+ else if (length) /* make sure length > 0 (if start contains spaces only) */
+ {
+ *start= doc;
+ return 4;
+ }
+ }
+ if (param->quot)
+ {
+ param->quot=*start=doc;
+ return 3; /* FTB_RBR */
+ }
+ return 0;
+}
+
+byte 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;
+ DBUG_ENTER("ft_simple_get_word");
+
+ do
+ {
+ for (;; doc++)
+ {
+ if (doc >= end) DBUG_RETURN(0);
+ if (true_word_char(cs, *doc)) break;
+ }
+
+ mwc= length= 0;
+ for (word->pos=doc; doc<end; length++, mbl=my_mbcharlen(cs, *(uchar *)doc), doc+=(mbl ? mbl : 1))
+ if (true_word_char(cs,*doc))
+ mwc= 0;
+ else if (!misc_word_char(*doc) || mwc++)
+ break;
+
+ 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 ft_parse_init(TREE *wtree, CHARSET_INFO *cs)
+{
+ DBUG_ENTER("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;
+}
+
+int ft_parse(TREE *wtree, byte *doc, int doclen, my_bool with_alloc)
+{
+ byte *end=doc+doclen;
+ FT_WORD w;
+ DBUG_ENTER("ft_parse");
+
+ while (ft_simple_get_word(wtree->custom_arg, &doc, end, &w, TRUE))
+ {
+ if (with_alloc)
+ {
+ byte *ptr;
+ /* allocating the data in the tree - to avoid mallocs and frees */
+ DBUG_ASSERT(wtree->with_delete==0);
+ ptr=(byte *)alloc_root(& wtree->mem_root,w.len);
+ memcpy(ptr, w.pos, w.len);
+ w.pos=ptr;
+ }
+ if (!tree_insert(wtree, &w, 0, wtree->custom_arg))
+ goto err;
+ }
+ DBUG_RETURN(0);
+
+err:
+ delete_tree(wtree);
+ DBUG_RETURN(1);
+}
+
diff --git a/storage/myisam/ft_static.c b/storage/myisam/ft_static.c
new file mode 100644
index 00000000000..e221950f445
--- /dev/null
+++ b/storage/myisam/ft_static.c
@@ -0,0 +1,628 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+#include "ftdefs.h"
+
+ulong ft_min_word_len=4;
+ulong ft_max_word_len=HA_FT_MAXCHARLEN;
+ulong ft_query_expansion_limit=5;
+char ft_boolean_syntax[]="+ -><()~*:\"\"&|";
+
+const HA_KEYSEG ft_keysegs[FT_SEGS]={
+{
+ 0, /* charset */
+ HA_FT_WLEN, /* start */
+ 0, /* null_pos */
+ 0, /* Bit pos */
+ HA_VAR_LENGTH_PART | HA_PACK_KEY, /* flag */
+ HA_FT_MAXBYTELEN, /* length */
+ HA_KEYTYPE_VARTEXT2, /* type */
+ 63, /* language (will be overwritten) */
+ 0, /* null_bit */
+ 2, 0, 0 /* bit_start, bit_end, bit_length */
+},
+{
+ /*
+ Note, this (and the last HA_KEYTYPE_END) segment should NOT
+ be packed in any way, otherwise w_search() won't be able to
+ update key entry 'in vivo'
+ */
+ 0, 0, 0, 0, HA_NO_SORT, HA_FT_WLEN, HA_FT_WTYPE, 63, 0, 0, 0, 0
+}
+};
+
+const struct _ft_vft _ft_vft_nlq = {
+ ft_nlq_read_next, ft_nlq_find_relevance, ft_nlq_close_search,
+ ft_nlq_get_relevance, ft_nlq_reinit_search
+};
+const struct _ft_vft _ft_vft_boolean = {
+ ft_boolean_read_next, ft_boolean_find_relevance, ft_boolean_close_search,
+ ft_boolean_get_relevance, ft_boolean_reinit_search
+};
+
+
+FT_INFO *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= ft_init_boolean_search((MI_INFO *)info, keynr, query, query_len,cs);
+ else
+ res= ft_init_nlq_search((MI_INFO *)info, keynr, query, query_len, flags,
+ record);
+ return res;
+}
+
+const char *ft_stopword_file = 0;
+const char *ft_precompiled_stopwords[] = {
+
+#ifdef COMPILE_STOPWORDS_IN
+
+/* This particular stopword list was taken from SMART distribution
+ ftp://ftp.cs.cornell.edu/pub/smart/smart.11.0.tar.Z
+ it was slightly modified to my taste, though
+ */
+
+ "a's",
+ "able",
+ "about",
+ "above",
+ "according",
+ "accordingly",
+ "across",
+ "actually",
+ "after",
+ "afterwards",
+ "again",
+ "against",
+ "ain't",
+ "all",
+ "allow",
+ "allows",
+ "almost",
+ "alone",
+ "along",
+ "already",
+ "also",
+ "although",
+ "always",
+ "am",
+ "among",
+ "amongst",
+ "an",
+ "and",
+ "another",
+ "any",
+ "anybody",
+ "anyhow",
+ "anyone",
+ "anything",
+ "anyway",
+ "anyways",
+ "anywhere",
+ "apart",
+ "appear",
+ "appreciate",
+ "appropriate",
+ "are",
+ "aren't",
+ "around",
+ "as",
+ "aside",
+ "ask",
+ "asking",
+ "associated",
+ "at",
+ "available",
+ "away",
+ "awfully",
+ "be",
+ "became",
+ "because",
+ "become",
+ "becomes",
+ "becoming",
+ "been",
+ "before",
+ "beforehand",
+ "behind",
+ "being",
+ "believe",
+ "below",
+ "beside",
+ "besides",
+ "best",
+ "better",
+ "between",
+ "beyond",
+ "both",
+ "brief",
+ "but",
+ "by",
+ "c'mon",
+ "c's",
+ "came",
+ "can",
+ "can't",
+ "cannot",
+ "cant",
+ "cause",
+ "causes",
+ "certain",
+ "certainly",
+ "changes",
+ "clearly",
+ "co",
+ "com",
+ "come",
+ "comes",
+ "concerning",
+ "consequently",
+ "consider",
+ "considering",
+ "contain",
+ "containing",
+ "contains",
+ "corresponding",
+ "could",
+ "couldn't",
+ "course",
+ "currently",
+ "definitely",
+ "described",
+ "despite",
+ "did",
+ "didn't",
+ "different",
+ "do",
+ "does",
+ "doesn't",
+ "doing",
+ "don't",
+ "done",
+ "down",
+ "downwards",
+ "during",
+ "each",
+ "edu",
+ "eg",
+ "eight",
+ "either",
+ "else",
+ "elsewhere",
+ "enough",
+ "entirely",
+ "especially",
+ "et",
+ "etc",
+ "even",
+ "ever",
+ "every",
+ "everybody",
+ "everyone",
+ "everything",
+ "everywhere",
+ "ex",
+ "exactly",
+ "example",
+ "except",
+ "far",
+ "few",
+ "fifth",
+ "first",
+ "five",
+ "followed",
+ "following",
+ "follows",
+ "for",
+ "former",
+ "formerly",
+ "forth",
+ "four",
+ "from",
+ "further",
+ "furthermore",
+ "get",
+ "gets",
+ "getting",
+ "given",
+ "gives",
+ "go",
+ "goes",
+ "going",
+ "gone",
+ "got",
+ "gotten",
+ "greetings",
+ "had",
+ "hadn't",
+ "happens",
+ "hardly",
+ "has",
+ "hasn't",
+ "have",
+ "haven't",
+ "having",
+ "he",
+ "he's",
+ "hello",
+ "help",
+ "hence",
+ "her",
+ "here",
+ "here's",
+ "hereafter",
+ "hereby",
+ "herein",
+ "hereupon",
+ "hers",
+ "herself",
+ "hi",
+ "him",
+ "himself",
+ "his",
+ "hither",
+ "hopefully",
+ "how",
+ "howbeit",
+ "however",
+ "i'd",
+ "i'll",
+ "i'm",
+ "i've",
+ "ie",
+ "if",
+ "ignored",
+ "immediate",
+ "in",
+ "inasmuch",
+ "inc",
+ "indeed",
+ "indicate",
+ "indicated",
+ "indicates",
+ "inner",
+ "insofar",
+ "instead",
+ "into",
+ "inward",
+ "is",
+ "isn't",
+ "it",
+ "it'd",
+ "it'll",
+ "it's",
+ "its",
+ "itself",
+ "just",
+ "keep",
+ "keeps",
+ "kept",
+ "know",
+ "knows",
+ "known",
+ "last",
+ "lately",
+ "later",
+ "latter",
+ "latterly",
+ "least",
+ "less",
+ "lest",
+ "let",
+ "let's",
+ "like",
+ "liked",
+ "likely",
+ "little",
+ "look",
+ "looking",
+ "looks",
+ "ltd",
+ "mainly",
+ "many",
+ "may",
+ "maybe",
+ "me",
+ "mean",
+ "meanwhile",
+ "merely",
+ "might",
+ "more",
+ "moreover",
+ "most",
+ "mostly",
+ "much",
+ "must",
+ "my",
+ "myself",
+ "name",
+ "namely",
+ "nd",
+ "near",
+ "nearly",
+ "necessary",
+ "need",
+ "needs",
+ "neither",
+ "never",
+ "nevertheless",
+ "new",
+ "next",
+ "nine",
+ "no",
+ "nobody",
+ "non",
+ "none",
+ "noone",
+ "nor",
+ "normally",
+ "not",
+ "nothing",
+ "novel",
+ "now",
+ "nowhere",
+ "obviously",
+ "of",
+ "off",
+ "often",
+ "oh",
+ "ok",
+ "okay",
+ "old",
+ "on",
+ "once",
+ "one",
+ "ones",
+ "only",
+ "onto",
+ "or",
+ "other",
+ "others",
+ "otherwise",
+ "ought",
+ "our",
+ "ours",
+ "ourselves",
+ "out",
+ "outside",
+ "over",
+ "overall",
+ "own",
+ "particular",
+ "particularly",
+ "per",
+ "perhaps",
+ "placed",
+ "please",
+ "plus",
+ "possible",
+ "presumably",
+ "probably",
+ "provides",
+ "que",
+ "quite",
+ "qv",
+ "rather",
+ "rd",
+ "re",
+ "really",
+ "reasonably",
+ "regarding",
+ "regardless",
+ "regards",
+ "relatively",
+ "respectively",
+ "right",
+ "said",
+ "same",
+ "saw",
+ "say",
+ "saying",
+ "says",
+ "second",
+ "secondly",
+ "see",
+ "seeing",
+ "seem",
+ "seemed",
+ "seeming",
+ "seems",
+ "seen",
+ "self",
+ "selves",
+ "sensible",
+ "sent",
+ "serious",
+ "seriously",
+ "seven",
+ "several",
+ "shall",
+ "she",
+ "should",
+ "shouldn't",
+ "since",
+ "six",
+ "so",
+ "some",
+ "somebody",
+ "somehow",
+ "someone",
+ "something",
+ "sometime",
+ "sometimes",
+ "somewhat",
+ "somewhere",
+ "soon",
+ "sorry",
+ "specified",
+ "specify",
+ "specifying",
+ "still",
+ "sub",
+ "such",
+ "sup",
+ "sure",
+ "t's",
+ "take",
+ "taken",
+ "tell",
+ "tends",
+ "th",
+ "than",
+ "thank",
+ "thanks",
+ "thanx",
+ "that",
+ "that's",
+ "thats",
+ "the",
+ "their",
+ "theirs",
+ "them",
+ "themselves",
+ "then",
+ "thence",
+ "there",
+ "there's",
+ "thereafter",
+ "thereby",
+ "therefore",
+ "therein",
+ "theres",
+ "thereupon",
+ "these",
+ "they",
+ "they'd",
+ "they'll",
+ "they're",
+ "they've",
+ "think",
+ "third",
+ "this",
+ "thorough",
+ "thoroughly",
+ "those",
+ "though",
+ "three",
+ "through",
+ "throughout",
+ "thru",
+ "thus",
+ "to",
+ "together",
+ "too",
+ "took",
+ "toward",
+ "towards",
+ "tried",
+ "tries",
+ "truly",
+ "try",
+ "trying",
+ "twice",
+ "two",
+ "un",
+ "under",
+ "unfortunately",
+ "unless",
+ "unlikely",
+ "until",
+ "unto",
+ "up",
+ "upon",
+ "us",
+ "use",
+ "used",
+ "useful",
+ "uses",
+ "using",
+ "usually",
+ "value",
+ "various",
+ "very",
+ "via",
+ "viz",
+ "vs",
+ "want",
+ "wants",
+ "was",
+ "wasn't",
+ "way",
+ "we",
+ "we'd",
+ "we'll",
+ "we're",
+ "we've",
+ "welcome",
+ "well",
+ "went",
+ "were",
+ "weren't",
+ "what",
+ "what's",
+ "whatever",
+ "when",
+ "whence",
+ "whenever",
+ "where",
+ "where's",
+ "whereafter",
+ "whereas",
+ "whereby",
+ "wherein",
+ "whereupon",
+ "wherever",
+ "whether",
+ "which",
+ "while",
+ "whither",
+ "who",
+ "who's",
+ "whoever",
+ "whole",
+ "whom",
+ "whose",
+ "why",
+ "will",
+ "willing",
+ "wish",
+ "with",
+ "within",
+ "without",
+ "won't",
+ "wonder",
+ "would",
+ "would",
+ "wouldn't",
+ "yes",
+ "yet",
+ "you",
+ "you'd",
+ "you'll",
+ "you're",
+ "you've",
+ "your",
+ "yours",
+ "yourself",
+ "yourselves",
+ "zero",
+#endif
+
+ NULL };
diff --git a/storage/myisam/ft_stem.c b/storage/myisam/ft_stem.c
new file mode 100644
index 00000000000..846d5d2247f
--- /dev/null
+++ b/storage/myisam/ft_stem.c
@@ -0,0 +1,19 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+/* mulitingual stem */
diff --git a/storage/myisam/ft_stopwords.c b/storage/myisam/ft_stopwords.c
new file mode 100644
index 00000000000..ab51afb0e82
--- /dev/null
+++ b/storage/myisam/ft_stopwords.c
@@ -0,0 +1,129 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+#include "ftdefs.h"
+#include "my_handler.h"
+
+typedef struct st_ft_stopwords
+{
+ const char * pos;
+ uint len;
+} FT_STOPWORD;
+
+static TREE *stopwords3=NULL;
+
+static int FT_STOPWORD_cmp(void* cmp_arg __attribute__((unused)),
+ FT_STOPWORD *w1, FT_STOPWORD *w2)
+{
+ return mi_compare_text(default_charset_info,
+ (uchar *)w1->pos,w1->len,
+ (uchar *)w2->pos,w2->len,0,0);
+}
+
+static void FT_STOPWORD_free(FT_STOPWORD *w, TREE_FREE action,
+ void *arg __attribute__((unused)))
+{
+ if (action == free_free)
+ my_free((gptr) w->pos, MYF(0));
+}
+
+static int ft_add_stopword(const char *w)
+{
+ FT_STOPWORD sw;
+ return !w ||
+ (((sw.len= (uint) strlen(sw.pos=w)) >= ft_min_word_len) &&
+ (tree_insert(stopwords3, &sw, 0, stopwords3->custom_arg)==NULL));
+}
+
+int ft_init_stopwords()
+{
+ if (!stopwords3)
+ {
+ if (!(stopwords3=(TREE *)my_malloc(sizeof(TREE),MYF(0))))
+ return -1;
+ init_tree(stopwords3,0,0,sizeof(FT_STOPWORD),(qsort_cmp2)&FT_STOPWORD_cmp,
+ 0,
+ (ft_stopword_file ? (tree_element_free)&FT_STOPWORD_free : 0),
+ NULL);
+ }
+
+ if (ft_stopword_file)
+ {
+ File fd;
+ uint len;
+ byte *buffer, *start, *end;
+ FT_WORD w;
+ int error=-1;
+
+ if (!*ft_stopword_file)
+ return 0;
+
+ if ((fd=my_open(ft_stopword_file, O_RDONLY, MYF(MY_WME))) == -1)
+ return -1;
+ len=(uint)my_seek(fd, 0L, MY_SEEK_END, MYF(0));
+ my_seek(fd, 0L, MY_SEEK_SET, MYF(0));
+ if (!(start=buffer=my_malloc(len+1, MYF(MY_WME))))
+ goto err0;
+ len=my_read(fd, buffer, len, MYF(MY_WME));
+ end=start+len;
+ while (ft_simple_get_word(default_charset_info, &start, end, &w, TRUE))
+ {
+ if (ft_add_stopword(my_strdup_with_length(w.pos, w.len, MYF(0))))
+ goto err1;
+ }
+ error=0;
+err1:
+ my_free(buffer, MYF(0));
+err0:
+ my_close(fd, MYF(MY_WME));
+ return error;
+ }
+ else
+ {
+ /* compatibility mode: to be removed */
+ char **sws=(char **)ft_precompiled_stopwords;
+
+ for (;*sws;sws++)
+ {
+ if (ft_add_stopword(*sws))
+ return -1;
+ }
+ ft_stopword_file="(built-in)"; /* for SHOW VARIABLES */
+ }
+ return 0;
+}
+
+int is_stopword(char *word, uint len)
+{
+ FT_STOPWORD sw;
+ sw.pos=word;
+ sw.len=len;
+ return tree_search(stopwords3,&sw, stopwords3->custom_arg) != NULL;
+}
+
+
+void ft_free_stopwords()
+{
+ if (stopwords3)
+ {
+ delete_tree(stopwords3); /* purecov: inspected */
+ my_free((char*) stopwords3,MYF(0));
+ stopwords3=0;
+ }
+ ft_stopword_file= 0;
+}
diff --git a/storage/myisam/ft_test1.c b/storage/myisam/ft_test1.c
new file mode 100644
index 00000000000..14be9aa1e8c
--- /dev/null
+++ b/storage/myisam/ft_test1.c
@@ -0,0 +1,316 @@
+/* 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 */
+
+/* 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 "ftdefs.h"
+#include "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);
+
+ exit(run_test("FT1"));
+}
+
+static MI_COLUMNDEF recinfo[3];
+static MI_KEYDEF keyinfo[2];
+static HA_KEYSEG keyseg[10];
+
+static int run_test(const char *filename)
+{
+ MI_INFO *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 + mi_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+mi_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].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 (mi_create(filename,(no_keys?0:1),keyinfo,2,recinfo,0,NULL,
+ (MI_CREATE_INFO*) 0, create_flag))
+ goto err;
+ if (!(file=mi_open(filename,2,0)))
+ goto err;
+
+ if (!silent)
+ printf("- %s stopwords\n",no_stopwords?"Skipping":"Initializing");
+ ft_init_stopwords(no_stopwords?NULL:ft_precompiled_stopwords);
+
+ if (!silent)
+ printf("- Writing key:s\n");
+
+ my_errno=0;
+ for (i=NUPD ; i<NDATAS; i++ )
+ {
+ create_record(record,i);
+ error=mi_write(file,record);
+ if (verbose || error)
+ printf("I= %2d mi_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=mi_rrnd(file,read_record,pos)) == 0)
+ {
+ create_record(record,NUPD-i-1);
+ if (mi_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 (mi_close(file)) goto err;
+ if(no_search) return 0;
+ if (!silent)
+ printf("- Reopening file\n");
+ if (!(file=mi_open(filename,2,0))) goto err;
+ if (!silent)
+ printf("- Reading rows with key\n");
+ for (i=0 ; i < NQUERIES ; i++)
+ {
+ FT_DOCLIST *result;
+ result=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= ft_nlq_read_next(result, read_record);
+ if (err==HA_ERR_END_OF_FILE)
+ {
+ printf("No more matches!\n");
+ break;
+ }
+ else if (err)
+ {
+ printf("ft_read_next %d failed with errno %3d\n",j,my_errno);
+ break;
+ }
+ w=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);
+ }
+ ft_nlq_close_search(result);
+ }
+
+ if (mi_close(file)) goto err;
+ my_end(MY_CHECK_ERROR);
+
+ return (0);
+err:
+ printf("got error: %3d when using myisam-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 '#':
+ DEBUGGER_ON;
+ 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);
+}
diff --git a/storage/myisam/ft_test1.h b/storage/myisam/ft_test1.h
new file mode 100644
index 00000000000..e360244057b
--- /dev/null
+++ b/storage/myisam/ft_test1.h
@@ -0,0 +1,421 @@
+/* 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 */
+
+/* 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 &#38; 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 &#38; 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"
+};
diff --git a/storage/myisam/ft_update.c b/storage/myisam/ft_update.c
new file mode 100644
index 00000000000..b8cd925bf4f
--- /dev/null
+++ b/storage/myisam/ft_update.c
@@ -0,0 +1,350 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+/* functions to work with full-text indices */
+
+#include "ftdefs.h"
+#include <math.h>
+
+void _mi_ft_segiterator_init(MI_INFO *info, uint keynr, const byte *record,
+ FT_SEG_ITERATOR *ftsi)
+{
+ DBUG_ENTER("_mi_ft_segiterator_init");
+
+ ftsi->num=info->s->keyinfo[keynr].keysegs;
+ ftsi->seg=info->s->keyinfo[keynr].seg;
+ ftsi->rec=record;
+ DBUG_VOID_RETURN;
+}
+
+void _mi_ft_segiterator_dummy_init(const byte *record, uint len,
+ FT_SEG_ITERATOR *ftsi)
+{
+ DBUG_ENTER("_mi_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(_mi_ft_segiterator())
+
+ so "1" means "OK", "0" means "EOF"
+*/
+
+uint _mi_ft_segiterator(register FT_SEG_ITERATOR *ftsi)
+{
+ DBUG_ENTER("_mi_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=_mi_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 ft_parse for every keyseg */
+
+uint _mi_ft_parse(TREE *parsed, MI_INFO *info, uint keynr,
+ const byte *record, my_bool with_alloc)
+{
+ FT_SEG_ITERATOR ftsi;
+ DBUG_ENTER("_mi_ft_parse");
+
+ _mi_ft_segiterator_init(info, keynr, record, &ftsi);
+
+ ft_parse_init(parsed, info->s->keyinfo[keynr].seg->charset);
+ while (_mi_ft_segiterator(&ftsi))
+ {
+ if (ftsi.pos)
+ if (ft_parse(parsed, (byte *)ftsi.pos, ftsi.len, with_alloc))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+FT_WORD * _mi_ft_parserecord(MI_INFO *info, uint keynr, const byte *record)
+{
+ TREE ptree;
+ DBUG_ENTER("_mi_ft_parserecord");
+
+ bzero((char*) &ptree, sizeof(ptree));
+ if (_mi_ft_parse(&ptree, info, keynr, record,0))
+ DBUG_RETURN(NULL);
+
+ DBUG_RETURN(ft_linearize(&ptree));
+}
+
+static int _mi_ft_store(MI_INFO *info, uint keynr, byte *keybuf,
+ FT_WORD *wlist, my_off_t filepos)
+{
+ uint key_length;
+ DBUG_ENTER("_mi_ft_store");
+
+ for (; wlist->pos; wlist++)
+ {
+ key_length=_ft_make_key(info,keynr,keybuf,wlist,filepos);
+ if (_mi_ck_write(info,keynr,(uchar*) keybuf,key_length))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+static int _mi_ft_erase(MI_INFO *info, uint keynr, byte *keybuf,
+ FT_WORD *wlist, my_off_t filepos)
+{
+ uint key_length, err=0;
+ DBUG_ENTER("_mi_ft_erase");
+
+ for (; wlist->pos; wlist++)
+ {
+ key_length=_ft_make_key(info,keynr,keybuf,wlist,filepos);
+ if (_mi_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 _mi_ft_cmp(MI_INFO *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("_mi_ft_cmp");
+
+ _mi_ft_segiterator_init(info, keynr, rec1, &ftsi1);
+ _mi_ft_segiterator_init(info, keynr, rec2, &ftsi2);
+
+ while (_mi_ft_segiterator(&ftsi1) && _mi_ft_segiterator(&ftsi2))
+ {
+ if ((ftsi1.pos != ftsi2.pos) &&
+ (!ftsi1.pos || !ftsi2.pos ||
+ mi_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 _mi_ft_update(MI_INFO *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("_mi_ft_update");
+
+ if (!(old_word=oldlist=_mi_ft_parserecord(info, keynr, oldrec)))
+ goto err0;
+ if (!(new_word=newlist=_mi_ft_parserecord(info, keynr, newrec)))
+ goto err1;
+
+ error=0;
+ while(old_word->pos && new_word->pos)
+ {
+ cmp= mi_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=_ft_make_key(info,keynr,keybuf,old_word,pos);
+ if ((error=_mi_ck_delete(info,keynr,(uchar*) keybuf,key_length)))
+ goto err2;
+ }
+ if (cmp > 0 || cmp2)
+ {
+ key_length=_ft_make_key(info,keynr,keybuf,new_word,pos);
+ if ((error=_mi_ck_write(info,keynr,(uchar*) keybuf,key_length)))
+ goto err2;
+ }
+ if (cmp<=0) old_word++;
+ if (cmp>=0) new_word++;
+ }
+ if (old_word->pos)
+ error=_mi_ft_erase(info,keynr,keybuf,old_word,pos);
+ else if (new_word->pos)
+ error=_mi_ft_store(info,keynr,keybuf,new_word,pos);
+
+err2:
+ my_free((char*) newlist,MYF(0));
+err1:
+ my_free((char*) oldlist,MYF(0));
+err0:
+ DBUG_RETURN(error);
+}
+
+
+/* adds a document to the collection */
+
+int _mi_ft_add(MI_INFO *info, uint keynr, byte *keybuf, const byte *record,
+ my_off_t pos)
+{
+ int error= -1;
+ FT_WORD *wlist;
+ DBUG_ENTER("_mi_ft_add");
+
+ if ((wlist=_mi_ft_parserecord(info, keynr, record)))
+ {
+ error=_mi_ft_store(info,keynr,keybuf,wlist,pos);
+ my_free((char*) wlist,MYF(0));
+ }
+ DBUG_RETURN(error);
+}
+
+
+/* removes a document from the collection */
+
+int _mi_ft_del(MI_INFO *info, uint keynr, byte *keybuf, const byte *record,
+ my_off_t pos)
+{
+ int error= -1;
+ FT_WORD *wlist;
+ DBUG_ENTER("_mi_ft_del");
+ DBUG_PRINT("enter",("keynr: %d",keynr));
+
+ if ((wlist=_mi_ft_parserecord(info, keynr, record)))
+ {
+ error=_mi_ft_erase(info,keynr,keybuf,wlist,pos);
+ my_free((char*) wlist,MYF(0));
+ }
+ DBUG_PRINT("exit",("Return: %d",error));
+ DBUG_RETURN(error);
+}
+
+uint _ft_make_key(MI_INFO *info, uint keynr, byte *keybuf, FT_WORD *wptr,
+ my_off_t filepos)
+{
+ byte buf[HA_FT_MAXBYTELEN+16];
+ DBUG_ENTER("_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(_mi_make_key(info,keynr,(uchar*) keybuf,buf,filepos));
+}
+
+
+/*
+ convert key value to ft2
+*/
+
+uint _mi_ft_convert_to_ft2(MI_INFO *info, uint keynr, uchar *key)
+{
+ my_off_t root;
+ DYNAMIC_ARRAY *da=info->ft1_to_ft2;
+ MI_KEYDEF *keyinfo=&info->s->ft2_keyinfo;
+ uchar *key_ptr= (uchar*) dynamic_array_ptr(da, 0), *end;
+ uint length, key_length;
+ DBUG_ENTER("_mi_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 (_mi_ck_delete(info, keynr, key, key_length) == 0)
+ {
+ /*
+ nothing to do here.
+ _mi_ck_delete() will populate info->ft1_to_ft2 with deleted keys
+ */
+ }
+
+ /* creating pageful of keys */
+ mi_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= _mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR ||
+ _mi_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(_mi_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);
+ _mi_dpointer(info, key+key_length+HA_FT_WLEN, root);
+
+ DBUG_RETURN(_mi_ck_real_write_btree(info,
+ info->s->keyinfo+keynr,
+ key, 0,
+ &info->s->state.key_root[keynr],
+ SEARCH_SAME));
+}
+
diff --git a/storage/myisam/ftbench/Ecompare.pl b/storage/myisam/ftbench/Ecompare.pl
new file mode 100755
index 00000000000..265534e704d
--- /dev/null
+++ b/storage/myisam/ftbench/Ecompare.pl
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+
+# compares out-files (as created by Ereport.pl) from dir1/*.out and dir2/*.out
+# for each effectiveness column computes the probability of the hypothesis
+# "Both files have the same effectiveness"
+
+# sign test is used to verify that test results are statistically
+# significant to support the hypothesis. Function is computed on the fly.
+
+# basic formula is \sum_{r=0}^R C_N^r 2^{-N}
+# As N can be big, we'll work with logarithms
+$log2=log(2);
+sub probab {
+ my $N=shift, $R=shift;
+
+ my $r, $sum=0;
+
+ for $r (0..$R) {
+ $sum+=exp(logfac($N)-logfac($r)-logfac($N-$r)-$N*$log2);
+ }
+ return $sum;
+}
+
+# log(N!)
+# for N<20 exact value from the table (below) is taken
+# otherwise, Stirling approximation for N! is used
+sub logfac {
+ my $n=shift; die "n=$n<0" if $n<0;
+ return $logfactab[$n] if $n<=$#logfactab;
+ return $n*log($n)-$n+log(2*3.14159265358*$n)/2;
+}
+@logfactab=(
+0, 0, 0.693147180559945, 1.79175946922805, 3.17805383034795,
+4.78749174278205, 6.57925121201010, 8.52516136106541, 10.6046029027453,
+12.8018274800815, 15.1044125730755, 17.5023078458739, 19.9872144956619,
+22.5521638531234, 25.1912211827387, 27.8992713838409, 30.6718601060807,
+33.5050734501369, 36.3954452080331, 39.3398841871995, 42.3356164607535,
+);
+
+############################# main () ###############################
+#$p=shift; $m=shift; $p-=$m;
+#if($p>$m) {
+# print "1 > 2 [+$p-$m]: ", probab($p+$m, $m), "\n";
+#} elsif($p<$m) {
+# print "1 < 2 [+$p-$m]: ", probab($p+$m, $p), "\n";
+#} else {
+# print "1 = 2 [+$p-$m]: ", probab($p+$m, $m), "\n";
+#}
+#exit;
+
+die "Use: $0 dir1 dir2\n" unless @ARGV==2 &&
+ -d ($dir1=shift) && -d ($dir2=shift);
+$_=`cd $dir1; echo *.out`;
+s/\.out\b//g;
+$total="";
+
+for $file (split) {
+ open(OUT1,$out1="$dir1/$file.out") || die "Cannot open $out1: $!";
+ open(OUT2,$out2="$dir2/$file.out") || die "Cannot open $out2: $!";
+
+ @p=@m=();
+ while(!eof(OUT1) || !eof(OUT2)) {
+ $_=<OUT1>; @l1=split; shift @l1;
+ $_=<OUT2>; @l2=split; shift @l2;
+
+ die "Number of columns differ in line $.\n" unless $#l1 == $#l2;
+
+ for (0..$#l1) {
+ $p[$_]+= $l1[$_] > $l2[$_];
+ $m[$_]+= $l1[$_] < $l2[$_];
+ }
+ }
+
+ for (0..$#l1) {
+ $pp[$_]+=$p[$_]; $mm[$_]+=$m[$_];
+ $total[$_].=rep($file, ($#l1 ? $_ : undef), $p[$_], $m[$_]);
+ }
+ close OUT1;
+ close OUT2;
+}
+
+for (0..$#l1) {
+ rep($total[$_], ($#l1 ? $_ : undef), $pp[$_], $mm[$_]);
+}
+
+sub rep {
+ my ($test, $n, $p, $m, $c, $r)=@_;
+
+ if ($p>$m) { $c=">"; $r="+"; }
+ elsif($p<$m) { $c="<"; $r="-"; }
+ else { $c="="; $r="="; }
+ $n=" $n: " if defined $n;
+ printf "%-8s $n $dir1 $c $dir2 [+%03d-%03d]: %16.15f\n",
+ $test, $p, $m, probab($p+$m, ($p>=$m ? $m : $p));
+ $r;
+}
diff --git a/storage/myisam/ftbench/Ecreate.pl b/storage/myisam/ftbench/Ecreate.pl
new file mode 100755
index 00000000000..d90a6f7a0ad
--- /dev/null
+++ b/storage/myisam/ftbench/Ecreate.pl
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+
+$test=shift || die "Usage $0 testname [option]";
+$option=shift;
+
+open(D, "<data/$test.d") || die "Cannot open(<data/$test.d): $!";
+open(Q, "<data/$test.q") || die "Cannot open(<data/$test.q): $!";
+
+$N=0;
+
+print <<__HEADER__;
+DROP TABLE IF EXISTS $test;
+CREATE TABLE $test (
+ id int(10) unsigned NOT NULL,
+ text text NOT NULL,
+ FULLTEXT KEY text (text)
+) TYPE=MyISAM CHARSET=latin1;
+
+ALTER TABLE $test DISABLE KEYS;
+__HEADER__
+
+while (<D>) { chomp;
+ s/'/\\'/g; ++$N;
+ print "INSERT $test VALUES ($N, '$_');\n";
+}
+
+print <<__PREP__;
+ALTER TABLE $test ENABLE KEYS;
+SELECT $N;
+__PREP__
+
+$N=0;
+
+while (<Q>) { chomp;
+ s/'/\\'/g; ++$N;
+ $_="MATCH text AGAINST ('$_' $option)";
+ print "SELECT $N, id, $_ FROM $test WHERE $_;\n";
+}
+
+print <<__FOOTER__;
+DROP TABLE $test;
+__FOOTER__
+
+
diff --git a/storage/myisam/ftbench/Ereport.pl b/storage/myisam/ftbench/Ereport.pl
new file mode 100755
index 00000000000..5969304da09
--- /dev/null
+++ b/storage/myisam/ftbench/Ereport.pl
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+
+die "Use: $0 eval_output qrels_file\n" unless @ARGV==2;
+
+open(EOUT,$eout=shift) || die "Cannot open $eout: $!";
+open(RELJ,$relj=shift) || die "Cannot open $relj: $!";
+
+$_=<EOUT>;
+die "$eout must start with a number!\n "unless /^[1-9][0-9]*\n/;
+$ndocs=$_+0;
+
+$qid=0;
+$relj_str=<RELJ>;
+$eout_str=<EOUT>;
+
+while(!eof(RELJ) || !eof(EOUT)) {
+ ++$qid;
+ %dq=();
+ $A=$B=$AB=0;
+ $Ravg=$Pavg=0;
+
+ while($relj_str =~ /^0*$qid\s+(\d+)/) {
+ ++$A;
+ $dq{$1+0}=1;
+ last unless $relj_str=<RELJ>;
+ }
+ # Favg measure = 1/(a/Pavg+(1-a)/Ravg)
+sub Favg { my $a=shift; $Pavg*$Ravg ? 1/($a/$Pavg+(1-$a)/$Ravg) : 0; }
+ # F0 : a=0 -- ignore precision
+ # F5 : a=0.5
+ # F1 : a=1 -- ignore recall
+ while($eout_str =~ /^$qid\s+(\d+)\s+(\d+(?:\.\d+)?)/) {
+ $B++;
+ $AB++ if $dq{$1+0};
+ $Ravg+=$AB;
+ $Pavg+=$AB/$B;
+ last unless $eout_str=<EOUT>;
+ }
+ next unless $A;
+
+ $Ravg/=$B*$A if $B;
+ $Pavg/=$B if $B;
+
+ printf "%5d %1.12f %1.12f %1.12f\n", $qid, Favg(0),Favg(0.5),Favg(1);
+}
+
+exit 0;
+
+
diff --git a/storage/myisam/ftbench/README b/storage/myisam/ftbench/README
new file mode 100644
index 00000000000..b1f8b66b15f
--- /dev/null
+++ b/storage/myisam/ftbench/README
@@ -0,0 +1,43 @@
+1. should be run from myisam/ftbench/
+2. myisam/ftdefs.h should NOT be locked (bk get, not bk edit!)
+3. there should be ./data/ subdir with test collections, files:
+ test1.d
+ test1.q
+ test1.r
+ test2.d
+ test2.q
+ test2.r
+ where test1, test2, etc - are arbitrary test names
+
+ *.[dq] files contain documents/queries one item per line.
+
+ *.r files have the structure:
+ 1 16 .....blablabla
+ 1 09 .....blablabla
+ 2 116 .....blablabla
+ ...
+
+ that is /^\d+\s+\d+/
+ and are sorted by the first number (not necessarily by the second)
+
+4. there should be ./t/ subdir with test directories
+
+ ./t
+ ./t/BEST/
+ ./t/testdir1/
+ ./t/testdir2/
+ ...
+
+ there *must* be ./t/BEST/ subdir or a symlink to one of other dirs in ./t
+ all other names (besides BEST) can be arbitrary
+
+ all test results are compared with BEST results.
+
+ test directories may contain ftdefs.h, my.cnf, ft_mode
+ (the last one is used as in ... MATCH ... AGAINST ("..." $ft_mode) ...)
+ NOTE: all *.out files in test directories will NOT be overwritten!
+ delete them to re-test
+
+5. run ./ft-test-run.sh
+6. go make some coffee
+
diff --git a/storage/myisam/ftbench/ft-test-run.sh b/storage/myisam/ftbench/ft-test-run.sh
new file mode 100755
index 00000000000..ceba818fa5c
--- /dev/null
+++ b/storage/myisam/ftbench/ft-test-run.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+if [ ! -x ./ft-test-run.sh ] ; then
+ echo "Usage: ./ft-test-run.sh"
+ exit 1
+fi
+
+BASE=`pwd`
+DATA=$BASE/var
+ROOT=`cd ../..; pwd`
+MYSQLD=$ROOT/sql/mysqld
+MYSQL=$ROOT/client/mysql
+MYSQLADMIN=$ROOT/client/mysqladmin
+SOCK=$DATA/mysql.sock
+PID=$DATA/mysql.pid
+H=../ftdefs.h
+OPTS="--no-defaults --socket=$SOCK --character-sets-dir=$ROOT/sql/share/charsets"
+DELAY=10
+
+stop_myslqd()
+{
+ [ -S $SOCK ] && $MYSQLADMIN $OPTS shutdown
+ [ -f $PID ] && kill `cat $PID` && sleep 15 && [ -f $PID ] && kill -9 `cat $PID`
+}
+
+if [ ! -d t/BEST ] ; then
+ echo "No ./t/BEST directory! Aborting..."
+ exit 1
+fi
+rm -f t/BEST/report.txt
+if [ -w $H ] ; then
+ echo "$H is writeable! Aborting..."
+ exit 1
+fi
+
+stop_myslqd
+rm -rf var > /dev/null 2>&1
+mkdir var
+mkdir var/test
+
+for batch in t/* ; do
+ [ ! -d $batch ] && continue
+ [ $batch -ef t/BEST -a $batch != t/BEST ] && continue
+
+ rm -rf var/test/* > /dev/null 2>&1
+ rm -f $H
+ if [ -f $BASE/$batch/ftdefs.h ] ; then
+ cat $BASE/$batch/ftdefs.h > $H
+ chmod a-wx $H
+ else
+ bk get -q $H
+ fi
+ OPTS="--defaults-file=$BASE/$batch/my.cnf --socket=$SOCK --character-sets-dir=$ROOT/sql/share/charsets"
+ stop_myslqd
+ rm -f $MYSQLD
+ echo "building $batch"
+ echo "============== $batch ===============" >> var/ft_test.log
+ (cd $ROOT; gmake) >> var/ft_test.log 2>&1
+
+ for prog in $MYSQLD $MYSQL $MYSQLADMIN ; do
+ if [ ! -x $prog ] ; then
+ echo "build failed: no $prog"
+ exit 1
+ fi
+ done
+
+ echo "=====================================" >> var/ft_test.log
+ $MYSQLD $OPTS --basedir=$BASE --skip-bdb --pid-file=$PID \
+ --language=$ROOT/sql/share/english \
+ --skip-grant-tables --skip-innodb \
+ --skip-networking --tmpdir=$DATA >> var/ft_test.log 2>&1 &
+
+ sleep $DELAY
+ $MYSQLADMIN $OPTS ping
+ if [ $? != 0 ] ; then
+ echo "$MYSQLD refused to start"
+ exit 1
+ fi
+ for test in `cd data; echo *.r|sed "s/\.r//g"` ; do
+ if [ -f $batch/$test.out ] ; then
+ echo "skipping $batch/$test.out"
+ continue
+ fi
+ echo "testing $batch/$test"
+ FT_MODE=`cat $batch/ft_mode 2>/dev/null`
+ ./Ecreate.pl $test "$FT_MODE" | $MYSQL $OPTS --skip-column-names test >var/$test.eval
+ echo "reporting $batch/$test"
+ ./Ereport.pl var/$test.eval data/$test.r > $batch/$test.out || exit
+ done
+ stop_myslqd
+ rm -f $H
+ bk get -q $H
+ if [ ! $batch -ef t/BEST ] ; then
+ echo "comparing $batch"
+ ./Ecompare.pl t/BEST $batch >> t/BEST/report.txt
+ fi
+done
+
diff --git a/storage/myisam/ftdefs.h b/storage/myisam/ftdefs.h
new file mode 100644
index 00000000000..91c679a1e58
--- /dev/null
+++ b/storage/myisam/ftdefs.h
@@ -0,0 +1,152 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+/* some definitions for full-text indices */
+
+#include "fulltext.h"
+#include <m_ctype.h>
+#include <my_tree.h>
+#include <queues.h>
+
+#define true_word_char(s,X) (my_isalnum(s,X) || (X)=='_')
+#define misc_word_char(X) ((X)=='\'')
+#define word_char(s,X) (true_word_char(s,X) || misc_word_char(X))
+
+#define FT_MAX_WORD_LEN_FOR_SORT 31
+
+#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_ft_word {
+ byte * pos;
+ uint len;
+ double weight;
+} FT_WORD;
+
+typedef struct st_ftb_param {
+ byte prev;
+ int yesno;
+ int plusminus;
+ bool pmsign;
+ bool trunc;
+ byte *quot;
+} FTB_PARAM;
+
+int is_stopword(char *word, uint len);
+
+uint _ft_make_key(MI_INFO *, uint , byte *, FT_WORD *, my_off_t);
+
+byte ft_get_word(CHARSET_INFO *, byte **, byte *, FT_WORD *, FTB_PARAM *);
+byte ft_simple_get_word(CHARSET_INFO *, byte **, const byte *,
+ FT_WORD *, my_bool);
+
+typedef struct _st_ft_seg_iterator {
+ uint num, len;
+ HA_KEYSEG *seg;
+ const byte *rec, *pos;
+} FT_SEG_ITERATOR;
+
+void _mi_ft_segiterator_init(MI_INFO *, uint, const byte *, FT_SEG_ITERATOR *);
+void _mi_ft_segiterator_dummy_init(const byte *, uint, FT_SEG_ITERATOR *);
+uint _mi_ft_segiterator(FT_SEG_ITERATOR *);
+
+void ft_parse_init(TREE *, CHARSET_INFO *);
+int ft_parse(TREE *, byte *, int, my_bool);
+FT_WORD * ft_linearize(TREE *);
+FT_WORD * _mi_ft_parserecord(MI_INFO *, uint, const byte *);
+uint _mi_ft_parse(TREE *, MI_INFO *, uint, const byte *, my_bool);
+
+FT_INFO *ft_init_nlq_search(MI_INFO *, uint, byte *, uint, uint, byte *);
+FT_INFO *ft_init_boolean_search(MI_INFO *, uint, byte *, uint, CHARSET_INFO *);
+
+extern const struct _ft_vft _ft_vft_nlq;
+int ft_nlq_read_next(FT_INFO *, char *);
+float ft_nlq_find_relevance(FT_INFO *, byte *, uint);
+void ft_nlq_close_search(FT_INFO *);
+float ft_nlq_get_relevance(FT_INFO *);
+my_off_t ft_nlq_get_docid(FT_INFO *);
+void ft_nlq_reinit_search(FT_INFO *);
+
+extern const struct _ft_vft _ft_vft_boolean;
+int ft_boolean_read_next(FT_INFO *, char *);
+float ft_boolean_find_relevance(FT_INFO *, byte *, uint);
+void ft_boolean_close_search(FT_INFO *);
+float ft_boolean_get_relevance(FT_INFO *);
+my_off_t ft_boolean_get_docid(FT_INFO *);
+void ft_boolean_reinit_search(FT_INFO *);
+
diff --git a/storage/myisam/fulltext.h b/storage/myisam/fulltext.h
new file mode 100644
index 00000000000..d8c74d4e94b
--- /dev/null
+++ b/storage/myisam/fulltext.h
@@ -0,0 +1,38 @@
+/* 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 */
+
+/* Written by Sergei A. Golubchik, who has a shared copyright to this code */
+
+/* some definitions for full-text indices */
+
+#include "myisamdef.h"
+#include "ft_global.h"
+
+#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];
+
+int _mi_ft_cmp(MI_INFO *, uint, const byte *, const byte *);
+int _mi_ft_add(MI_INFO *, uint, byte *, const byte *, my_off_t);
+int _mi_ft_del(MI_INFO *, uint, byte *, const byte *, my_off_t);
+
+uint _mi_ft_convert_to_ft2(MI_INFO *, uint, uchar *);
+
diff --git a/storage/myisam/make-ccc b/storage/myisam/make-ccc
new file mode 100755
index 00000000000..6d1303729db
--- /dev/null
+++ b/storage/myisam/make-ccc
@@ -0,0 +1,5 @@
+rm -f .deps/*.P
+ccc -DMAP_TO_USE_RAID -I./../include -I../include -DDBUG_OFF -fast -O3 -c mi_cache.c mi_changed.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_locking.c mi_log.c mi_open.c mi_packrec.c mi_page.c mi_panic.c mi_range.c mi_rename.c mi_rfirst.c mi_rkey.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 ft_update.c ft_search.o ft_stem.o ft_stopwords.c ft_parser.c
+make sort.o mi_check.o
+rm libmyisam.a
+ar -cr libmyisam.a mi_cache.o sort.o mi_check.o
diff --git a/storage/myisam/mi_cache.c b/storage/myisam/mi_cache.c
new file mode 100644
index 00000000000..8dee068c50e
--- /dev/null
+++ b/storage/myisam/mi_cache.c
@@ -0,0 +1,108 @@
+/* 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 */
+
+/*
+ Functions for read record cacheing with myisam
+ 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 "myisamdef.h"
+
+int _mi_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("_mi_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,MI_BLOCK_INFO_HEADER_LENGTH - in_buff_length -
+ read_length);
+ DBUG_RETURN(0);
+} /* _mi_read_cache */
diff --git a/storage/myisam/mi_changed.c b/storage/myisam/mi_changed.c
new file mode 100644
index 00000000000..c2ab5568eba
--- /dev/null
+++ b/storage/myisam/mi_changed.c
@@ -0,0 +1,34 @@
+/* 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 */
+
+/* Check if somebody has changed table since last check. */
+
+#include "myisamdef.h"
+
+ /* Return 0 if table isn't changed */
+
+int mi_is_changed(MI_INFO *info)
+{
+ int result;
+ DBUG_ENTER("mi_is_changed");
+ if (fast_mi_readinfo(info))
+ DBUG_RETURN(-1);
+ VOID(_mi_writeinfo(info,0));
+ result=(int) info->data_changed;
+ info->data_changed=0;
+ DBUG_PRINT("exit",("result: %d",result));
+ DBUG_RETURN(result);
+}
diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c
new file mode 100644
index 00000000000..dd8cc736741
--- /dev/null
+++ b/storage/myisam/mi_check.c
@@ -0,0 +1,4082 @@
+/* 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 */
+
+/* Describe, check and repair of MyISAM tables */
+
+#include "ftdefs.h"
+#include <m_ctype.h>
+#include <stdarg.h>
+#include <my_getopt.h>
+#ifdef HAVE_SYS_VADVISE_H
+#include <sys/vadvise.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#include "rt_index.h"
+
+#ifndef USE_RAID
+#define my_raid_create(A,B,C,D,E,F,G) my_create(A,B,C,G)
+#define my_raid_delete(A,B,C) my_delete(A,B)
+#endif
+
+ /* Functions defined in this file */
+
+static int check_k_link(MI_CHECK *param, MI_INFO *info,uint nr);
+static int chk_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
+ my_off_t page, uchar *buff, ha_rows *keys,
+ ha_checksum *key_checksum, uint level);
+static uint isam_key_length(MI_INFO *info,MI_KEYDEF *keyinfo);
+static ha_checksum calc_checksum(ha_rows count);
+static int writekeys(MI_CHECK *param, MI_INFO *info,byte *buff,
+ my_off_t filepos);
+static int sort_one_index(MI_CHECK *param, MI_INFO *info,MI_KEYDEF *keyinfo,
+ my_off_t pagepos, File new_file);
+static int sort_key_read(MI_SORT_PARAM *sort_param,void *key);
+static int sort_ft_key_read(MI_SORT_PARAM *sort_param,void *key);
+static int sort_get_next_record(MI_SORT_PARAM *sort_param);
+static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,const void *b);
+static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a);
+static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a);
+static my_off_t get_record_for_key(MI_INFO *info,MI_KEYDEF *keyinfo,
+ uchar *key);
+static int sort_insert_key(MI_SORT_PARAM *sort_param,
+ reg1 SORT_KEY_BLOCKS *key_block,
+ uchar *key, my_off_t prev_block);
+static int sort_delete_record(MI_SORT_PARAM *sort_param);
+/*static int flush_pending_blocks(MI_CHECK *param);*/
+static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint blocks,
+ uint buffer_length);
+static ha_checksum mi_byte_checksum(const byte *buf, uint length);
+static void set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share);
+
+void myisamchk_init(MI_CHECK *param)
+{
+ bzero((gptr) param,sizeof(*param));
+ param->opt_follow_links=1;
+ param->keys_in_use= ~(ulonglong) 0;
+ param->search_after_block=HA_OFFSET_ERROR;
+ param->auto_increment_value= 0;
+ param->use_buffers=USE_BUFFER_INIT;
+ param->read_buffer_length=READ_BUFFER_INIT;
+ param->write_buffer_length=READ_BUFFER_INIT;
+ param->sort_buffer_length=SORT_BUFFER_INIT;
+ param->sort_key_blocks=BUFFERS_WHEN_SORTING;
+ param->tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
+ param->myf_rw=MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL);
+ param->start_check_pos=0;
+ param->max_record_length= LONGLONG_MAX;
+ param->key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
+}
+
+ /* Check the status flags for the table */
+
+int chk_status(MI_CHECK *param, register MI_INFO *info)
+{
+ MYISAM_SHARE *share=info->s;
+
+ if (mi_is_crashed_on_repair(info))
+ mi_check_print_warning(param,
+ "Table is marked as crashed and last repair failed");
+ else if (mi_is_crashed(info))
+ mi_check_print_warning(param,
+ "Table is marked as crashed");
+ if (share->state.open_count != (uint) (info->s->global_changed ? 1 : 0))
+ {
+ /* Don't count this as a real warning, as check can correct this ! */
+ uint save=param->warning_printed;
+ mi_check_print_warning(param,
+ share->state.open_count==1 ?
+ "%d client is using or hasn't closed the table properly" :
+ "%d clients are using or haven't closed the table properly",
+ share->state.open_count);
+ /* If this will be fixed by the check, forget the warning */
+ if (param->testflag & T_UPDATE_STATE)
+ param->warning_printed=save;
+ }
+ return 0;
+}
+
+ /* Check delete links */
+
+int chk_del(MI_CHECK *param, register MI_INFO *info, uint test_flag)
+{
+ reg2 ha_rows i;
+ uint delete_link_length;
+ my_off_t empty,next_link,old_link;
+ char buff[22],buff2[22];
+ DBUG_ENTER("chk_del");
+
+ LINT_INIT(old_link);
+ param->record_checksum=0;
+ delete_link_length=((info->s->options & HA_OPTION_PACK_RECORD) ? 20 :
+ info->s->rec_reflength+1);
+
+ if (!(test_flag & T_SILENT))
+ puts("- check record delete-chain");
+
+ next_link=info->s->state.dellink;
+ if (info->state->del == 0)
+ {
+ if (test_flag & T_VERBOSE)
+ {
+ puts("No recordlinks");
+ }
+ }
+ else
+ {
+ if (test_flag & T_VERBOSE)
+ printf("Recordlinks: ");
+ empty=0;
+ for (i= info->state->del ; i > 0L && next_link != HA_OFFSET_ERROR ; i--)
+ {
+ if (*killed_ptr(param))
+ DBUG_RETURN(1);
+ if (test_flag & T_VERBOSE)
+ printf(" %9s",llstr(next_link,buff));
+ if (next_link >= info->state->data_file_length)
+ goto wrong;
+ if (my_pread(info->dfile,(char*) buff,delete_link_length,
+ next_link,MYF(MY_NABP)))
+ {
+ if (test_flag & T_VERBOSE) puts("");
+ mi_check_print_error(param,"Can't read delete-link at filepos: %s",
+ llstr(next_link,buff));
+ DBUG_RETURN(1);
+ }
+ if (*buff != '\0')
+ {
+ if (test_flag & T_VERBOSE) puts("");
+ mi_check_print_error(param,"Record at pos: %s is not remove-marked",
+ llstr(next_link,buff));
+ goto wrong;
+ }
+ if (info->s->options & HA_OPTION_PACK_RECORD)
+ {
+ my_off_t prev_link=mi_sizekorr(buff+12);
+ if (empty && prev_link != old_link)
+ {
+ if (test_flag & T_VERBOSE) puts("");
+ mi_check_print_error(param,"Deleted block at %s doesn't point back at previous delete link",llstr(next_link,buff2));
+ goto wrong;
+ }
+ old_link=next_link;
+ next_link=mi_sizekorr(buff+4);
+ empty+=mi_uint3korr(buff+1);
+ }
+ else
+ {
+ param->record_checksum+=(ha_checksum) next_link;
+ next_link=_mi_rec_pos(info->s,(uchar*) buff+1);
+ empty+=info->s->base.pack_reclength;
+ }
+ }
+ if (test_flag & T_VERBOSE)
+ puts("\n");
+ if (empty != info->state->empty)
+ {
+ mi_check_print_warning(param,
+ "Found %s deleted space in delete link chain. Should be %s",
+ llstr(empty,buff2),
+ llstr(info->state->empty,buff));
+ }
+ if (next_link != HA_OFFSET_ERROR)
+ {
+ mi_check_print_error(param,
+ "Found more than the expected %s deleted rows in delete link chain",
+ llstr(info->state->del, buff));
+ goto wrong;
+ }
+ if (i != 0)
+ {
+ mi_check_print_error(param,
+ "Found %s deleted rows in delete link chain. Should be %s",
+ llstr(info->state->del - i, buff2),
+ llstr(info->state->del, buff));
+ goto wrong;
+ }
+ }
+ DBUG_RETURN(0);
+
+wrong:
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ if (test_flag & T_VERBOSE) puts("");
+ mi_check_print_error(param,"record delete-link-chain corrupted");
+ DBUG_RETURN(1);
+} /* chk_del */
+
+
+ /* Check delete links in index file */
+
+static int check_k_link(MI_CHECK *param, register MI_INFO *info, uint nr)
+{
+ my_off_t next_link;
+ uint block_size=(nr+1)*MI_MIN_KEY_BLOCK_LENGTH;
+ ha_rows records;
+ char llbuff[21],*buff;
+ DBUG_ENTER("check_k_link");
+
+ if (param->testflag & T_VERBOSE)
+ printf("block_size %4d:",block_size);
+
+ next_link=info->s->state.key_del[nr];
+ records= (ha_rows) (info->state->key_file_length / block_size);
+ while (next_link != HA_OFFSET_ERROR && records > 0)
+ {
+ if (*killed_ptr(param))
+ DBUG_RETURN(1);
+ if (param->testflag & T_VERBOSE)
+ printf("%16s",llstr(next_link,llbuff));
+ if (next_link > info->state->key_file_length ||
+ next_link & (info->s->blocksize-1))
+ DBUG_RETURN(1);
+ if (!(buff=key_cache_read(info->s->key_cache,
+ info->s->kfile, next_link, DFLT_INIT_HITS,
+ (byte*) info->buff,
+ myisam_block_size, block_size, 1)))
+ DBUG_RETURN(1);
+ next_link=mi_sizekorr(buff);
+ records--;
+ param->key_file_blocks+=block_size;
+ }
+ if (param->testflag & T_VERBOSE)
+ {
+ if (next_link != HA_OFFSET_ERROR)
+ printf("%16s\n",llstr(next_link,llbuff));
+ else
+ puts("");
+ }
+ DBUG_RETURN (next_link != HA_OFFSET_ERROR);
+} /* check_k_link */
+
+
+ /* Check sizes of files */
+
+int chk_size(MI_CHECK *param, register MI_INFO *info)
+{
+ int error=0;
+ register my_off_t skr,size;
+ char buff[22],buff2[22];
+ DBUG_ENTER("chk_size");
+
+ if (!(param->testflag & T_SILENT)) puts("- check file-size");
+
+ /* The following is needed if called externally (not from myisamchk) */
+ flush_key_blocks(info->s->key_cache,
+ info->s->kfile, FLUSH_FORCE_WRITE);
+
+ size=my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0));
+ if ((skr=(my_off_t) info->state->key_file_length) != size)
+ {
+ /* Don't give error if file generated by myisampack */
+ if (skr > size && info->s->state.key_map)
+ {
+ error=1;
+ mi_check_print_error(param,
+ "Size of indexfile is: %-8s Should be: %s",
+ llstr(size,buff), llstr(skr,buff2));
+ }
+ else
+ mi_check_print_warning(param,
+ "Size of indexfile is: %-8s Should be: %s",
+ llstr(size,buff), llstr(skr,buff2));
+ }
+ if (!(param->testflag & T_VERY_SILENT) &&
+ ! (info->s->options & HA_OPTION_COMPRESS_RECORD) &&
+ ulonglong2double(info->state->key_file_length) >
+ ulonglong2double(info->s->base.margin_key_file_length)*0.9)
+ mi_check_print_warning(param,"Keyfile is almost full, %10s of %10s used",
+ llstr(info->state->key_file_length,buff),
+ llstr(info->s->base.max_key_file_length-1,buff));
+
+ size=my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
+ skr=(my_off_t) info->state->data_file_length;
+ if (info->s->options & HA_OPTION_COMPRESS_RECORD)
+ skr+= MEMMAP_EXTRA_MARGIN;
+#ifdef USE_RELOC
+ if (info->data_file_type == STATIC_RECORD &&
+ skr < (my_off_t) info->s->base.reloc*info->s->base.min_pack_length)
+ skr=(my_off_t) info->s->base.reloc*info->s->base.min_pack_length;
+#endif
+ if (skr != size)
+ {
+ info->state->data_file_length=size; /* Skip other errors */
+ if (skr > size && skr != size + MEMMAP_EXTRA_MARGIN)
+ {
+ error=1;
+ mi_check_print_error(param,"Size of datafile is: %-9s Should be: %s",
+ llstr(size,buff), llstr(skr,buff2));
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ }
+ else
+ {
+ mi_check_print_warning(param,
+ "Size of datafile is: %-9s Should be: %s",
+ llstr(size,buff), llstr(skr,buff2));
+ }
+ }
+ if (!(param->testflag & T_VERY_SILENT) &&
+ !(info->s->options & HA_OPTION_COMPRESS_RECORD) &&
+ ulonglong2double(info->state->data_file_length) >
+ (ulonglong2double(info->s->base.max_data_file_length)*0.9))
+ mi_check_print_warning(param, "Datafile is almost full, %10s of %10s used",
+ llstr(info->state->data_file_length,buff),
+ llstr(info->s->base.max_data_file_length-1,buff2));
+ DBUG_RETURN(error);
+} /* chk_size */
+
+
+ /* Check keys */
+
+int chk_key(MI_CHECK *param, register MI_INFO *info)
+{
+ uint key,found_keys=0,full_text_keys=0,result=0;
+ ha_rows keys;
+ ha_checksum old_record_checksum,init_checksum;
+ my_off_t all_keydata,all_totaldata,key_totlength,length;
+ ulong *rec_per_key_part;
+ MYISAM_SHARE *share=info->s;
+ MI_KEYDEF *keyinfo;
+ char buff[22],buff2[22];
+ DBUG_ENTER("chk_key");
+
+ if (!(param->testflag & T_SILENT))
+ puts("- check key delete-chain");
+
+ param->key_file_blocks=info->s->base.keystart;
+ for (key=0 ; key < info->s->state.header.max_block_size ; key++)
+ if (check_k_link(param,info,key))
+ {
+ if (param->testflag & T_VERBOSE) puts("");
+ mi_check_print_error(param,"key delete-link-chain corrupted");
+ DBUG_RETURN(-1);
+ }
+
+ if (!(param->testflag & T_SILENT)) puts("- check index reference");
+
+ all_keydata=all_totaldata=key_totlength=0;
+ old_record_checksum=0;
+ init_checksum=param->record_checksum;
+ if (!(share->options &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
+ old_record_checksum=calc_checksum(info->state->records+info->state->del-1)*
+ share->base.pack_reclength;
+ rec_per_key_part= param->rec_per_key_part;
+ for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
+ rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++)
+ {
+ param->key_crc[key]=0;
+ if (!(((ulonglong) 1 << key) & share->state.key_map))
+ {
+ /* Remember old statistics for key */
+ memcpy((char*) rec_per_key_part,
+ (char*) (share->state.rec_per_key_part +
+ (uint) (rec_per_key_part - param->rec_per_key_part)),
+ keyinfo->keysegs*sizeof(*rec_per_key_part));
+ continue;
+ }
+ found_keys++;
+
+ param->record_checksum=init_checksum;
+ bzero((char*) &param->unique_count,sizeof(param->unique_count));
+ if ((!(param->testflag & T_SILENT)))
+ printf ("- check data record references index: %d\n",key+1);
+ if (keyinfo->flag & HA_FULLTEXT)
+ full_text_keys++;
+ if (share->state.key_root[key] == HA_OFFSET_ERROR &&
+ (info->state->records == 0 || keyinfo->flag & HA_FULLTEXT))
+ continue;
+ if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
+ DFLT_INIT_HITS,info->buff,0))
+ {
+ mi_check_print_error(param,"Can't read indexpage from filepos: %s",
+ llstr(share->state.key_root[key],buff));
+ if (!(param->testflag & T_INFO))
+ DBUG_RETURN(-1);
+ result= -1;
+ continue;
+ }
+ param->key_file_blocks+=keyinfo->block_length;
+ keys=0;
+ param->keydata=param->totaldata=0;
+ param->key_blocks=0;
+ param->max_level=0;
+ if (chk_index(param,info,keyinfo,share->state.key_root[key],info->buff,
+ &keys, param->key_crc+key,1))
+ DBUG_RETURN(-1);
+ if(!(keyinfo->flag & (HA_FULLTEXT | HA_SPATIAL)))
+ {
+ if (keys != info->state->records)
+ {
+ mi_check_print_error(param,"Found %s keys of %s",llstr(keys,buff),
+ llstr(info->state->records,buff2));
+ if (!(param->testflag & T_INFO))
+ DBUG_RETURN(-1);
+ result= -1;
+ continue;
+ }
+ if (found_keys - full_text_keys == 1 &&
+ ((share->options &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
+ (param->testflag & T_DONT_CHECK_CHECKSUM)))
+ old_record_checksum=param->record_checksum;
+ else if (old_record_checksum != param->record_checksum)
+ {
+ if (key)
+ mi_check_print_error(param,"Key %u doesn't point at same records that key 1",
+ key+1);
+ else
+ mi_check_print_error(param,"Key 1 doesn't point at all records");
+ if (!(param->testflag & T_INFO))
+ DBUG_RETURN(-1);
+ result= -1;
+ continue;
+ }
+ }
+ if ((uint) share->base.auto_key -1 == key)
+ {
+ /* Check that auto_increment key is bigger than max key value */
+ ulonglong save_auto_value=info->s->state.auto_increment;
+ info->s->state.auto_increment=0;
+ info->lastinx=key;
+ _mi_read_key_record(info, 0L, info->rec_buff);
+ update_auto_increment(info, info->rec_buff);
+ if (info->s->state.auto_increment > save_auto_value)
+ {
+ mi_check_print_warning(param,
+ "Auto-increment value: %s is smaller than max used value: %s",
+ llstr(save_auto_value,buff2),
+ llstr(info->s->state.auto_increment, buff));
+ }
+ if (param->testflag & T_AUTO_INC)
+ {
+ set_if_bigger(info->s->state.auto_increment,
+ param->auto_increment_value);
+ }
+ else
+ info->s->state.auto_increment=save_auto_value;
+
+ /* Check that there isn't a row with auto_increment = 0 in the table */
+ mi_extra(info,HA_EXTRA_KEYREAD,0);
+ bzero(info->lastkey,keyinfo->seg->length);
+ if (!mi_rkey(info, info->rec_buff, key, (const byte*) info->lastkey,
+ keyinfo->seg->length, HA_READ_KEY_EXACT))
+ {
+ /* Don't count this as a real warning, as myisamchk can't correct it */
+ uint save=param->warning_printed;
+ mi_check_print_warning(param,
+ "Found row where the auto_increment column has the value 0");
+ param->warning_printed=save;
+ }
+ mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
+ }
+
+ length=(my_off_t) isam_key_length(info,keyinfo)*keys + param->key_blocks*2;
+ if (param->testflag & T_INFO && param->totaldata != 0L && keys != 0L)
+ printf("Key: %2d: Keyblocks used: %3d%% Packed: %4d%% Max levels: %2d\n",
+ key+1,
+ (int) (my_off_t2double(param->keydata)*100.0/my_off_t2double(param->totaldata)),
+ (int) ((my_off_t2double(length) - my_off_t2double(param->keydata))*100.0/
+ my_off_t2double(length)),
+ param->max_level);
+ all_keydata+=param->keydata; all_totaldata+=param->totaldata; key_totlength+=length;
+
+ if (param->testflag & T_STATISTICS)
+ update_key_parts(keyinfo, rec_per_key_part, param->unique_count,
+ (ulonglong) info->state->records);
+ }
+ if (param->testflag & T_INFO)
+ {
+ if (all_totaldata != 0L && found_keys > 0)
+ printf("Total: Keyblocks used: %3d%% Packed: %4d%%\n\n",
+ (int) (my_off_t2double(all_keydata)*100.0/
+ my_off_t2double(all_totaldata)),
+ (int) ((my_off_t2double(key_totlength) -
+ my_off_t2double(all_keydata))*100.0/
+ my_off_t2double(key_totlength)));
+ else if (all_totaldata != 0L && share->state.key_map)
+ puts("");
+ }
+ if (param->key_file_blocks != info->state->key_file_length &&
+ param->keys_in_use != ~(ulonglong) 0)
+ mi_check_print_warning(param, "Some data are unreferenced in keyfile");
+ if (found_keys != full_text_keys)
+ param->record_checksum=old_record_checksum-init_checksum; /* Remove delete links */
+ else
+ param->record_checksum=0;
+ DBUG_RETURN(result);
+} /* chk_key */
+
+
+static int chk_index_down(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
+ my_off_t page, uchar *buff, ha_rows *keys,
+ ha_checksum *key_checksum, uint level)
+{
+ char llbuff[22],llbuff2[22];
+ if (page > info->state->key_file_length || (page & (info->s->blocksize -1)))
+ {
+ my_off_t max_length=my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0));
+ mi_check_print_error(param,"Wrong pagepointer: %s at page: %s",
+ llstr(page,llbuff),llstr(page,llbuff2));
+
+ if (page+info->s->blocksize > max_length)
+ goto err;
+ info->state->key_file_length=(max_length &
+ ~ (my_off_t) (info->s->blocksize-1));
+ }
+ if (!_mi_fetch_keypage(info,keyinfo,page, DFLT_INIT_HITS,buff,0))
+ {
+ mi_check_print_error(param,"Can't read key from filepos: %s",
+ llstr(page,llbuff));
+ goto err;
+ }
+ param->key_file_blocks+=keyinfo->block_length;
+ if (chk_index(param,info,keyinfo,page,buff,keys,key_checksum,level))
+ goto err;
+
+ return 0;
+err:
+ return 1;
+}
+
+ /* Check if index is ok */
+
+static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
+ my_off_t page, uchar *buff, ha_rows *keys,
+ ha_checksum *key_checksum, uint level)
+{
+ int flag;
+ uint used_length,comp_flag,nod_flag,key_length=0,not_used;
+ uchar key[MI_MAX_POSSIBLE_KEY_BUFF],*temp_buff,*keypos,*old_keypos,*endpos;
+ my_off_t next_page,record;
+ char llbuff[22];
+ DBUG_ENTER("chk_index");
+ DBUG_DUMP("buff",(byte*) buff,mi_getint(buff));
+
+ /* TODO: implement appropriate check for RTree keys */
+ if (keyinfo->flag & HA_SPATIAL)
+ DBUG_RETURN(0);
+
+ if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
+ {
+ mi_check_print_error(param,"Not enough memory for keyblock");
+ DBUG_RETURN(-1);
+ }
+
+ if (keyinfo->flag & HA_NOSAME)
+ comp_flag=SEARCH_FIND | SEARCH_UPDATE; /* Not real duplicates */
+ else
+ comp_flag=SEARCH_SAME; /* Keys in positionorder */
+ nod_flag=mi_test_if_nod(buff);
+ used_length=mi_getint(buff);
+ keypos=buff+2+nod_flag;
+ endpos=buff+used_length;
+
+ param->keydata+=used_length; param->totaldata+=keyinfo->block_length; /* INFO */
+ param->key_blocks++;
+ if (level > param->max_level)
+ param->max_level=level;
+
+ if (used_length > keyinfo->block_length)
+ {
+ mi_check_print_error(param,"Wrong pageinfo at page: %s",
+ llstr(page,llbuff));
+ goto err;
+ }
+ for ( ;; )
+ {
+ if (*killed_ptr(param))
+ goto err;
+ memcpy((char*) info->lastkey,(char*) key,key_length);
+ info->lastkey_length=key_length;
+ if (nod_flag)
+ {
+ next_page=_mi_kpos(nod_flag,keypos);
+ if (chk_index_down(param,info,keyinfo,next_page,
+ temp_buff,keys,key_checksum,level+1))
+ goto err;
+ }
+ old_keypos=keypos;
+ if (keypos >= endpos ||
+ (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
+ break;
+ if (keypos > endpos)
+ {
+ mi_check_print_error(param,"Wrong key block length at page: %s",llstr(page,llbuff));
+ goto err;
+ }
+ if ((*keys)++ &&
+ (flag=ha_key_cmp(keyinfo->seg,info->lastkey,key,key_length,
+ comp_flag, &not_used)) >=0)
+ {
+ DBUG_DUMP("old",(byte*) info->lastkey, info->lastkey_length);
+ DBUG_DUMP("new",(byte*) key, key_length);
+ DBUG_DUMP("new_in_page",(char*) old_keypos,(uint) (keypos-old_keypos));
+
+ if (comp_flag & SEARCH_FIND && flag == 0)
+ mi_check_print_error(param,"Found duplicated key at page %s",llstr(page,llbuff));
+ else
+ mi_check_print_error(param,"Key in wrong position at page %s",llstr(page,llbuff));
+ goto err;
+ }
+ if (param->testflag & T_STATISTICS)
+ {
+ if (*keys != 1L) /* not first_key */
+ {
+ uint diff;
+ ha_key_cmp(keyinfo->seg,info->lastkey,key,USE_WHOLE_KEY,
+ SEARCH_FIND | SEARCH_NULL_ARE_NOT_EQUAL,
+ &diff);
+ param->unique_count[diff-1]++;
+ }
+ }
+ (*key_checksum)+= mi_byte_checksum((byte*) key,
+ key_length- info->s->rec_reflength);
+ record= _mi_dpos(info,0,key+key_length);
+ if (keyinfo->flag & HA_FULLTEXT) /* special handling for ft2 */
+ {
+ uint off;
+ int subkeys;
+ get_key_full_length_rdonly(off, key);
+ subkeys=ft_sintXkorr(key+off);
+ if (subkeys < 0)
+ {
+ ha_rows tmp_keys=0;
+ if (chk_index_down(param,info,&info->s->ft2_keyinfo,record,
+ temp_buff,&tmp_keys,key_checksum,1))
+ goto err;
+ if (tmp_keys + subkeys)
+ {
+ mi_check_print_error(param,
+ "Number of words in the 2nd level tree "
+ "does not match the number in the header. "
+ "Parent word in on the page %s, offset %u",
+ llstr(page,llbuff), (uint) (old_keypos-buff));
+ goto err;
+ }
+ (*keys)+=tmp_keys-1;
+ continue;
+ }
+ /* fall through */
+ }
+ if (record >= info->state->data_file_length)
+ {
+#ifndef DBUG_OFF
+ char llbuff2[22], llbuff3[22];
+#endif
+ mi_check_print_error(param,"Found key at page %s that points to record outside datafile",llstr(page,llbuff));
+ DBUG_PRINT("test",("page: %s record: %s filelength: %s",
+ llstr(page,llbuff),llstr(record,llbuff2),
+ llstr(info->state->data_file_length,llbuff3)));
+ DBUG_DUMP("key",(byte*) key,key_length);
+ DBUG_DUMP("new_in_page",(char*) old_keypos,(uint) (keypos-old_keypos));
+ goto err;
+ }
+ param->record_checksum+=(ha_checksum) record;
+ }
+ if (keypos != endpos)
+ {
+ mi_check_print_error(param,"Keyblock size at page %s is not correct. Block length: %d key length: %d",
+ llstr(page,llbuff), used_length, (keypos - buff));
+ goto err;
+ }
+ my_afree((byte*) temp_buff);
+ DBUG_RETURN(0);
+ err:
+ my_afree((byte*) temp_buff);
+ DBUG_RETURN(1);
+} /* chk_index */
+
+
+ /* Calculate a checksum of 1+2+3+4...N = N*(N+1)/2 without overflow */
+
+static ha_checksum calc_checksum(ha_rows count)
+{
+ ulonglong sum,a,b;
+ DBUG_ENTER("calc_checksum");
+
+ sum=0;
+ a=count; b=count+1;
+ if (a & 1)
+ b>>=1;
+ else
+ a>>=1;
+ while (b)
+ {
+ if (b & 1)
+ sum+=a;
+ a<<=1; b>>=1;
+ }
+ DBUG_PRINT("exit",("sum: %lx",(ulong) sum));
+ DBUG_RETURN((ha_checksum) sum);
+} /* calc_checksum */
+
+
+ /* Calc length of key in normal isam */
+
+static uint isam_key_length(MI_INFO *info, register MI_KEYDEF *keyinfo)
+{
+ uint length;
+ HA_KEYSEG *keyseg;
+ DBUG_ENTER("isam_key_length");
+
+ length= info->s->rec_reflength;
+ for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++)
+ length+= keyseg->length;
+
+ DBUG_PRINT("exit",("length: %d",length));
+ DBUG_RETURN(length);
+} /* key_length */
+
+
+ /* Check that record-link is ok */
+
+int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend)
+{
+ int error,got_error,flag;
+ uint key,left_length,b_type,field;
+ ha_rows records,del_blocks;
+ my_off_t used,empty,pos,splits,start_recpos,
+ del_length,link_used,start_block;
+ byte *record,*to;
+ char llbuff[22],llbuff2[22],llbuff3[22];
+ ha_checksum intern_record_checksum;
+ ha_checksum key_checksum[MI_MAX_POSSIBLE_KEY];
+ my_bool static_row_size;
+ MI_KEYDEF *keyinfo;
+ MI_BLOCK_INFO block_info;
+ DBUG_ENTER("chk_data_link");
+
+ if (!(param->testflag & T_SILENT))
+ {
+ if (extend)
+ puts("- check records and index references");
+ else
+ puts("- check record links");
+ }
+
+ if (!(record= (byte*) my_malloc(info->s->base.pack_reclength,MYF(0))))
+ {
+ mi_check_print_error(param,"Not enough memory for record");
+ DBUG_RETURN(-1);
+ }
+ records=del_blocks=0;
+ used=link_used=splits=del_length=0;
+ intern_record_checksum=param->glob_crc=0;
+ LINT_INIT(left_length); LINT_INIT(start_recpos); LINT_INIT(to);
+ got_error=error=0;
+ empty=info->s->pack.header_length;
+
+ /* Check how to calculate checksum of rows */
+ static_row_size=1;
+ if (info->s->data_file_type == COMPRESSED_RECORD)
+ {
+ for (field=0 ; field < info->s->base.fields ; field++)
+ {
+ if (info->s->rec[field].base_type == FIELD_BLOB ||
+ info->s->rec[field].base_type == FIELD_VARCHAR)
+ {
+ static_row_size=0;
+ break;
+ }
+ }
+ }
+
+ pos=my_b_tell(&param->read_cache);
+ bzero((char*) key_checksum, info->s->base.keys * sizeof(key_checksum[0]));
+ while (pos < info->state->data_file_length)
+ {
+ if (*killed_ptr(param))
+ goto err2;
+ switch (info->s->data_file_type) {
+ case STATIC_RECORD:
+ if (my_b_read(&param->read_cache,(byte*) record,
+ info->s->base.pack_reclength))
+ goto err;
+ start_recpos=pos;
+ pos+=info->s->base.pack_reclength;
+ splits++;
+ if (*record == '\0')
+ {
+ del_blocks++;
+ del_length+=info->s->base.pack_reclength;
+ continue; /* Record removed */
+ }
+ param->glob_crc+= mi_static_checksum(info,record);
+ used+=info->s->base.pack_reclength;
+ break;
+ case DYNAMIC_RECORD:
+ flag=block_info.second_read=0;
+ block_info.next_filepos=pos;
+ do
+ {
+ if (_mi_read_cache(&param->read_cache,(byte*) block_info.header,
+ (start_block=block_info.next_filepos),
+ sizeof(block_info.header),
+ (flag ? 0 : READING_NEXT) | READING_HEADER))
+ goto err;
+ if (start_block & (MI_DYN_ALIGN_SIZE-1))
+ {
+ mi_check_print_error(param,"Wrong aligned block at %s",
+ llstr(start_block,llbuff));
+ goto err2;
+ }
+ b_type=_mi_get_block_info(&block_info,-1,start_block);
+ if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
+ BLOCK_FATAL_ERROR))
+ {
+ if (b_type & BLOCK_SYNC_ERROR)
+ {
+ if (flag)
+ {
+ mi_check_print_error(param,"Unexpected byte: %d at link: %s",
+ (int) block_info.header[0],
+ llstr(start_block,llbuff));
+ goto err2;
+ }
+ pos=block_info.filepos+block_info.block_len;
+ goto next;
+ }
+ if (b_type & BLOCK_DELETED)
+ {
+ if (block_info.block_len < info->s->base.min_block_length)
+ {
+ mi_check_print_error(param,
+ "Deleted block with impossible length %lu at %s",
+ block_info.block_len,llstr(pos,llbuff));
+ goto err2;
+ }
+ if ((block_info.next_filepos != HA_OFFSET_ERROR &&
+ block_info.next_filepos >= info->state->data_file_length) ||
+ (block_info.prev_filepos != HA_OFFSET_ERROR &&
+ block_info.prev_filepos >= info->state->data_file_length))
+ {
+ mi_check_print_error(param,"Delete link points outside datafile at %s",
+ llstr(pos,llbuff));
+ goto err2;
+ }
+ del_blocks++;
+ del_length+=block_info.block_len;
+ pos=block_info.filepos+block_info.block_len;
+ splits++;
+ goto next;
+ }
+ mi_check_print_error(param,"Wrong bytesec: %d-%d-%d at linkstart: %s",
+ block_info.header[0],block_info.header[1],
+ block_info.header[2],
+ llstr(start_block,llbuff));
+ goto err2;
+ }
+ if (info->state->data_file_length < block_info.filepos+
+ block_info.block_len)
+ {
+ mi_check_print_error(param,
+ "Recordlink that points outside datafile at %s",
+ llstr(pos,llbuff));
+ got_error=1;
+ break;
+ }
+ splits++;
+ if (!flag++) /* First block */
+ {
+ start_recpos=pos;
+ pos=block_info.filepos+block_info.block_len;
+ if (block_info.rec_len > (uint) info->s->base.max_pack_length)
+ {
+ mi_check_print_error(param,"Found too long record (%lu) at %s",
+ (ulong) block_info.rec_len,
+ llstr(start_recpos,llbuff));
+ got_error=1;
+ break;
+ }
+ if (info->s->base.blobs)
+ {
+ if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
+ &info->rec_buff)))
+ {
+ mi_check_print_error(param,
+ "Not enough memory (%lu) for blob at %s",
+ (ulong) block_info.rec_len,
+ llstr(start_recpos,llbuff));
+ got_error=1;
+ break;
+ }
+ }
+ else
+ to= info->rec_buff;
+ left_length=block_info.rec_len;
+ }
+ if (left_length < block_info.data_len)
+ {
+ mi_check_print_error(param,"Found too long record (%lu) at %s",
+ (ulong) block_info.data_len,
+ llstr(start_recpos,llbuff));
+ got_error=1;
+ break;
+ }
+ if (_mi_read_cache(&param->read_cache,(byte*) to,block_info.filepos,
+ (uint) block_info.data_len,
+ flag == 1 ? READING_NEXT : 0))
+ goto err;
+ to+=block_info.data_len;
+ link_used+= block_info.filepos-start_block;
+ used+= block_info.filepos - start_block + block_info.data_len;
+ empty+=block_info.block_len-block_info.data_len;
+ left_length-=block_info.data_len;
+ if (left_length)
+ {
+ if (b_type & BLOCK_LAST)
+ {
+ mi_check_print_error(param,
+ "Wrong record length %s of %s at %s",
+ llstr(block_info.rec_len-left_length,llbuff),
+ llstr(block_info.rec_len, llbuff2),
+ llstr(start_recpos,llbuff3));
+ got_error=1;
+ break;
+ }
+ if (info->state->data_file_length < block_info.next_filepos)
+ {
+ mi_check_print_error(param,
+ "Found next-recordlink that points outside datafile at %s",
+ llstr(block_info.filepos,llbuff));
+ got_error=1;
+ break;
+ }
+ }
+ } while (left_length);
+ if (! got_error)
+ {
+ if (_mi_rec_unpack(info,record,info->rec_buff,block_info.rec_len) ==
+ MY_FILE_ERROR)
+ {
+ mi_check_print_error(param,"Found wrong record at %s",
+ llstr(start_recpos,llbuff));
+ got_error=1;
+ }
+ else
+ {
+ info->checksum=mi_checksum(info,record);
+ if (param->testflag & (T_EXTEND | T_MEDIUM | T_VERBOSE))
+ {
+ if (_mi_rec_check(info,record, info->rec_buff,block_info.rec_len))
+ {
+ mi_check_print_error(param,"Found wrong packed record at %s",
+ llstr(start_recpos,llbuff));
+ got_error=1;
+ }
+ }
+ if (!got_error)
+ param->glob_crc+= info->checksum;
+ }
+ }
+ else if (!flag)
+ pos=block_info.filepos+block_info.block_len;
+ break;
+ case COMPRESSED_RECORD:
+ if (_mi_read_cache(&param->read_cache,(byte*) block_info.header, pos,
+ info->s->pack.ref_length, READING_NEXT))
+ goto err;
+ start_recpos=pos;
+ splits++;
+ VOID(_mi_pack_get_block_info(info,&block_info, -1, start_recpos));
+ pos=block_info.filepos+block_info.rec_len;
+ if (block_info.rec_len < (uint) info->s->min_pack_length ||
+ block_info.rec_len > (uint) info->s->max_pack_length)
+ {
+ mi_check_print_error(param,
+ "Found block with wrong recordlength: %d at %s",
+ block_info.rec_len, llstr(start_recpos,llbuff));
+ got_error=1;
+ break;
+ }
+ if (_mi_read_cache(&param->read_cache,(byte*) info->rec_buff,
+ block_info.filepos, block_info.rec_len, READING_NEXT))
+ goto err;
+ if (_mi_pack_rec_unpack(info,record,info->rec_buff,block_info.rec_len))
+ {
+ mi_check_print_error(param,"Found wrong record at %s",
+ llstr(start_recpos,llbuff));
+ got_error=1;
+ }
+ if (static_row_size)
+ param->glob_crc+= mi_static_checksum(info,record);
+ else
+ param->glob_crc+= mi_checksum(info,record);
+ link_used+= (block_info.filepos - start_recpos);
+ used+= (pos-start_recpos);
+ } /* switch */
+ if (! got_error)
+ {
+ intern_record_checksum+=(ha_checksum) start_recpos;
+ records++;
+ if (param->testflag & T_WRITE_LOOP && records % WRITE_COUNT == 0)
+ {
+ printf("%s\r", llstr(records,llbuff)); VOID(fflush(stdout));
+ }
+
+ /* Check if keys match the record */
+
+ for (key=0,keyinfo= info->s->keyinfo; key < info->s->base.keys;
+ key++,keyinfo++)
+ {
+ if ((((ulonglong) 1 << key) & info->s->state.key_map))
+ {
+ if(!(keyinfo->flag & HA_FULLTEXT))
+ {
+ uint key_length=_mi_make_key(info,key,info->lastkey,record,
+ start_recpos);
+ if (extend)
+ {
+ /* We don't need to lock the key tree here as we don't allow
+ concurrent threads when running myisamchk
+ */
+ if (_mi_search(info,keyinfo,info->lastkey,key_length,
+ SEARCH_SAME, info->s->state.key_root[key]))
+ {
+ mi_check_print_error(param,"Record at: %10s Can't find key for index: %2d",
+ llstr(start_recpos,llbuff),key+1);
+ if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
+ goto err2;
+ }
+ }
+ else
+ key_checksum[key]+=mi_byte_checksum((byte*) info->lastkey,
+ key_length);
+ }
+ }
+ }
+ }
+ else
+ {
+ got_error=0;
+ if (error++ > MAXERR || !(param->testflag & T_VERBOSE))
+ goto err2;
+ }
+ next:; /* Next record */
+ }
+ if (param->testflag & T_WRITE_LOOP)
+ {
+ VOID(fputs(" \r",stdout)); VOID(fflush(stdout));
+ }
+ if (records != info->state->records)
+ {
+ mi_check_print_error(param,"Record-count is not ok; is %-10s Should be: %s",
+ llstr(records,llbuff), llstr(info->state->records,llbuff2));
+ error=1;
+ }
+ else if (param->record_checksum &&
+ param->record_checksum != intern_record_checksum)
+ {
+ mi_check_print_error(param,
+ "Keypointers and record positions doesn't match");
+ error=1;
+ }
+ else if (param->glob_crc != info->s->state.checksum &&
+ (info->s->options &
+ (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)))
+ {
+ mi_check_print_warning(param,
+ "Record checksum is not the same as checksum stored in the index file\n");
+ error=1;
+ }
+ else if (!extend)
+ {
+ for (key=0 ; key < info->s->base.keys; key++)
+ {
+ if (key_checksum[key] != param->key_crc[key] &&
+ !(info->s->keyinfo[key].flag & (HA_FULLTEXT | HA_SPATIAL)))
+ {
+ mi_check_print_error(param,"Checksum for key: %2d doesn't match checksum for records",
+ key+1);
+ error=1;
+ }
+ }
+ }
+
+ if (del_length != info->state->empty)
+ {
+ mi_check_print_warning(param,
+ "Found %s deleted space. Should be %s",
+ llstr(del_length,llbuff2),
+ llstr(info->state->empty,llbuff));
+ }
+ if (used+empty+del_length != info->state->data_file_length)
+ {
+ mi_check_print_warning(param,
+ "Found %s record-data and %s unused data and %s deleted-data",
+ llstr(used,llbuff),llstr(empty,llbuff2),
+ llstr(del_length,llbuff3));
+ mi_check_print_warning(param,
+ "Total %s, Should be: %s",
+ llstr((used+empty+del_length),llbuff),
+ llstr(info->state->data_file_length,llbuff2));
+ }
+ if (del_blocks != info->state->del)
+ {
+ mi_check_print_warning(param,
+ "Found %10s deleted blocks Should be: %s",
+ llstr(del_blocks,llbuff),
+ llstr(info->state->del,llbuff2));
+ }
+ if (splits != info->s->state.split)
+ {
+ mi_check_print_warning(param,
+ "Found %10s parts Should be: %s parts",
+ llstr(splits,llbuff),
+ llstr(info->s->state.split,llbuff2));
+ }
+ if (param->testflag & T_INFO)
+ {
+ if (param->warning_printed || param->error_printed)
+ puts("");
+ if (used != 0 && ! param->error_printed)
+ {
+ printf("Records:%18s M.recordlength:%9lu Packed:%14.0f%%\n",
+ llstr(records,llbuff), (long)((used-link_used)/records),
+ (info->s->base.blobs ? 0.0 :
+ (ulonglong2double((ulonglong) info->s->base.reclength*records)-
+ my_off_t2double(used))/
+ ulonglong2double((ulonglong) info->s->base.reclength*records)*100.0));
+ printf("Recordspace used:%9.0f%% Empty space:%12d%% Blocks/Record: %6.2f\n",
+ (ulonglong2double(used-link_used)/ulonglong2double(used-link_used+empty)*100.0),
+ (!records ? 100 : (int) (ulonglong2double(del_length+empty)/
+ my_off_t2double(used)*100.0)),
+ ulonglong2double(splits - del_blocks) / records);
+ }
+ printf("Record blocks:%12s Delete blocks:%10s\n",
+ llstr(splits-del_blocks,llbuff),llstr(del_blocks,llbuff2));
+ printf("Record data: %12s Deleted data: %10s\n",
+ llstr(used-link_used,llbuff),llstr(del_length,llbuff2));
+ printf("Lost space: %12s Linkdata: %10s\n",
+ llstr(empty,llbuff),llstr(link_used,llbuff2));
+ }
+ my_free((gptr) record,MYF(0));
+ DBUG_RETURN (error);
+ err:
+ mi_check_print_error(param,"got error: %d when reading datafile at record: %s",my_errno, llstr(records,llbuff));
+ err2:
+ my_free((gptr) record,MYF(0));
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ DBUG_RETURN(1);
+} /* chk_data_link */
+
+
+ /* Recover old table by reading each record and writing all keys */
+ /* Save new datafile-name in temp_filename */
+
+int mi_repair(MI_CHECK *param, register MI_INFO *info,
+ my_string name, int rep_quick)
+{
+ int error,got_error;
+ uint i;
+ ha_rows start_records,new_header_length;
+ my_off_t del;
+ File new_file;
+ MYISAM_SHARE *share=info->s;
+ char llbuff[22],llbuff2[22];
+ SORT_INFO sort_info;
+ MI_SORT_PARAM sort_param;
+ DBUG_ENTER("mi_repair");
+
+ bzero((char *)&sort_info, sizeof(sort_info));
+ bzero((char *)&sort_param, sizeof(sort_param));
+ start_records=info->state->records;
+ new_header_length=(param->testflag & T_UNPACK) ? 0L :
+ share->pack.header_length;
+ got_error=1;
+ new_file= -1;
+ sort_param.sort_info=&sort_info;
+
+ if (!(param->testflag & T_SILENT))
+ {
+ printf("- recovering (with keycache) MyISAM-table '%s'\n",name);
+ printf("Data records: %s\n", llstr(info->state->records,llbuff));
+ }
+ param->testflag|=T_REP; /* for easy checking */
+
+ if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
+ param->testflag|=T_CALC_CHECKSUM;
+
+ if (!param->using_global_keycache)
+ VOID(init_key_cache(dflt_key_cache, param->key_cache_block_size,
+ param->use_buffers, 0, 0));
+
+ if (init_io_cache(&param->read_cache,info->dfile,
+ (uint) param->read_buffer_length,
+ READ_CACHE,share->pack.header_length,1,MYF(MY_WME)))
+ {
+ bzero(&info->rec_cache,sizeof(info->rec_cache));
+ goto err;
+ }
+ if (!rep_quick)
+ if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
+ WRITE_CACHE, new_header_length, 1,
+ MYF(MY_WME | MY_WAIT_IF_FULL)))
+ goto err;
+ info->opt_flag|=WRITE_CACHE_USED;
+ if (!(sort_param.record=(byte*) my_malloc((uint) share->base.pack_reclength,
+ MYF(0))) ||
+ !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
+ {
+ mi_check_print_error(param, "Not enough memory for extra record");
+ goto err;
+ }
+
+ if (!rep_quick)
+ {
+ /* Get real path for data file */
+ if ((new_file=my_raid_create(fn_format(param->temp_filename,
+ share->data_file_name, "",
+ DATA_TMP_EXT, 2+4),
+ 0,param->tmpfile_createflag,
+ share->base.raid_type,
+ share->base.raid_chunks,
+ share->base.raid_chunksize,
+ MYF(0))) < 0)
+ {
+ mi_check_print_error(param,"Can't create new tempfile: '%s'",
+ param->temp_filename);
+ goto err;
+ }
+ if (filecopy(param,new_file,info->dfile,0L,new_header_length,
+ "datafile-header"))
+ goto err;
+ info->s->state.dellink= HA_OFFSET_ERROR;
+ info->rec_cache.file=new_file;
+ if (param->testflag & T_UNPACK)
+ {
+ share->options&= ~HA_OPTION_COMPRESS_RECORD;
+ mi_int2store(share->state.header.options,share->options);
+ }
+ }
+ sort_info.info=info;
+ sort_info.param = param;
+ sort_param.read_cache=param->read_cache;
+ sort_param.pos=sort_param.max_pos=share->pack.header_length;
+ sort_param.filepos=new_header_length;
+ param->read_cache.end_of_file=sort_info.filelength=
+ my_seek(info->dfile,0L,MY_SEEK_END,MYF(0));
+ sort_info.dupp=0;
+ sort_param.fix_datafile= (my_bool) (! rep_quick);
+ sort_param.master=1;
+ sort_info.max_records= ~(ha_rows) 0;
+
+ set_data_file_type(&sort_info, share);
+ del=info->state->del;
+ info->state->records=info->state->del=share->state.split=0;
+ info->state->empty=0;
+ param->glob_crc=0;
+ if (param->testflag & T_CALC_CHECKSUM)
+ param->calc_checksum=1;
+
+ info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
+ for (i=0 ; i < info->s->base.keys ; i++)
+ share->state.key_root[i]= HA_OFFSET_ERROR;
+ for (i=0 ; i < share->state.header.max_block_size ; i++)
+ share->state.key_del[i]= HA_OFFSET_ERROR;
+
+ /*
+ I think mi_repair and mi_repair_by_sort should do the same
+ (according, e.g. to ha_myisam::repair), but as mi_repair doesn't
+ touch key_map it cannot be used to T_CREATE_MISSING_KEYS.
+ That is what the next line is for
+ */
+
+ if (param->testflag & T_CREATE_MISSING_KEYS)
+ share->state.key_map= ((((ulonglong) 1L << share->base.keys)-1) &
+ param->keys_in_use);
+
+ info->state->key_file_length=share->base.keystart;
+
+ lock_memory(param); /* Everything is alloced */
+ while (!(error=sort_get_next_record(&sort_param)))
+ {
+ if (writekeys(param,info,(byte*)sort_param.record,sort_param.filepos))
+ {
+ if (my_errno != HA_ERR_FOUND_DUPP_KEY)
+ goto err;
+ DBUG_DUMP("record",(byte*) sort_param.record,share->base.pack_reclength);
+ mi_check_print_info(param,"Duplicate key %2d for record at %10s against new record at %10s",
+ info->errkey+1,
+ llstr(sort_param.start_recpos,llbuff),
+ llstr(info->dupp_key_pos,llbuff2));
+ if (param->testflag & T_VERBOSE)
+ {
+ VOID(_mi_make_key(info,(uint) info->errkey,info->lastkey,
+ sort_param.record,0L));
+ _mi_print_key(stdout,share->keyinfo[info->errkey].seg,info->lastkey,
+ USE_WHOLE_KEY);
+ }
+ sort_info.dupp++;
+ if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
+ {
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ param->error_printed=1;
+ goto err;
+ }
+ continue;
+ }
+ if (sort_write_record(&sort_param))
+ goto err;
+ }
+ if (error > 0 || write_data_suffix(&sort_info, (my_bool)!rep_quick) ||
+ flush_io_cache(&info->rec_cache) || param->read_cache.error < 0)
+ goto err;
+
+ if (param->testflag & T_WRITE_LOOP)
+ {
+ VOID(fputs(" \r",stdout)); VOID(fflush(stdout));
+ }
+ if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0)))
+ {
+ mi_check_print_warning(param,
+ "Can't change size of indexfile, error: %d",
+ my_errno);
+ goto err;
+ }
+
+ if (rep_quick && del+sort_info.dupp != info->state->del)
+ {
+ mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
+ mi_check_print_error(param,"Run recovery again without -q");
+ got_error=1;
+ param->retry_repair=1;
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ goto err;
+ }
+ if (param->testflag & T_SAFE_REPAIR)
+ {
+ /* Don't repair if we loosed more than one row */
+ if (info->state->records+1 < start_records)
+ {
+ info->state->records=start_records;
+ got_error=1;
+ goto err;
+ }
+ }
+
+ if (!rep_quick)
+ {
+ my_close(info->dfile,MYF(0));
+ info->dfile=new_file;
+ info->state->data_file_length=sort_param.filepos;
+ share->state.version=(ulong) time((time_t*) 0); /* Force reopen */
+ }
+ else
+ {
+ info->state->data_file_length=sort_param.max_pos;
+ }
+ if (param->testflag & T_CALC_CHECKSUM)
+ share->state.checksum=param->glob_crc;
+
+ if (!(param->testflag & T_SILENT))
+ {
+ if (start_records != info->state->records)
+ printf("Data records: %s\n", llstr(info->state->records,llbuff));
+ if (sort_info.dupp)
+ mi_check_print_warning(param,
+ "%s records have been removed",
+ llstr(sort_info.dupp,llbuff));
+ }
+
+ got_error=0;
+ /* If invoked by external program that uses thr_lock */
+ if (&share->state.state != info->state)
+ memcpy( &share->state.state, info->state, sizeof(*info->state));
+
+err:
+ if (!got_error)
+ {
+ /* Replace the actual file with the temporary file */
+ if (new_file >= 0)
+ {
+ my_close(new_file,MYF(0));
+ info->dfile=new_file= -1;
+ if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
+ DATA_TMP_EXT, share->base.raid_chunks,
+ (param->testflag & T_BACKUP_DATA ?
+ MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
+ mi_open_datafile(info,share,-1))
+ got_error=1;
+ }
+ }
+ if (got_error)
+ {
+ if (! param->error_printed)
+ mi_check_print_error(param,"%d for record at pos %s",my_errno,
+ llstr(sort_param.start_recpos,llbuff));
+ if (new_file >= 0)
+ {
+ VOID(my_close(new_file,MYF(0)));
+ VOID(my_raid_delete(param->temp_filename,info->s->base.raid_chunks,
+ MYF(MY_WME)));
+ info->rec_cache.file=-1; /* don't flush data to new_file, it's closed */
+ }
+ mi_mark_crashed_on_repair(info);
+ }
+ my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff),
+ MYF(MY_ALLOW_ZERO_PTR));
+ my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
+ VOID(end_io_cache(&param->read_cache));
+ info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
+ VOID(end_io_cache(&info->rec_cache));
+ got_error|=flush_blocks(param, share->key_cache, share->kfile);
+ if (!got_error && param->testflag & T_UNPACK)
+ {
+ share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
+ share->pack.header_length=0;
+ share->data_file_type=sort_info.new_data_file_type;
+ }
+ share->state.changed|= (STATE_NOT_OPTIMIZED_KEYS | STATE_NOT_SORTED_PAGES |
+ STATE_NOT_ANALYZED);
+ DBUG_RETURN(got_error);
+}
+
+
+/* Uppate keyfile when doing repair */
+
+static int writekeys(MI_CHECK *param, register MI_INFO *info, byte *buff,
+ my_off_t filepos)
+{
+ register uint i;
+ uchar *key;
+ DBUG_ENTER("writekeys");
+
+ key=info->lastkey+info->s->base.max_key_length;
+ for (i=0 ; i < info->s->base.keys ; i++)
+ {
+ if (((ulonglong) 1 << i) & info->s->state.key_map)
+ {
+ if (info->s->keyinfo[i].flag & HA_FULLTEXT )
+ {
+ if (_mi_ft_add(info,i,(char*) key,buff,filepos))
+ goto err;
+ }
+#ifdef HAVE_SPATIAL
+ else if (info->s->keyinfo[i].flag & HA_SPATIAL)
+ {
+ uint key_length=_mi_make_key(info,i,key,buff,filepos);
+ if (rtree_insert(info, i, key, key_length))
+ goto err;
+ }
+#endif /*HAVE_SPATIAL*/
+ else
+ {
+ uint key_length=_mi_make_key(info,i,key,buff,filepos);
+ if (_mi_ck_write(info,i,key,key_length))
+ goto err;
+ }
+ }
+ }
+ DBUG_RETURN(0);
+
+ err:
+ if (my_errno == HA_ERR_FOUND_DUPP_KEY)
+ {
+ info->errkey=(int) i; /* This key was found */
+ while ( i-- > 0 )
+ {
+ if (((ulonglong) 1 << i) & info->s->state.key_map)
+ {
+ if (info->s->keyinfo[i].flag & HA_FULLTEXT)
+ {
+ if (_mi_ft_del(info,i,(char*) key,buff,filepos))
+ break;
+ }
+ else
+ {
+ uint key_length=_mi_make_key(info,i,key,buff,filepos);
+ if (_mi_ck_delete(info,i,key,key_length))
+ break;
+ }
+ }
+ }
+ }
+ /* Remove checksum that was added to glob_crc in sort_get_next_record */
+ if (param->calc_checksum)
+ param->glob_crc-= info->checksum;
+ DBUG_PRINT("error",("errno: %d",my_errno));
+ DBUG_RETURN(-1);
+} /* writekeys */
+
+
+ /* Change all key-pointers that points to a records */
+
+int movepoint(register MI_INFO *info, byte *record, my_off_t oldpos,
+ my_off_t newpos, uint prot_key)
+{
+ register uint i;
+ uchar *key;
+ uint key_length;
+ DBUG_ENTER("movepoint");
+
+ key=info->lastkey+info->s->base.max_key_length;
+ for (i=0 ; i < info->s->base.keys; i++)
+ {
+ if (i != prot_key && (((ulonglong) 1 << i) & info->s->state.key_map))
+ {
+ key_length=_mi_make_key(info,i,key,record,oldpos);
+ if (info->s->keyinfo[i].flag & HA_NOSAME)
+ { /* Change pointer direct */
+ uint nod_flag;
+ MI_KEYDEF *keyinfo;
+ keyinfo=info->s->keyinfo+i;
+ if (_mi_search(info,keyinfo,key,USE_WHOLE_KEY,
+ (uint) (SEARCH_SAME | SEARCH_SAVE_BUFF),
+ info->s->state.key_root[i]))
+ DBUG_RETURN(-1);
+ nod_flag=mi_test_if_nod(info->buff);
+ _mi_dpointer(info,info->int_keypos-nod_flag-
+ info->s->rec_reflength,newpos);
+ if (_mi_write_keypage(info,keyinfo,info->last_keypage,
+ DFLT_INIT_HITS,info->buff))
+ DBUG_RETURN(-1);
+ }
+ else
+ { /* Change old key to new */
+ if (_mi_ck_delete(info,i,key,key_length))
+ DBUG_RETURN(-1);
+ key_length=_mi_make_key(info,i,key,record,newpos);
+ if (_mi_ck_write(info,i,key,key_length))
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+} /* movepoint */
+
+
+ /* Tell system that we want all memory for our cache */
+
+void lock_memory(MI_CHECK *param __attribute__((unused)))
+{
+#ifdef SUN_OS /* Key-cacheing thrases on sun 4.1 */
+ if (param->opt_lock_memory)
+ {
+ int success = mlockall(MCL_CURRENT); /* or plock(DATLOCK); */
+ if (geteuid() == 0 && success != 0)
+ mi_check_print_warning(param,
+ "Failed to lock memory. errno %d",my_errno);
+ }
+#endif
+} /* lock_memory */
+
+
+ /* Flush all changed blocks to disk */
+
+int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file)
+{
+ if (flush_key_blocks(key_cache, file, FLUSH_RELEASE))
+ {
+ mi_check_print_error(param,"%d when trying to write bufferts",my_errno);
+ return(1);
+ }
+ if (!param->using_global_keycache)
+ end_key_cache(key_cache,1);
+ return 0;
+} /* flush_blocks */
+
+
+ /* Sort index for more efficent reads */
+
+int mi_sort_index(MI_CHECK *param, register MI_INFO *info, my_string name)
+{
+ reg2 uint key;
+ reg1 MI_KEYDEF *keyinfo;
+ File new_file;
+ my_off_t index_pos[MI_MAX_POSSIBLE_KEY];
+ uint r_locks,w_locks;
+ int old_lock;
+ MYISAM_SHARE *share=info->s;
+ MI_STATE_INFO old_state;
+ DBUG_ENTER("mi_sort_index");
+
+ if (!(param->testflag & T_SILENT))
+ printf("- Sorting index for MyISAM-table '%s'\n",name);
+
+ /* Get real path for index file */
+ fn_format(param->temp_filename,name,"", MI_NAME_IEXT,2+4+32);
+ if ((new_file=my_create(fn_format(param->temp_filename,param->temp_filename,
+ "", INDEX_TMP_EXT,2+4),
+ 0,param->tmpfile_createflag,MYF(0))) <= 0)
+ {
+ mi_check_print_error(param,"Can't create new tempfile: '%s'",
+ param->temp_filename);
+ DBUG_RETURN(-1);
+ }
+ if (filecopy(param, new_file,share->kfile,0L,
+ (ulong) share->base.keystart, "headerblock"))
+ goto err;
+
+ param->new_file_pos=share->base.keystart;
+ for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ;
+ key++,keyinfo++)
+ {
+ if (!(((ulonglong) 1 << key) & share->state.key_map))
+ continue;
+
+ if (share->state.key_root[key] != HA_OFFSET_ERROR)
+ {
+ index_pos[key]=param->new_file_pos; /* Write first block here */
+ if (sort_one_index(param,info,keyinfo,share->state.key_root[key],
+ new_file))
+ goto err;
+ }
+ else
+ index_pos[key]= HA_OFFSET_ERROR; /* No blocks */
+ }
+
+ /* Flush key cache for this file if we are calling this outside myisamchk */
+ flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED);
+
+ share->state.version=(ulong) time((time_t*) 0);
+ old_state= share->state; /* save state if not stored */
+ r_locks= share->r_locks;
+ w_locks= share->w_locks;
+ old_lock= info->lock_type;
+
+ /* Put same locks as old file */
+ share->r_locks= share->w_locks= share->tot_locks= 0;
+ (void) _mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE);
+ VOID(my_close(share->kfile,MYF(MY_WME)));
+ share->kfile = -1;
+ VOID(my_close(new_file,MYF(MY_WME)));
+ if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,0,
+ MYF(0)) ||
+ mi_open_keyfile(share))
+ goto err2;
+ info->lock_type= F_UNLCK; /* Force mi_readinfo to lock */
+ _mi_readinfo(info,F_WRLCK,0); /* Will lock the table */
+ info->lock_type= old_lock;
+ share->r_locks= r_locks;
+ share->w_locks= w_locks;
+ share->tot_locks= r_locks+w_locks;
+ share->state= old_state; /* Restore old state */
+
+ info->state->key_file_length=param->new_file_pos;
+ info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
+ for (key=0 ; key < info->s->base.keys ; key++)
+ info->s->state.key_root[key]=index_pos[key];
+ for (key=0 ; key < info->s->state.header.max_block_size ; key++)
+ info->s->state.key_del[key]= HA_OFFSET_ERROR;
+
+ info->s->state.changed&= ~STATE_NOT_SORTED_PAGES;
+ DBUG_RETURN(0);
+
+err:
+ VOID(my_close(new_file,MYF(MY_WME)));
+err2:
+ VOID(my_delete(param->temp_filename,MYF(MY_WME)));
+ DBUG_RETURN(-1);
+} /* mi_sort_index */
+
+
+ /* Sort records recursive using one index */
+
+static int sort_one_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
+ my_off_t pagepos, File new_file)
+{
+ uint length,nod_flag,used_length, key_length;
+ uchar *buff,*keypos,*endpos;
+ uchar key[MI_MAX_POSSIBLE_KEY_BUFF];
+ my_off_t new_page_pos,next_page;
+ char llbuff[22];
+ DBUG_ENTER("sort_one_index");
+
+ new_page_pos=param->new_file_pos;
+ param->new_file_pos+=keyinfo->block_length;
+
+ if (!(buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
+ {
+ mi_check_print_error(param,"Not enough memory for key block");
+ DBUG_RETURN(-1);
+ }
+ if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0))
+ {
+ mi_check_print_error(param,"Can't read key block from filepos: %s",
+ llstr(pagepos,llbuff));
+ goto err;
+ }
+ if ((nod_flag=mi_test_if_nod(buff)) || keyinfo->flag & HA_FULLTEXT)
+ {
+ used_length=mi_getint(buff);
+ keypos=buff+2+nod_flag;
+ endpos=buff+used_length;
+ for ( ;; )
+ {
+ if (nod_flag)
+ {
+ next_page=_mi_kpos(nod_flag,keypos);
+ _mi_kpointer(info,keypos-nod_flag,param->new_file_pos); /* Save new pos */
+ if (sort_one_index(param,info,keyinfo,next_page,new_file))
+ {
+ DBUG_PRINT("error",("From page: %ld, keyoffset: %d used_length: %d",
+ (ulong) pagepos, (int) (keypos - buff),
+ (int) used_length));
+ DBUG_DUMP("buff",(byte*) buff,used_length);
+ goto err;
+ }
+ }
+ if (keypos >= endpos ||
+ (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,key)) == 0)
+ break;
+ DBUG_ASSERT(keypos <= endpos);
+ if (keyinfo->flag & HA_FULLTEXT)
+ {
+ uint off;
+ int subkeys;
+ get_key_full_length_rdonly(off, key);
+ subkeys=ft_sintXkorr(key+off);
+ if (subkeys < 0)
+ {
+ next_page= _mi_dpos(info,0,key+key_length);
+ _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
+ param->new_file_pos); /* Save new pos */
+ if (sort_one_index(param,info,&info->s->ft2_keyinfo,
+ next_page,new_file))
+ goto err;
+ }
+ }
+ }
+ }
+
+ /* Fill block with zero and write it to the new index file */
+ length=mi_getint(buff);
+ bzero((byte*) buff+length,keyinfo->block_length-length);
+ if (my_pwrite(new_file,(byte*) buff,(uint) keyinfo->block_length,
+ new_page_pos,MYF(MY_NABP | MY_WAIT_IF_FULL)))
+ {
+ mi_check_print_error(param,"Can't write indexblock, error: %d",my_errno);
+ goto err;
+ }
+ my_afree((gptr) buff);
+ DBUG_RETURN(0);
+err:
+ my_afree((gptr) buff);
+ DBUG_RETURN(1);
+} /* sort_one_index */
+
+
+ /*
+ Let temporary file replace old file.
+ This assumes that the new file was created in the same
+ directory as given by realpath(filename).
+ This will ensure that any symlinks that are used will still work.
+ Copy stats from old file to new file, deletes orignal and
+ changes new file name to old file name
+ */
+
+int change_to_newfile(const char * filename, const char * old_ext,
+ const char * new_ext,
+ uint raid_chunks __attribute__((unused)),
+ myf MyFlags)
+{
+ char old_filename[FN_REFLEN],new_filename[FN_REFLEN];
+#ifdef USE_RAID
+ if (raid_chunks)
+ return my_raid_redel(fn_format(old_filename,filename,"",old_ext,2+4),
+ fn_format(new_filename,filename,"",new_ext,2+4),
+ raid_chunks,
+ MYF(MY_WME | MY_LINK_WARNING | MyFlags));
+#endif
+ /* Get real path to filename */
+ (void) fn_format(old_filename,filename,"",old_ext,2+4+32);
+ return my_redel(old_filename,
+ fn_format(new_filename,old_filename,"",new_ext,2+4),
+ MYF(MY_WME | MY_LINK_WARNING | MyFlags));
+} /* change_to_newfile */
+
+
+ /* Locks a whole file */
+ /* Gives an error-message if file can't be locked */
+
+int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type,
+ const char *filetype, const char *filename)
+{
+ if (my_lock(file,lock_type,start,F_TO_EOF,
+ param->testflag & T_WAIT_FOREVER ? MYF(MY_SEEK_NOT_DONE) :
+ MYF(MY_SEEK_NOT_DONE | MY_DONT_WAIT)))
+ {
+ mi_check_print_error(param," %d when locking %s '%s'",my_errno,filetype,filename);
+ param->error_printed=2; /* Don't give that data is crashed */
+ return 1;
+ }
+ return 0;
+} /* lock_file */
+
+
+ /* Copy a block between two files */
+
+int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
+ my_off_t length, const char *type)
+{
+ char tmp_buff[IO_SIZE],*buff;
+ ulong buff_length;
+ DBUG_ENTER("filecopy");
+
+ buff_length=(ulong) min(param->write_buffer_length,length);
+ if (!(buff=my_malloc(buff_length,MYF(0))))
+ {
+ buff=tmp_buff; buff_length=IO_SIZE;
+ }
+
+ VOID(my_seek(from,start,MY_SEEK_SET,MYF(0)));
+ while (length > buff_length)
+ {
+ if (my_read(from,(byte*) buff,buff_length,MYF(MY_NABP)) ||
+ my_write(to,(byte*) buff,buff_length,param->myf_rw))
+ goto err;
+ length-= buff_length;
+ }
+ if (my_read(from,(byte*) buff,(uint) length,MYF(MY_NABP)) ||
+ my_write(to,(byte*) buff,(uint) length,param->myf_rw))
+ goto err;
+ if (buff != tmp_buff)
+ my_free(buff,MYF(0));
+ DBUG_RETURN(0);
+err:
+ if (buff != tmp_buff)
+ my_free(buff,MYF(0));
+ mi_check_print_error(param,"Can't copy %s to tempfile, error %d",
+ type,my_errno);
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Repair table or given index using sorting
+
+ SYNOPSIS
+ mi_repair_by_sort()
+ param Repair parameters
+ info MyISAM handler to repair
+ name Name of table (for warnings)
+ rep_quick set to <> 0 if we should not change data file
+
+ RESULT
+ 0 ok
+ <>0 Error
+*/
+
+int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
+ const char * name, int rep_quick)
+{
+ int got_error;
+ uint i;
+ ulong length;
+ ha_rows start_records;
+ my_off_t new_header_length,del;
+ File new_file;
+ MI_SORT_PARAM sort_param;
+ MYISAM_SHARE *share=info->s;
+ HA_KEYSEG *keyseg;
+ ulong *rec_per_key_part;
+ char llbuff[22];
+ SORT_INFO sort_info;
+ ulonglong key_map=share->state.key_map;
+ DBUG_ENTER("mi_repair_by_sort");
+
+ start_records=info->state->records;
+ got_error=1;
+ new_file= -1;
+ new_header_length=(param->testflag & T_UNPACK) ? 0 :
+ share->pack.header_length;
+ if (!(param->testflag & T_SILENT))
+ {
+ printf("- recovering (with sort) MyISAM-table '%s'\n",name);
+ printf("Data records: %s\n", llstr(start_records,llbuff));
+ }
+ param->testflag|=T_REP; /* for easy checking */
+
+ if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
+ param->testflag|=T_CALC_CHECKSUM;
+
+ bzero((char*)&sort_info,sizeof(sort_info));
+ bzero((char *)&sort_param, sizeof(sort_param));
+ if (!(sort_info.key_block=
+ alloc_key_blocks(param,
+ (uint) param->sort_key_blocks,
+ share->base.max_key_block_length))
+ || init_io_cache(&param->read_cache,info->dfile,
+ (uint) param->read_buffer_length,
+ READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) ||
+ (! rep_quick &&
+ init_io_cache(&info->rec_cache,info->dfile,
+ (uint) param->write_buffer_length,
+ WRITE_CACHE,new_header_length,1,
+ MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw)))
+ goto err;
+ sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
+ info->opt_flag|=WRITE_CACHE_USED;
+ info->rec_cache.file=info->dfile; /* for sort_delete_record */
+
+ if (!(sort_param.record=(byte*) my_malloc((uint) share->base.pack_reclength,
+ MYF(0))) ||
+ !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff))
+ {
+ mi_check_print_error(param, "Not enough memory for extra record");
+ goto err;
+ }
+ if (!rep_quick)
+ {
+ /* Get real path for data file */
+ if ((new_file=my_raid_create(fn_format(param->temp_filename,
+ share->data_file_name, "",
+ DATA_TMP_EXT, 2+4),
+ 0,param->tmpfile_createflag,
+ share->base.raid_type,
+ share->base.raid_chunks,
+ share->base.raid_chunksize,
+ MYF(0))) < 0)
+ {
+ mi_check_print_error(param,"Can't create new tempfile: '%s'",
+ param->temp_filename);
+ goto err;
+ }
+ if (filecopy(param, new_file,info->dfile,0L,new_header_length,
+ "datafile-header"))
+ goto err;
+ if (param->testflag & T_UNPACK)
+ {
+ share->options&= ~HA_OPTION_COMPRESS_RECORD;
+ mi_int2store(share->state.header.options,share->options);
+ }
+ share->state.dellink= HA_OFFSET_ERROR;
+ info->rec_cache.file=new_file;
+ }
+
+ info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
+ if (!(param->testflag & T_CREATE_MISSING_KEYS))
+ {
+ /*
+ Flush key cache for this file if we are calling this outside
+ myisamchk
+ */
+ flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED);
+ /* Clear the pointers to the given rows */
+ for (i=0 ; i < share->base.keys ; i++)
+ share->state.key_root[i]= HA_OFFSET_ERROR;
+ for (i=0 ; i < share->state.header.max_block_size ; i++)
+ share->state.key_del[i]= HA_OFFSET_ERROR;
+ info->state->key_file_length=share->base.keystart;
+ }
+ else
+ {
+ if (flush_key_blocks(share->key_cache,share->kfile, FLUSH_FORCE_WRITE))
+ goto err;
+ key_map= ~key_map; /* Create the missing keys */
+ }
+
+ sort_info.info=info;
+ sort_info.param = param;
+
+ set_data_file_type(&sort_info, share);
+ sort_param.filepos=new_header_length;
+ sort_info.dupp=0;
+ sort_info.buff=0;
+ param->read_cache.end_of_file=sort_info.filelength=
+ my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0));
+
+ sort_param.wordlist=NULL;
+
+ if (share->data_file_type == DYNAMIC_RECORD)
+ length=max(share->base.min_pack_length+1,share->base.min_block_length);
+ else if (share->data_file_type == COMPRESSED_RECORD)
+ length=share->base.min_block_length;
+ else
+ length=share->base.pack_reclength;
+ sort_info.max_records=
+ ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records :
+ (ha_rows) (sort_info.filelength/length+1));
+ sort_param.key_cmp=sort_key_cmp;
+ sort_param.lock_in_memory=lock_memory;
+ sort_param.tmpdir=param->tmpdir;
+ sort_param.sort_info=&sort_info;
+ sort_param.fix_datafile= (my_bool) (! rep_quick);
+ sort_param.master =1;
+
+ del=info->state->del;
+ param->glob_crc=0;
+ if (param->testflag & T_CALC_CHECKSUM)
+ param->calc_checksum=1;
+
+ rec_per_key_part= param->rec_per_key_part;
+ for (sort_param.key=0 ; sort_param.key < share->base.keys ;
+ rec_per_key_part+=sort_param.keyinfo->keysegs, sort_param.key++)
+ {
+ sort_param.read_cache=param->read_cache;
+ sort_param.keyinfo=share->keyinfo+sort_param.key;
+ sort_param.seg=sort_param.keyinfo->seg;
+ if (!(((ulonglong) 1 << sort_param.key) & key_map))
+ {
+ /* Remember old statistics for key */
+ memcpy((char*) rec_per_key_part,
+ (char*) (share->state.rec_per_key_part +
+ (uint) (rec_per_key_part - param->rec_per_key_part)),
+ sort_param.keyinfo->keysegs*sizeof(*rec_per_key_part));
+ continue;
+ }
+
+ if ((!(param->testflag & T_SILENT)))
+ printf ("- Fixing index %d\n",sort_param.key+1);
+ sort_param.max_pos=sort_param.pos=share->pack.header_length;
+ keyseg=sort_param.seg;
+ bzero((char*) sort_param.unique,sizeof(sort_param.unique));
+ sort_param.key_length=share->rec_reflength;
+ for (i=0 ; keyseg[i].type != HA_KEYTYPE_END; i++)
+ {
+ sort_param.key_length+=keyseg[i].length;
+ if (keyseg[i].flag & HA_SPACE_PACK)
+ sort_param.key_length+=get_pack_length(keyseg[i].length);
+ if (keyseg[i].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
+ sort_param.key_length+=2 + test(keyseg[i].length >= 127);
+ if (keyseg[i].flag & HA_NULL_PART)
+ sort_param.key_length++;
+ }
+ info->state->records=info->state->del=share->state.split=0;
+ info->state->empty=0;
+
+ if (sort_param.keyinfo->flag & HA_FULLTEXT)
+ {
+ uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
+ sort_param.keyinfo->seg->charset->mbmaxlen;
+ sort_info.max_records=
+ (ha_rows) (sort_info.filelength/ft_min_word_len+1);
+
+ sort_param.key_read=sort_ft_key_read;
+ sort_param.key_write=sort_ft_key_write;
+ sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
+ }
+ else
+ {
+ sort_param.key_read=sort_key_read;
+ sort_param.key_write=sort_key_write;
+ }
+
+ if (_create_index_by_sort(&sort_param,
+ (my_bool) (!(param->testflag & T_VERBOSE)),
+ (uint) param->sort_buffer_length))
+ {
+ param->retry_repair=1;
+ goto err;
+ }
+ param->calc_checksum=0; /* No need to calc glob_crc */
+
+ /* Set for next loop */
+ sort_info.max_records= (ha_rows) info->state->records;
+
+ if (param->testflag & T_STATISTICS)
+ update_key_parts(sort_param.keyinfo, rec_per_key_part, sort_param.unique,
+ (ulonglong) info->state->records);
+ share->state.key_map|=(ulonglong) 1 << sort_param.key;
+
+ if (sort_param.fix_datafile)
+ {
+ param->read_cache.end_of_file=sort_param.filepos;
+ if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
+ goto err;
+ if (param->testflag & T_SAFE_REPAIR)
+ {
+ /* Don't repair if we loosed more than one row */
+ if (info->state->records+1 < start_records)
+ {
+ info->state->records=start_records;
+ goto err;
+ }
+ }
+ share->state.state.data_file_length = info->state->data_file_length=
+ sort_param.filepos;
+ /* Only whole records */
+ share->state.version=(ulong) time((time_t*) 0);
+ my_close(info->dfile,MYF(0));
+ info->dfile=new_file;
+ share->data_file_type=sort_info.new_data_file_type;
+ share->pack.header_length=(ulong) new_header_length;
+ sort_param.fix_datafile=0;
+ }
+ else
+ info->state->data_file_length=sort_param.max_pos;
+
+ param->read_cache.file=info->dfile; /* re-init read cache */
+ reinit_io_cache(&param->read_cache,READ_CACHE,share->pack.header_length,
+ 1,1);
+ }
+
+ if (param->testflag & T_WRITE_LOOP)
+ {
+ VOID(fputs(" \r",stdout)); VOID(fflush(stdout));
+ }
+
+ if (rep_quick && del+sort_info.dupp != info->state->del)
+ {
+ mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
+ mi_check_print_error(param,"Run recovery again without -q");
+ got_error=1;
+ param->retry_repair=1;
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ goto err;
+ }
+
+ if (rep_quick & T_FORCE_UNIQUENESS)
+ {
+ my_off_t skr=info->state->data_file_length+
+ (share->options & HA_OPTION_COMPRESS_RECORD ?
+ MEMMAP_EXTRA_MARGIN : 0);
+#ifdef USE_RELOC
+ if (share->data_file_type == STATIC_RECORD &&
+ skr < share->base.reloc*share->base.min_pack_length)
+ skr=share->base.reloc*share->base.min_pack_length;
+#endif
+ if (skr != sort_info.filelength && !info->s->base.raid_type)
+ if (my_chsize(info->dfile,skr,0,MYF(0)))
+ mi_check_print_warning(param,
+ "Can't change size of datafile, error: %d",
+ my_errno);
+ }
+ if (param->testflag & T_CALC_CHECKSUM)
+ share->state.checksum=param->glob_crc;
+
+ if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0)))
+ mi_check_print_warning(param,
+ "Can't change size of indexfile, error: %d",
+ my_errno);
+
+ if (!(param->testflag & T_SILENT))
+ {
+ if (start_records != info->state->records)
+ printf("Data records: %s\n", llstr(info->state->records,llbuff));
+ if (sort_info.dupp)
+ mi_check_print_warning(param,
+ "%s records have been removed",
+ llstr(sort_info.dupp,llbuff));
+ }
+ got_error=0;
+
+ if (&share->state.state != info->state)
+ memcpy( &share->state.state, info->state, sizeof(*info->state));
+
+err:
+ got_error|= flush_blocks(param, share->key_cache, share->kfile);
+ VOID(end_io_cache(&info->rec_cache));
+ if (!got_error)
+ {
+ /* Replace the actual file with the temporary file */
+ if (new_file >= 0)
+ {
+ my_close(new_file,MYF(0));
+ info->dfile=new_file= -1;
+ if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
+ DATA_TMP_EXT, share->base.raid_chunks,
+ (param->testflag & T_BACKUP_DATA ?
+ MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
+ mi_open_datafile(info,share,-1))
+ got_error=1;
+ }
+ }
+ if (got_error)
+ {
+ if (! param->error_printed)
+ mi_check_print_error(param,"%d when fixing table",my_errno);
+ if (new_file >= 0)
+ {
+ VOID(my_close(new_file,MYF(0)));
+ VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks,
+ MYF(MY_WME)));
+ if (info->dfile == new_file)
+ info->dfile= -1;
+ }
+ mi_mark_crashed_on_repair(info);
+ }
+ else if (key_map == share->state.key_map)
+ share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
+ share->state.changed|=STATE_NOT_SORTED_PAGES;
+
+ my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff),
+ MYF(MY_ALLOW_ZERO_PTR));
+ my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
+ my_free((gptr) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
+ my_free((gptr) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
+ VOID(end_io_cache(&param->read_cache));
+ info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
+ if (!got_error && (param->testflag & T_UNPACK))
+ {
+ share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
+ share->pack.header_length=0;
+ }
+ DBUG_RETURN(got_error);
+}
+
+/*
+ Threaded repair of table using sorting
+
+ SYNOPSIS
+ mi_repair_parallel()
+ param Repair parameters
+ info MyISAM handler to repair
+ name Name of table (for warnings)
+ rep_quick set to <> 0 if we should not change data file
+
+ DESCRIPTION
+ Same as mi_repair_by_sort but do it multithreaded
+ Each key is handled by a separate thread.
+ TODO: make a number of threads a parameter
+
+ RESULT
+ 0 ok
+ <>0 Error
+*/
+
+int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
+ const char * name, int rep_quick)
+{
+#ifndef THREAD
+ return mi_repair_by_sort(param, info, name, rep_quick);
+#else
+ int got_error;
+ uint i,key, total_key_length, istep;
+ ulong rec_length;
+ ha_rows start_records;
+ my_off_t new_header_length,del;
+ File new_file;
+ MI_SORT_PARAM *sort_param=0;
+ MYISAM_SHARE *share=info->s;
+ ulong *rec_per_key_part;
+ HA_KEYSEG *keyseg;
+ char llbuff[22];
+ IO_CACHE_SHARE io_share;
+ SORT_INFO sort_info;
+ ulonglong key_map=share->state.key_map;
+ pthread_attr_t thr_attr;
+ DBUG_ENTER("mi_repair_parallel");
+
+ start_records=info->state->records;
+ got_error=1;
+ new_file= -1;
+ new_header_length=(param->testflag & T_UNPACK) ? 0 :
+ share->pack.header_length;
+ if (!(param->testflag & T_SILENT))
+ {
+ printf("- parallel recovering (with sort) MyISAM-table '%s'\n",name);
+ printf("Data records: %s\n", llstr(start_records,llbuff));
+ }
+ param->testflag|=T_REP; /* for easy checking */
+
+ if (info->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
+ param->testflag|=T_CALC_CHECKSUM;
+
+ bzero((char*)&sort_info,sizeof(sort_info));
+ if (!(sort_info.key_block=
+ alloc_key_blocks(param,
+ (uint) param->sort_key_blocks,
+ share->base.max_key_block_length))
+ || init_io_cache(&param->read_cache,info->dfile,
+ (uint) param->read_buffer_length,
+ READ_CACHE,share->pack.header_length,1,MYF(MY_WME)) ||
+ (! rep_quick &&
+ init_io_cache(&info->rec_cache,info->dfile,
+ (uint) param->write_buffer_length,
+ WRITE_CACHE,new_header_length,1,
+ MYF(MY_WME | MY_WAIT_IF_FULL) & param->myf_rw)))
+ goto err;
+ sort_info.key_block_end=sort_info.key_block+param->sort_key_blocks;
+ info->opt_flag|=WRITE_CACHE_USED;
+ info->rec_cache.file=info->dfile; /* for sort_delete_record */
+
+ if (!rep_quick)
+ {
+ /* Get real path for data file */
+ if ((new_file=my_raid_create(fn_format(param->temp_filename,
+ share->data_file_name, "",
+ DATA_TMP_EXT,
+ 2+4),
+ 0,param->tmpfile_createflag,
+ share->base.raid_type,
+ share->base.raid_chunks,
+ share->base.raid_chunksize,
+ MYF(0))) < 0)
+ {
+ mi_check_print_error(param,"Can't create new tempfile: '%s'",
+ param->temp_filename);
+ goto err;
+ }
+ if (filecopy(param, new_file,info->dfile,0L,new_header_length,
+ "datafile-header"))
+ goto err;
+ if (param->testflag & T_UNPACK)
+ {
+ share->options&= ~HA_OPTION_COMPRESS_RECORD;
+ mi_int2store(share->state.header.options,share->options);
+ }
+ share->state.dellink= HA_OFFSET_ERROR;
+ info->rec_cache.file=new_file;
+ }
+
+ info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
+ if (!(param->testflag & T_CREATE_MISSING_KEYS))
+ {
+ /*
+ Flush key cache for this file if we are calling this outside
+ myisamchk
+ */
+ flush_key_blocks(share->key_cache,share->kfile, FLUSH_IGNORE_CHANGED);
+ /* Clear the pointers to the given rows */
+ for (i=0 ; i < share->base.keys ; i++)
+ share->state.key_root[i]= HA_OFFSET_ERROR;
+ for (i=0 ; i < share->state.header.max_block_size ; i++)
+ share->state.key_del[i]= HA_OFFSET_ERROR;
+ info->state->key_file_length=share->base.keystart;
+ }
+ else
+ {
+ if (flush_key_blocks(share->key_cache,share->kfile, FLUSH_FORCE_WRITE))
+ goto err;
+ key_map= ~key_map; /* Create the missing keys */
+ }
+
+ sort_info.info=info;
+ sort_info.param = param;
+
+ set_data_file_type(&sort_info, share);
+ sort_info.dupp=0;
+ sort_info.buff=0;
+ param->read_cache.end_of_file=sort_info.filelength=
+ my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0));
+
+ if (share->data_file_type == DYNAMIC_RECORD)
+ rec_length=max(share->base.min_pack_length+1,share->base.min_block_length);
+ else if (share->data_file_type == COMPRESSED_RECORD)
+ rec_length=share->base.min_block_length;
+ else
+ rec_length=share->base.pack_reclength;
+ /*
+ +1 below is required hack for parallel repair mode.
+ The info->state->records value, that is compared later
+ to sort_info.max_records and cannot exceed it, is
+ increased in sort_key_write. In mi_repair_by_sort, sort_key_write
+ is called after sort_key_read, where the comparison is performed,
+ but in parallel mode master thread can call sort_key_write
+ before some other repair thread calls sort_key_read.
+ Furthermore I'm not even sure +1 would be enough.
+ May be sort_info.max_records shold be always set to max value in
+ parallel mode.
+ */
+ sort_info.max_records=
+ ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records + 1:
+ (ha_rows) (sort_info.filelength/rec_length+1));
+
+ del=info->state->del;
+ param->glob_crc=0;
+ if (param->testflag & T_CALC_CHECKSUM)
+ param->calc_checksum=1;
+
+ if (!(sort_param=(MI_SORT_PARAM *)
+ my_malloc((uint) share->base.keys *
+ (sizeof(MI_SORT_PARAM) + share->base.pack_reclength),
+ MYF(MY_ZEROFILL))))
+ {
+ mi_check_print_error(param,"Not enough memory for key!");
+ goto err;
+ }
+ total_key_length=0;
+ rec_per_key_part= param->rec_per_key_part;
+ info->state->records=info->state->del=share->state.split=0;
+ info->state->empty=0;
+
+ for (i=key=0, istep=1 ; key < share->base.keys ;
+ rec_per_key_part+=sort_param[i].keyinfo->keysegs, i+=istep, key++)
+ {
+ sort_param[i].key=key;
+ sort_param[i].keyinfo=share->keyinfo+key;
+ sort_param[i].seg=sort_param[i].keyinfo->seg;
+ if (!(((ulonglong) 1 << key) & key_map))
+ {
+ /* Remember old statistics for key */
+ memcpy((char*) rec_per_key_part,
+ (char*) (share->state.rec_per_key_part+
+ (uint) (rec_per_key_part - param->rec_per_key_part)),
+ sort_param[i].keyinfo->keysegs*sizeof(*rec_per_key_part));
+ istep=0;
+ continue;
+ }
+ istep=1;
+ if ((!(param->testflag & T_SILENT)))
+ printf ("- Fixing index %d\n",key+1);
+ if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
+ {
+ sort_param[i].key_read=sort_ft_key_read;
+ sort_param[i].key_write=sort_ft_key_write;
+ }
+ else
+ {
+ sort_param[i].key_read=sort_key_read;
+ sort_param[i].key_write=sort_key_write;
+ }
+ sort_param[i].key_cmp=sort_key_cmp;
+ sort_param[i].lock_in_memory=lock_memory;
+ sort_param[i].tmpdir=param->tmpdir;
+ sort_param[i].sort_info=&sort_info;
+ sort_param[i].master=0;
+ sort_param[i].fix_datafile=0;
+
+ sort_param[i].filepos=new_header_length;
+ sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length;
+
+ sort_param[i].record= (((char *)(sort_param+share->base.keys))+
+ (share->base.pack_reclength * i));
+ if (!mi_alloc_rec_buff(info, -1, &sort_param[i].rec_buff))
+ {
+ mi_check_print_error(param,"Not enough memory!");
+ goto err;
+ }
+
+ sort_param[i].key_length=share->rec_reflength;
+ for (keyseg=sort_param[i].seg; keyseg->type != HA_KEYTYPE_END;
+ keyseg++)
+ {
+ sort_param[i].key_length+=keyseg->length;
+ if (keyseg->flag & HA_SPACE_PACK)
+ sort_param[i].key_length+=get_pack_length(keyseg->length);
+ if (keyseg->flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
+ sort_param[i].key_length+=2 + test(keyseg->length >= 127);
+ if (keyseg->flag & HA_NULL_PART)
+ sort_param[i].key_length++;
+ }
+ total_key_length+=sort_param[i].key_length;
+
+ if (sort_param[i].keyinfo->flag & HA_FULLTEXT)
+ {
+ uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
+ sort_param[i].keyinfo->seg->charset->mbmaxlen;
+ sort_param[i].key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
+ }
+ }
+ sort_info.total_keys=i;
+ sort_param[0].master= 1;
+ sort_param[0].fix_datafile= (my_bool)(! rep_quick);
+
+ sort_info.got_error=0;
+ pthread_mutex_init(&sort_info.mutex, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&sort_info.cond, 0);
+ pthread_mutex_lock(&sort_info.mutex);
+
+ init_io_cache_share(&param->read_cache, &io_share, i);
+ (void) pthread_attr_init(&thr_attr);
+ (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
+
+ for (i=0 ; i < sort_info.total_keys ; i++)
+ {
+ sort_param[i].read_cache=param->read_cache;
+ /*
+ two approaches: the same amount of memory for each thread
+ or the memory for the same number of keys for each thread...
+ In the second one all the threads will fill their sort_buffers
+ (and call write_keys) at the same time, putting more stress on i/o.
+ */
+ sort_param[i].sortbuff_size=
+#ifndef USING_SECOND_APPROACH
+ param->sort_buffer_length/sort_info.total_keys;
+#else
+ param->sort_buffer_length*sort_param[i].key_length/total_key_length;
+#endif
+ if (pthread_create(&sort_param[i].thr, &thr_attr,
+ thr_find_all_keys,
+ (void *) (sort_param+i)))
+ {
+ mi_check_print_error(param,"Cannot start a repair thread");
+ remove_io_thread(&param->read_cache);
+ sort_info.got_error=1;
+ }
+ else
+ sort_info.threads_running++;
+ }
+ (void) pthread_attr_destroy(&thr_attr);
+
+ /* waiting for all threads to finish */
+ while (sort_info.threads_running)
+ pthread_cond_wait(&sort_info.cond, &sort_info.mutex);
+ pthread_mutex_unlock(&sort_info.mutex);
+
+ if ((got_error= thr_write_keys(sort_param)))
+ {
+ param->retry_repair=1;
+ goto err;
+ }
+ got_error=1; /* Assume the following may go wrong */
+
+ if (sort_param[0].fix_datafile)
+ {
+ if (write_data_suffix(&sort_info,1) || end_io_cache(&info->rec_cache))
+ goto err;
+ if (param->testflag & T_SAFE_REPAIR)
+ {
+ /* Don't repair if we loosed more than one row */
+ if (info->state->records+1 < start_records)
+ {
+ info->state->records=start_records;
+ goto err;
+ }
+ }
+ share->state.state.data_file_length= info->state->data_file_length=
+ sort_param->filepos;
+ /* Only whole records */
+ share->state.version=(ulong) time((time_t*) 0);
+ my_close(info->dfile,MYF(0));
+ info->dfile=new_file;
+ share->data_file_type=sort_info.new_data_file_type;
+ share->pack.header_length=(ulong) new_header_length;
+ }
+ else
+ info->state->data_file_length=sort_param->max_pos;
+
+ if (rep_quick && del+sort_info.dupp != info->state->del)
+ {
+ mi_check_print_error(param,"Couldn't fix table with quick recovery: Found wrong number of deleted records");
+ mi_check_print_error(param,"Run recovery again without -q");
+ param->retry_repair=1;
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ goto err;
+ }
+
+ if (rep_quick & T_FORCE_UNIQUENESS)
+ {
+ my_off_t skr=info->state->data_file_length+
+ (share->options & HA_OPTION_COMPRESS_RECORD ?
+ MEMMAP_EXTRA_MARGIN : 0);
+#ifdef USE_RELOC
+ if (share->data_file_type == STATIC_RECORD &&
+ skr < share->base.reloc*share->base.min_pack_length)
+ skr=share->base.reloc*share->base.min_pack_length;
+#endif
+ if (skr != sort_info.filelength && !info->s->base.raid_type)
+ if (my_chsize(info->dfile,skr,0,MYF(0)))
+ mi_check_print_warning(param,
+ "Can't change size of datafile, error: %d",
+ my_errno);
+ }
+ if (param->testflag & T_CALC_CHECKSUM)
+ share->state.checksum=param->glob_crc;
+
+ if (my_chsize(share->kfile,info->state->key_file_length,0,MYF(0)))
+ mi_check_print_warning(param,
+ "Can't change size of indexfile, error: %d", my_errno);
+
+ if (!(param->testflag & T_SILENT))
+ {
+ if (start_records != info->state->records)
+ printf("Data records: %s\n", llstr(info->state->records,llbuff));
+ if (sort_info.dupp)
+ mi_check_print_warning(param,
+ "%s records have been removed",
+ llstr(sort_info.dupp,llbuff));
+ }
+ got_error=0;
+
+ if (&share->state.state != info->state)
+ memcpy(&share->state.state, info->state, sizeof(*info->state));
+
+err:
+ got_error|= flush_blocks(param, share->key_cache, share->kfile);
+ VOID(end_io_cache(&info->rec_cache));
+ if (!got_error)
+ {
+ /* Replace the actual file with the temporary file */
+ if (new_file >= 0)
+ {
+ my_close(new_file,MYF(0));
+ info->dfile=new_file= -1;
+ if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
+ DATA_TMP_EXT, share->base.raid_chunks,
+ (param->testflag & T_BACKUP_DATA ?
+ MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
+ mi_open_datafile(info,share,-1))
+ got_error=1;
+ }
+ }
+ if (got_error)
+ {
+ if (! param->error_printed)
+ mi_check_print_error(param,"%d when fixing table",my_errno);
+ if (new_file >= 0)
+ {
+ VOID(my_close(new_file,MYF(0)));
+ VOID(my_raid_delete(param->temp_filename,share->base.raid_chunks,
+ MYF(MY_WME)));
+ if (info->dfile == new_file)
+ info->dfile= -1;
+ }
+ mi_mark_crashed_on_repair(info);
+ }
+ else if (key_map == share->state.key_map)
+ share->state.changed&= ~STATE_NOT_OPTIMIZED_KEYS;
+ share->state.changed|=STATE_NOT_SORTED_PAGES;
+
+ pthread_cond_destroy (&sort_info.cond);
+ pthread_mutex_destroy(&sort_info.mutex);
+
+ my_free((gptr) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((gptr) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR));
+ my_free((gptr) sort_param,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
+ VOID(end_io_cache(&param->read_cache));
+ info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
+ if (!got_error && (param->testflag & T_UNPACK))
+ {
+ share->state.header.options[0]&= (uchar) ~HA_OPTION_COMPRESS_RECORD;
+ share->pack.header_length=0;
+ }
+ DBUG_RETURN(got_error);
+#endif /* THREAD */
+}
+
+ /* Read next record and return next key */
+
+static int sort_key_read(MI_SORT_PARAM *sort_param, void *key)
+{
+ int error;
+ SORT_INFO *sort_info=sort_param->sort_info;
+ MI_INFO *info=sort_info->info;
+ DBUG_ENTER("sort_key_read");
+
+ if ((error=sort_get_next_record(sort_param)))
+ DBUG_RETURN(error);
+ if (info->state->records == sort_info->max_records)
+ {
+ mi_check_print_error(sort_info->param,
+ "Key %d - Found too many records; Can't continue",
+ sort_param->key+1);
+ DBUG_RETURN(1);
+ }
+ sort_param->real_key_length=
+ (info->s->rec_reflength+
+ _mi_make_key(info, sort_param->key, (uchar*) key,
+ sort_param->record, sort_param->filepos));
+#ifdef HAVE_purify
+ bzero(key+sort_param->real_key_length,
+ (sort_param->key_length-sort_param->real_key_length));
+#endif
+ DBUG_RETURN(sort_write_record(sort_param));
+} /* sort_key_read */
+
+static int sort_ft_key_read(MI_SORT_PARAM *sort_param, void *key)
+{
+ int error;
+ SORT_INFO *sort_info=sort_param->sort_info;
+ MI_INFO *info=sort_info->info;
+ FT_WORD *wptr=0;
+ DBUG_ENTER("sort_ft_key_read");
+
+ if (!sort_param->wordlist)
+ {
+ for (;;)
+ {
+ my_free((char*) wptr, MYF(MY_ALLOW_ZERO_PTR));
+ if ((error=sort_get_next_record(sort_param)))
+ DBUG_RETURN(error);
+ if (!(wptr=_mi_ft_parserecord(info,sort_param->key,sort_param->record)))
+ DBUG_RETURN(1);
+ if (wptr->pos)
+ break;
+ error=sort_write_record(sort_param);
+ }
+ sort_param->wordptr=sort_param->wordlist=wptr;
+ }
+ else
+ {
+ error=0;
+ wptr=(FT_WORD*)(sort_param->wordptr);
+ }
+
+ sort_param->real_key_length=(info->s->rec_reflength+
+ _ft_make_key(info, sort_param->key,
+ key, wptr++, sort_param->filepos));
+#ifdef HAVE_purify
+ if (sort_param->key_length > sort_param->real_key_length)
+ bzero(key+sort_param->real_key_length,
+ (sort_param->key_length-sort_param->real_key_length));
+#endif
+ if (!wptr->pos)
+ {
+ my_free((char*) sort_param->wordlist, MYF(0));
+ sort_param->wordlist=0;
+ error=sort_write_record(sort_param);
+ }
+ else
+ sort_param->wordptr=(void*)wptr;
+
+ DBUG_RETURN(error);
+} /* sort_ft_key_read */
+
+
+ /* Read next record from file using parameters in sort_info */
+ /* Return -1 if end of file, 0 if ok and > 0 if error */
+
+static int sort_get_next_record(MI_SORT_PARAM *sort_param)
+{
+ int searching;
+ uint found_record,b_type,left_length;
+ my_off_t pos;
+ byte *to;
+ MI_BLOCK_INFO block_info;
+ SORT_INFO *sort_info=sort_param->sort_info;
+ MI_CHECK *param=sort_info->param;
+ MI_INFO *info=sort_info->info;
+ MYISAM_SHARE *share=info->s;
+ char llbuff[22],llbuff2[22];
+ DBUG_ENTER("sort_get_next_record");
+
+ if (*killed_ptr(param))
+ DBUG_RETURN(1);
+
+ switch (share->data_file_type) {
+ case STATIC_RECORD:
+ for (;;)
+ {
+ if (my_b_read(&sort_param->read_cache,sort_param->record,
+ share->base.pack_reclength))
+ {
+ if (sort_param->read_cache.error)
+ param->out_flag |= O_DATA_LOST;
+ param->retry_repair=1;
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ DBUG_RETURN(-1);
+ }
+ sort_param->start_recpos=sort_param->pos;
+ if (!sort_param->fix_datafile)
+ {
+ sort_param->filepos=sort_param->pos;
+ if (sort_param->master)
+ share->state.split++;
+ }
+ sort_param->max_pos=(sort_param->pos+=share->base.pack_reclength);
+ if (*sort_param->record)
+ {
+ if (param->calc_checksum)
+ param->glob_crc+= (info->checksum=
+ mi_static_checksum(info,sort_param->record));
+ DBUG_RETURN(0);
+ }
+ if (!sort_param->fix_datafile && sort_param->master)
+ {
+ info->state->del++;
+ info->state->empty+=share->base.pack_reclength;
+ }
+ }
+ case DYNAMIC_RECORD:
+ LINT_INIT(to);
+ pos=sort_param->pos;
+ searching=(sort_param->fix_datafile && (param->testflag & T_EXTEND));
+ for (;;)
+ {
+ found_record=block_info.second_read= 0;
+ left_length=1;
+ if (searching)
+ {
+ pos=MY_ALIGN(pos,MI_DYN_ALIGN_SIZE);
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ sort_param->start_recpos=pos;
+ }
+ do
+ {
+ if (pos > sort_param->max_pos)
+ sort_param->max_pos=pos;
+ if (pos & (MI_DYN_ALIGN_SIZE-1))
+ {
+ if ((param->testflag & T_VERBOSE) || searching == 0)
+ mi_check_print_info(param,"Wrong aligned block at %s",
+ llstr(pos,llbuff));
+ if (searching)
+ goto try_next;
+ }
+ if (found_record && pos == param->search_after_block)
+ mi_check_print_info(param,"Block: %s used by record at %s",
+ llstr(param->search_after_block,llbuff),
+ llstr(sort_param->start_recpos,llbuff2));
+ if (_mi_read_cache(&sort_param->read_cache,
+ (byte*) block_info.header,pos,
+ MI_BLOCK_INFO_HEADER_LENGTH,
+ (! found_record ? READING_NEXT : 0) |
+ READING_HEADER))
+ {
+ if (found_record)
+ {
+ mi_check_print_info(param,
+ "Can't read whole record at %s (errno: %d)",
+ llstr(sort_param->start_recpos,llbuff),errno);
+ goto try_next;
+ }
+ DBUG_RETURN(-1);
+ }
+ if (searching && ! sort_param->fix_datafile)
+ {
+ param->error_printed=1;
+ param->retry_repair=1;
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ DBUG_RETURN(1); /* Something wrong with data */
+ }
+ b_type=_mi_get_block_info(&block_info,-1,pos);
+ if ((b_type & (BLOCK_ERROR | BLOCK_FATAL_ERROR)) ||
+ ((b_type & BLOCK_FIRST) &&
+ (block_info.rec_len < (uint) share->base.min_pack_length ||
+ block_info.rec_len > (uint) share->base.max_pack_length)))
+ {
+ uint i;
+ if (param->testflag & T_VERBOSE || searching == 0)
+ mi_check_print_info(param,
+ "Wrong bytesec: %3d-%3d-%3d at %10s; Skipped",
+ block_info.header[0],block_info.header[1],
+ block_info.header[2],llstr(pos,llbuff));
+ if (found_record)
+ goto try_next;
+ block_info.second_read=0;
+ searching=1;
+ /* Search after block in read header string */
+ for (i=MI_DYN_ALIGN_SIZE ;
+ i < MI_BLOCK_INFO_HEADER_LENGTH ;
+ i+= MI_DYN_ALIGN_SIZE)
+ if (block_info.header[i] >= 1 &&
+ block_info.header[i] <= MI_MAX_DYN_HEADER_BYTE)
+ break;
+ pos+=(ulong) i;
+ sort_param->start_recpos=pos;
+ continue;
+ }
+ if (b_type & BLOCK_DELETED)
+ {
+ bool error=0;
+ if (block_info.block_len+ (uint) (block_info.filepos-pos) <
+ share->base.min_block_length)
+ {
+ if (!searching)
+ mi_check_print_info(param,
+ "Deleted block with impossible length %u at %s",
+ block_info.block_len,llstr(pos,llbuff));
+ error=1;
+ }
+ else
+ {
+ if ((block_info.next_filepos != HA_OFFSET_ERROR &&
+ block_info.next_filepos >=
+ info->state->data_file_length) ||
+ (block_info.prev_filepos != HA_OFFSET_ERROR &&
+ block_info.prev_filepos >= info->state->data_file_length))
+ {
+ if (!searching)
+ mi_check_print_info(param,
+ "Delete link points outside datafile at %s",
+ llstr(pos,llbuff));
+ error=1;
+ }
+ }
+ if (error)
+ {
+ if (found_record)
+ goto try_next;
+ searching=1;
+ pos+= MI_DYN_ALIGN_SIZE;
+ sort_param->start_recpos=pos;
+ block_info.second_read=0;
+ continue;
+ }
+ }
+ else
+ {
+ if (block_info.block_len+ (uint) (block_info.filepos-pos) <
+ share->base.min_block_length ||
+ block_info.block_len > (uint) share->base.max_pack_length+
+ MI_SPLIT_LENGTH)
+ {
+ if (!searching)
+ mi_check_print_info(param,
+ "Found block with impossible length %u at %s; Skipped",
+ block_info.block_len+ (uint) (block_info.filepos-pos),
+ llstr(pos,llbuff));
+ if (found_record)
+ goto try_next;
+ searching=1;
+ pos+= MI_DYN_ALIGN_SIZE;
+ sort_param->start_recpos=pos;
+ block_info.second_read=0;
+ continue;
+ }
+ }
+ if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
+ {
+ if (!sort_param->fix_datafile && sort_param->master &&
+ (b_type & BLOCK_DELETED))
+ {
+ info->state->empty+=block_info.block_len;
+ info->state->del++;
+ share->state.split++;
+ }
+ if (found_record)
+ goto try_next;
+ if (searching)
+ {
+ pos+=MI_DYN_ALIGN_SIZE;
+ sort_param->start_recpos=pos;
+ }
+ else
+ pos=block_info.filepos+block_info.block_len;
+ block_info.second_read=0;
+ continue;
+ }
+
+ if (!sort_param->fix_datafile && sort_param->master)
+ share->state.split++;
+ if (! found_record++)
+ {
+ sort_param->find_length=left_length=block_info.rec_len;
+ sort_param->start_recpos=pos;
+ if (!sort_param->fix_datafile)
+ sort_param->filepos=sort_param->start_recpos;
+ if (sort_param->fix_datafile && (param->testflag & T_EXTEND))
+ sort_param->pos=block_info.filepos+1;
+ else
+ sort_param->pos=block_info.filepos+block_info.block_len;
+ if (share->base.blobs)
+ {
+ if (!(to=mi_alloc_rec_buff(info,block_info.rec_len,
+ &(sort_param->rec_buff))))
+ {
+ if (param->max_record_length >= block_info.rec_len)
+ {
+ mi_check_print_error(param,"Not enough memory for blob at %s (need %lu)",
+ llstr(sort_param->start_recpos,llbuff),
+ (ulong) block_info.rec_len);
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ mi_check_print_info(param,"Not enough memory for blob at %s (need %lu); Row skipped",
+ llstr(sort_param->start_recpos,llbuff),
+ (ulong) block_info.rec_len);
+ goto try_next;
+ }
+ }
+ }
+ else
+ to= sort_param->rec_buff;
+ }
+ if (left_length < block_info.data_len || ! block_info.data_len)
+ {
+ mi_check_print_info(param,
+ "Found block with too small length at %s; Skipped",
+ llstr(sort_param->start_recpos,llbuff));
+ goto try_next;
+ }
+ if (block_info.filepos + block_info.data_len >
+ sort_param->read_cache.end_of_file)
+ {
+ mi_check_print_info(param,
+ "Found block that points outside data file at %s",
+ llstr(sort_param->start_recpos,llbuff));
+ goto try_next;
+ }
+ if (_mi_read_cache(&sort_param->read_cache,to,block_info.filepos,
+ block_info.data_len,
+ (found_record == 1 ? READING_NEXT : 0)))
+ {
+ mi_check_print_info(param,
+ "Read error for block at: %s (error: %d); Skipped",
+ llstr(block_info.filepos,llbuff),my_errno);
+ goto try_next;
+ }
+ left_length-=block_info.data_len;
+ to+=block_info.data_len;
+ pos=block_info.next_filepos;
+ if (pos == HA_OFFSET_ERROR && left_length)
+ {
+ mi_check_print_info(param,"Wrong block with wrong total length starting at %s",
+ llstr(sort_param->start_recpos,llbuff));
+ goto try_next;
+ }
+ if (pos + MI_BLOCK_INFO_HEADER_LENGTH > sort_param->read_cache.end_of_file)
+ {
+ mi_check_print_info(param,"Found link that points at %s (outside data file) at %s",
+ llstr(pos,llbuff2),
+ llstr(sort_param->start_recpos,llbuff));
+ goto try_next;
+ }
+ } while (left_length);
+
+ if (_mi_rec_unpack(info,sort_param->record,sort_param->rec_buff,
+ sort_param->find_length) != MY_FILE_ERROR)
+ {
+ if (sort_param->read_cache.error < 0)
+ DBUG_RETURN(1);
+ if (info->s->calc_checksum)
+ info->checksum=mi_checksum(info,sort_param->record);
+ if ((param->testflag & (T_EXTEND | T_REP)) || searching)
+ {
+ if (_mi_rec_check(info, sort_param->record, sort_param->rec_buff,
+ sort_param->find_length))
+ {
+ mi_check_print_info(param,"Found wrong packed record at %s",
+ llstr(sort_param->start_recpos,llbuff));
+ goto try_next;
+ }
+ }
+ if (param->calc_checksum)
+ param->glob_crc+= info->checksum;
+ DBUG_RETURN(0);
+ }
+ if (!searching)
+ mi_check_print_info(param,"Key %d - Found wrong stored record at %s",
+ sort_param->key+1,
+ llstr(sort_param->start_recpos,llbuff));
+ try_next:
+ pos=(sort_param->start_recpos+=MI_DYN_ALIGN_SIZE);
+ searching=1;
+ }
+ case COMPRESSED_RECORD:
+ for (searching=0 ;; searching=1, sort_param->pos++)
+ {
+ if (_mi_read_cache(&sort_param->read_cache,(byte*) block_info.header,
+ sort_param->pos,
+ share->pack.ref_length,READING_NEXT))
+ DBUG_RETURN(-1);
+ if (searching && ! sort_param->fix_datafile)
+ {
+ param->error_printed=1;
+ param->retry_repair=1;
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ DBUG_RETURN(1); /* Something wrong with data */
+ }
+ sort_param->start_recpos=sort_param->pos;
+ if (_mi_pack_get_block_info(info,&block_info,-1,sort_param->pos))
+ DBUG_RETURN(-1);
+ if (!block_info.rec_len &&
+ sort_param->pos + MEMMAP_EXTRA_MARGIN ==
+ sort_param->read_cache.end_of_file)
+ DBUG_RETURN(-1);
+ if (block_info.rec_len < (uint) share->min_pack_length ||
+ block_info.rec_len > (uint) share->max_pack_length)
+ {
+ if (! searching)
+ mi_check_print_info(param,"Found block with wrong recordlength: %d at %s\n",
+ block_info.rec_len,
+ llstr(sort_param->pos,llbuff));
+ continue;
+ }
+ if (_mi_read_cache(&sort_param->read_cache,(byte*) sort_param->rec_buff,
+ block_info.filepos, block_info.rec_len,
+ READING_NEXT))
+ {
+ if (! searching)
+ mi_check_print_info(param,"Couldn't read whole record from %s",
+ llstr(sort_param->pos,llbuff));
+ continue;
+ }
+ if (_mi_pack_rec_unpack(info,sort_param->record,sort_param->rec_buff,
+ block_info.rec_len))
+ {
+ if (! searching)
+ mi_check_print_info(param,"Found wrong record at %s",
+ llstr(sort_param->pos,llbuff));
+ continue;
+ }
+ info->checksum=mi_checksum(info,sort_param->record);
+ if (!sort_param->fix_datafile)
+ {
+ sort_param->filepos=sort_param->pos;
+ if (sort_param->master)
+ share->state.split++;
+ }
+ sort_param->max_pos=(sort_param->pos=block_info.filepos+
+ block_info.rec_len);
+ info->packed_length=block_info.rec_len;
+ if (param->calc_checksum)
+ param->glob_crc+= info->checksum;
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_RETURN(1); /* Impossible */
+}
+
+
+ /* Write record to new file */
+
+int sort_write_record(MI_SORT_PARAM *sort_param)
+{
+ int flag;
+ uint length;
+ ulong block_length,reclength;
+ byte *from;
+ byte block_buff[8];
+ SORT_INFO *sort_info=sort_param->sort_info;
+ MI_CHECK *param=sort_info->param;
+ MI_INFO *info=sort_info->info;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("sort_write_record");
+
+ if (sort_param->fix_datafile)
+ {
+ switch (sort_info->new_data_file_type) {
+ case STATIC_RECORD:
+ if (my_b_write(&info->rec_cache,sort_param->record,
+ share->base.pack_reclength))
+ {
+ mi_check_print_error(param,"%d when writing to datafile",my_errno);
+ DBUG_RETURN(1);
+ }
+ sort_param->filepos+=share->base.pack_reclength;
+ info->s->state.split++;
+ /* sort_info->param->glob_crc+=mi_static_checksum(info, sort_param->record); */
+ break;
+ case DYNAMIC_RECORD:
+ if (! info->blobs)
+ from=sort_param->rec_buff;
+ else
+ {
+ /* must be sure that local buffer is big enough */
+ reclength=info->s->base.pack_reclength+
+ _my_calc_total_blob_length(info,sort_param->record)+
+ ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
+ MI_DYN_DELETE_BLOCK_HEADER;
+ if (sort_info->buff_length < reclength)
+ {
+ if (!(sort_info->buff=my_realloc(sort_info->buff, (uint) reclength,
+ MYF(MY_FREE_ON_ERROR |
+ MY_ALLOW_ZERO_PTR))))
+ DBUG_RETURN(1);
+ sort_info->buff_length=reclength;
+ }
+ from=sort_info->buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER);
+ }
+ info->checksum=mi_checksum(info,sort_param->record);
+ reclength=_mi_rec_pack(info,from,sort_param->record);
+ flag=0;
+ /* sort_info->param->glob_crc+=info->checksum; */
+
+ do
+ {
+ block_length=reclength+ 3 + test(reclength >= (65520-3));
+ if (block_length < share->base.min_block_length)
+ block_length=share->base.min_block_length;
+ info->update|=HA_STATE_WRITE_AT_END;
+ block_length=MY_ALIGN(block_length,MI_DYN_ALIGN_SIZE);
+ if (block_length > MI_MAX_BLOCK_LENGTH)
+ block_length=MI_MAX_BLOCK_LENGTH;
+ if (_mi_write_part_record(info,0L,block_length,
+ sort_param->filepos+block_length,
+ &from,&reclength,&flag))
+ {
+ mi_check_print_error(param,"%d when writing to datafile",my_errno);
+ DBUG_RETURN(1);
+ }
+ sort_param->filepos+=block_length;
+ info->s->state.split++;
+ } while (reclength);
+ /* sort_info->param->glob_crc+=info->checksum; */
+ break;
+ case COMPRESSED_RECORD:
+ reclength=info->packed_length;
+ length=save_pack_length(block_buff,reclength);
+ if (info->s->base.blobs)
+ length+=save_pack_length(block_buff+length,info->blob_length);
+ if (my_b_write(&info->rec_cache,block_buff,length) ||
+ my_b_write(&info->rec_cache,(byte*) sort_param->rec_buff,reclength))
+ {
+ mi_check_print_error(param,"%d when writing to datafile",my_errno);
+ DBUG_RETURN(1);
+ }
+ /* sort_info->param->glob_crc+=info->checksum; */
+ sort_param->filepos+=reclength+length;
+ info->s->state.split++;
+ break;
+ }
+ }
+ if (sort_param->master)
+ {
+ info->state->records++;
+ if ((param->testflag & T_WRITE_LOOP) &&
+ (info->state->records % WRITE_COUNT) == 0)
+ {
+ char llbuff[22];
+ printf("%s\r", llstr(info->state->records,llbuff));
+ VOID(fflush(stdout));
+ }
+ }
+ DBUG_RETURN(0);
+} /* sort_write_record */
+
+
+ /* Compare two keys from _create_index_by_sort */
+
+static int sort_key_cmp(MI_SORT_PARAM *sort_param, const void *a,
+ const void *b)
+{
+ uint not_used;
+ return (ha_key_cmp(sort_param->seg, *((uchar**) a), *((uchar**) b),
+ USE_WHOLE_KEY, SEARCH_SAME,&not_used));
+} /* sort_key_cmp */
+
+
+static int sort_key_write(MI_SORT_PARAM *sort_param, const void *a)
+{
+ uint diff_pos;
+ char llbuff[22],llbuff2[22];
+ SORT_INFO *sort_info=sort_param->sort_info;
+ MI_CHECK *param= sort_info->param;
+ int cmp;
+
+ if (sort_info->key_block->inited)
+ {
+ cmp=ha_key_cmp(sort_param->seg,sort_info->key_block->lastkey,
+ (uchar*) a, USE_WHOLE_KEY,SEARCH_FIND | SEARCH_UPDATE,
+ &diff_pos);
+ sort_param->unique[diff_pos-1]++;
+ }
+ else
+ {
+ cmp= -1;
+ }
+ if ((sort_param->keyinfo->flag & HA_NOSAME) && cmp == 0)
+ {
+ sort_info->dupp++;
+ sort_info->info->lastpos=get_record_for_key(sort_info->info,
+ sort_param->keyinfo,
+ (uchar*) a);
+ mi_check_print_warning(param,
+ "Duplicate key for record at %10s against record at %10s",
+ llstr(sort_info->info->lastpos,llbuff),
+ llstr(get_record_for_key(sort_info->info,
+ sort_param->keyinfo,
+ sort_info->key_block->
+ lastkey),
+ llbuff2));
+ param->testflag|=T_RETRY_WITHOUT_QUICK;
+ if (sort_info->param->testflag & T_VERBOSE)
+ _mi_print_key(stdout,sort_param->seg,(uchar*) a, USE_WHOLE_KEY);
+ return (sort_delete_record(sort_param));
+ }
+#ifndef DBUG_OFF
+ if (cmp > 0)
+ {
+ mi_check_print_error(param,
+ "Internal error: Keys are not in order from sort");
+ return(1);
+ }
+#endif
+ return (sort_insert_key(sort_param,sort_info->key_block,
+ (uchar*) a, HA_OFFSET_ERROR));
+} /* sort_key_write */
+
+int sort_ft_buf_flush(MI_SORT_PARAM *sort_param)
+{
+ SORT_INFO *sort_info=sort_param->sort_info;
+ SORT_KEY_BLOCKS *key_block=sort_info->key_block;
+ MYISAM_SHARE *share=sort_info->info->s;
+ uint val_off, val_len;
+ int error;
+ SORT_FT_BUF *ft_buf=sort_info->ft_buf;
+ uchar *from, *to;
+
+ val_len=share->ft2_keyinfo.keylength;
+ get_key_full_length_rdonly(val_off, ft_buf->lastkey);
+ to=ft_buf->lastkey+val_off;
+
+ if (ft_buf->buf)
+ {
+ /* flushing first-level tree */
+ error=sort_insert_key(sort_param,key_block,ft_buf->lastkey,
+ HA_OFFSET_ERROR);
+ for (from=to+val_len;
+ !error && from < ft_buf->buf;
+ from+= val_len)
+ {
+ memcpy(to, from, val_len);
+ error=sort_insert_key(sort_param,key_block,ft_buf->lastkey,
+ HA_OFFSET_ERROR);
+ }
+ return error;
+ }
+ /* flushing second-level tree keyblocks */
+ error=flush_pending_blocks(sort_param);
+ /* updating lastkey with second-level tree info */
+ ft_intXstore(ft_buf->lastkey+val_off, -ft_buf->count);
+ _mi_dpointer(sort_info->info, ft_buf->lastkey+val_off+HA_FT_WLEN,
+ share->state.key_root[sort_param->key]);
+ /* restoring first level tree data in sort_info/sort_param */
+ sort_info->key_block=sort_info->key_block_end- sort_info->param->sort_key_blocks;
+ sort_param->keyinfo=share->keyinfo+sort_param->key;
+ share->state.key_root[sort_param->key]=HA_OFFSET_ERROR;
+ /* writing lastkey in first-level tree */
+ return error ? error :
+ sort_insert_key(sort_param,sort_info->key_block,
+ ft_buf->lastkey,HA_OFFSET_ERROR);
+}
+
+static int sort_ft_key_write(MI_SORT_PARAM *sort_param, const void *a)
+{
+ uint a_len, val_off, val_len, error;
+ uchar *p;
+ SORT_INFO *sort_info=sort_param->sort_info;
+ SORT_FT_BUF *ft_buf=sort_info->ft_buf;
+ SORT_KEY_BLOCKS *key_block=sort_info->key_block;
+
+ val_len=HA_FT_WLEN+sort_info->info->s->base.rec_reflength;
+ get_key_full_length_rdonly(a_len, (uchar *)a);
+
+ if (!ft_buf)
+ {
+ /*
+ use two-level tree only if key_reflength fits in rec_reflength place
+ and row format is NOT static - for _mi_dpointer not to garble offsets
+ */
+ if ((sort_info->info->s->base.key_reflength <=
+ sort_info->info->s->base.rec_reflength) &&
+ (sort_info->info->s->options &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
+ ft_buf=(SORT_FT_BUF *)my_malloc(sort_param->keyinfo->block_length +
+ sizeof(SORT_FT_BUF), MYF(MY_WME));
+
+ if (!ft_buf)
+ {
+ sort_param->key_write=sort_key_write;
+ return sort_key_write(sort_param, a);
+ }
+ sort_info->ft_buf=ft_buf;
+ goto word_init_ft_buf; /* no need to duplicate the code */
+ }
+ get_key_full_length_rdonly(val_off, ft_buf->lastkey);
+
+ if (mi_compare_text(sort_param->seg->charset,
+ ((uchar *)a)+1,a_len-1,
+ ft_buf->lastkey+1,val_off-1, 0, 0)==0)
+ {
+ if (!ft_buf->buf) /* store in second-level tree */
+ {
+ ft_buf->count++;
+ return sort_insert_key(sort_param,key_block,
+ ((uchar *)a)+a_len, HA_OFFSET_ERROR);
+ }
+
+ /* storing the key in the buffer. */
+ memcpy (ft_buf->buf, (char *)a+a_len, val_len);
+ ft_buf->buf+=val_len;
+ if (ft_buf->buf < ft_buf->end)
+ return 0;
+
+ /* converting to two-level tree */
+ p=ft_buf->lastkey+val_off;
+
+ while (key_block->inited)
+ key_block++;
+ sort_info->key_block=key_block;
+ sort_param->keyinfo=& sort_info->info->s->ft2_keyinfo;
+ ft_buf->count=(ft_buf->buf - p)/val_len;
+
+ /* flushing buffer to second-level tree */
+ for (error=0; !error && p < ft_buf->buf; p+= val_len)
+ error=sort_insert_key(sort_param,key_block,p,HA_OFFSET_ERROR);
+ ft_buf->buf=0;
+ return error;
+ }
+
+ /* flushing buffer */
+ if ((error=sort_ft_buf_flush(sort_param)))
+ return error;
+
+word_init_ft_buf:
+ a_len+=val_len;
+ memcpy(ft_buf->lastkey, a, a_len);
+ ft_buf->buf=ft_buf->lastkey+a_len;
+ /*
+ 32 is just a safety margin here
+ (at least max(val_len, sizeof(nod_flag)) should be there).
+ May be better performance could be achieved if we'd put
+ (sort_info->keyinfo->block_length-32)/XXX
+ instead.
+ TODO: benchmark the best value for XXX.
+ */
+ ft_buf->end=ft_buf->lastkey+ (sort_param->keyinfo->block_length-32);
+ return 0;
+} /* sort_ft_key_write */
+
+
+ /* get pointer to record from a key */
+
+static my_off_t get_record_for_key(MI_INFO *info, MI_KEYDEF *keyinfo,
+ uchar *key)
+{
+ return _mi_dpos(info,0,key+_mi_keylength(keyinfo,key));
+} /* get_record_for_key */
+
+
+ /* Insert a key in sort-key-blocks */
+
+static int sort_insert_key(MI_SORT_PARAM *sort_param,
+ register SORT_KEY_BLOCKS *key_block, uchar *key,
+ my_off_t prev_block)
+{
+ uint a_length,t_length,nod_flag;
+ my_off_t filepos,key_file_length;
+ uchar *anc_buff,*lastkey;
+ MI_KEY_PARAM s_temp;
+ MI_INFO *info;
+ MI_KEYDEF *keyinfo=sort_param->keyinfo;
+ SORT_INFO *sort_info= sort_param->sort_info;
+ MI_CHECK *param=sort_info->param;
+ DBUG_ENTER("sort_insert_key");
+
+ anc_buff=key_block->buff;
+ info=sort_info->info;
+ lastkey=key_block->lastkey;
+ nod_flag= (key_block == sort_info->key_block ? 0 :
+ info->s->base.key_reflength);
+
+ if (!key_block->inited)
+ {
+ key_block->inited=1;
+ if (key_block == sort_info->key_block_end)
+ {
+ mi_check_print_error(param,"To many key-block-levels; Try increasing sort_key_blocks");
+ DBUG_RETURN(1);
+ }
+ a_length=2+nod_flag;
+ key_block->end_pos=anc_buff+2;
+ lastkey=0; /* No previous key in block */
+ }
+ else
+ a_length=mi_getint(anc_buff);
+
+ /* Save pointer to previous block */
+ if (nod_flag)
+ _mi_kpointer(info,key_block->end_pos,prev_block);
+
+ t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
+ (uchar*) 0,lastkey,lastkey,key,
+ &s_temp);
+ (*keyinfo->store_key)(keyinfo, key_block->end_pos+nod_flag,&s_temp);
+ a_length+=t_length;
+ mi_putint(anc_buff,a_length,nod_flag);
+ key_block->end_pos+=t_length;
+ if (a_length <= keyinfo->block_length)
+ {
+ VOID(_mi_move_key(keyinfo,key_block->lastkey,key));
+ key_block->last_length=a_length-t_length;
+ DBUG_RETURN(0);
+ }
+
+ /* Fill block with end-zero and write filled block */
+ mi_putint(anc_buff,key_block->last_length,nod_flag);
+ bzero((byte*) anc_buff+key_block->last_length,
+ keyinfo->block_length- key_block->last_length);
+ key_file_length=info->state->key_file_length;
+ if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
+ DBUG_RETURN(1);
+
+ /* If we read the page from the key cache, we have to write it back to it */
+ if (key_file_length == info->state->key_file_length)
+ {
+ if (_mi_write_keypage(info, keyinfo, filepos, DFLT_INIT_HITS, anc_buff))
+ DBUG_RETURN(1);
+ }
+ else if (my_pwrite(info->s->kfile,(byte*) anc_buff,
+ (uint) keyinfo->block_length,filepos, param->myf_rw))
+ DBUG_RETURN(1);
+ DBUG_DUMP("buff",(byte*) anc_buff,mi_getint(anc_buff));
+
+ /* Write separator-key to block in next level */
+ if (sort_insert_key(sort_param,key_block+1,key_block->lastkey,filepos))
+ DBUG_RETURN(1);
+
+ /* clear old block and write new key in it */
+ key_block->inited=0;
+ DBUG_RETURN(sort_insert_key(sort_param, key_block,key,prev_block));
+} /* sort_insert_key */
+
+
+ /* Delete record when we found a duplicated key */
+
+static int sort_delete_record(MI_SORT_PARAM *sort_param)
+{
+ uint i;
+ int old_file,error;
+ uchar *key;
+ SORT_INFO *sort_info=sort_param->sort_info;
+ MI_CHECK *param=sort_info->param;
+ MI_INFO *info=sort_info->info;
+ DBUG_ENTER("sort_delete_record");
+
+ if ((param->testflag & (T_FORCE_UNIQUENESS|T_QUICK)) == T_QUICK)
+ {
+ mi_check_print_error(param,
+ "Quick-recover aborted; Run recovery without switch -q or with switch -qq");
+ DBUG_RETURN(1);
+ }
+ if (info->s->options & HA_OPTION_COMPRESS_RECORD)
+ {
+ mi_check_print_error(param,
+ "Recover aborted; Can't run standard recovery on compressed tables with errors in data-file. Use switch 'myisamchk --safe-recover' to fix it\n",stderr);;
+ DBUG_RETURN(1);
+ }
+
+ old_file=info->dfile;
+ info->dfile=info->rec_cache.file;
+ if (sort_info->current_key)
+ {
+ key=info->lastkey+info->s->base.max_key_length;
+ if ((error=(*info->s->read_rnd)(info,sort_param->record,info->lastpos,0)) &&
+ error != HA_ERR_RECORD_DELETED)
+ {
+ mi_check_print_error(param,"Can't read record to be removed");
+ info->dfile=old_file;
+ DBUG_RETURN(1);
+ }
+
+ for (i=0 ; i < sort_info->current_key ; i++)
+ {
+ uint key_length=_mi_make_key(info,i,key,sort_param->record,info->lastpos);
+ if (_mi_ck_delete(info,i,key,key_length))
+ {
+ mi_check_print_error(param,"Can't delete key %d from record to be removed",i+1);
+ info->dfile=old_file;
+ DBUG_RETURN(1);
+ }
+ }
+ if (param->calc_checksum)
+ param->glob_crc-=(*info->s->calc_checksum)(info, sort_param->record);
+ }
+ error=flush_io_cache(&info->rec_cache) || (*info->s->delete_record)(info);
+ info->dfile=old_file; /* restore actual value */
+ info->state->records--;
+ DBUG_RETURN(error);
+} /* sort_delete_record */
+
+ /* Fix all pending blocks and flush everything to disk */
+
+int flush_pending_blocks(MI_SORT_PARAM *sort_param)
+{
+ uint nod_flag,length;
+ my_off_t filepos,key_file_length;
+ SORT_KEY_BLOCKS *key_block;
+ SORT_INFO *sort_info= sort_param->sort_info;
+ myf myf_rw=sort_info->param->myf_rw;
+ MI_INFO *info=sort_info->info;
+ MI_KEYDEF *keyinfo=sort_param->keyinfo;
+ DBUG_ENTER("flush_pending_blocks");
+
+ filepos= HA_OFFSET_ERROR; /* if empty file */
+ nod_flag=0;
+ for (key_block=sort_info->key_block ; key_block->inited ; key_block++)
+ {
+ key_block->inited=0;
+ length=mi_getint(key_block->buff);
+ if (nod_flag)
+ _mi_kpointer(info,key_block->end_pos,filepos);
+ key_file_length=info->state->key_file_length;
+ bzero((byte*) key_block->buff+length, keyinfo->block_length-length);
+ if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
+ DBUG_RETURN(1);
+
+ /* If we read the page from the key cache, we have to write it back */
+ if (key_file_length == info->state->key_file_length)
+ {
+ if (_mi_write_keypage(info, keyinfo, filepos,
+ DFLT_INIT_HITS, key_block->buff))
+ DBUG_RETURN(1);
+ }
+ else if (my_pwrite(info->s->kfile,(byte*) key_block->buff,
+ (uint) keyinfo->block_length,filepos, myf_rw))
+ DBUG_RETURN(1);
+ DBUG_DUMP("buff",(byte*) key_block->buff,length);
+ nod_flag=1;
+ }
+ info->s->state.key_root[sort_param->key]=filepos; /* Last is root for tree */
+ DBUG_RETURN(0);
+} /* flush_pending_blocks */
+
+ /* alloc space and pointers for key_blocks */
+
+static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint blocks,
+ uint buffer_length)
+{
+ reg1 uint i;
+ SORT_KEY_BLOCKS *block;
+ DBUG_ENTER("alloc_key_blocks");
+
+ if (!(block=(SORT_KEY_BLOCKS*) my_malloc((sizeof(SORT_KEY_BLOCKS)+
+ buffer_length+IO_SIZE)*blocks,
+ MYF(0))))
+ {
+ mi_check_print_error(param,"Not enough memory for sort-key-blocks");
+ return(0);
+ }
+ for (i=0 ; i < blocks ; i++)
+ {
+ block[i].inited=0;
+ block[i].buff=(uchar*) (block+blocks)+(buffer_length+IO_SIZE)*i;
+ }
+ DBUG_RETURN(block);
+} /* alloc_key_blocks */
+
+
+ /* Check if file is almost full */
+
+int test_if_almost_full(MI_INFO *info)
+{
+ if (info->s->options & HA_OPTION_COMPRESS_RECORD)
+ return 0;
+ return (my_seek(info->s->kfile,0L,MY_SEEK_END,MYF(0))/10*9 >
+ (my_off_t) (info->s->base.max_key_file_length) ||
+ my_seek(info->dfile,0L,MY_SEEK_END,MYF(0))/10*9 >
+ (my_off_t) info->s->base.max_data_file_length);
+}
+
+ /* Recreate table with bigger more alloced record-data */
+
+int recreate_table(MI_CHECK *param, MI_INFO **org_info, char *filename)
+{
+ int error;
+ MI_INFO info;
+ MYISAM_SHARE share;
+ MI_KEYDEF *keyinfo,*key,*key_end;
+ HA_KEYSEG *keysegs,*keyseg;
+ MI_COLUMNDEF *recdef,*rec,*end;
+ MI_UNIQUEDEF *uniquedef,*u_ptr,*u_end;
+ MI_STATUS_INFO status_info;
+ uint unpack,key_parts;
+ ha_rows max_records;
+ ulonglong file_length,tmp_length;
+ MI_CREATE_INFO create_info;
+
+ error=1; /* Default error */
+ info= **org_info;
+ status_info= (*org_info)->state[0];
+ info.state= &status_info;
+ share= *(*org_info)->s;
+ unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
+ (param->testflag & T_UNPACK);
+ if (!(keyinfo=(MI_KEYDEF*) my_alloca(sizeof(MI_KEYDEF)*share.base.keys)))
+ return 0;
+ memcpy((byte*) keyinfo,(byte*) share.keyinfo,
+ (size_t) (sizeof(MI_KEYDEF)*share.base.keys));
+
+ key_parts= share.base.all_key_parts;
+ if (!(keysegs=(HA_KEYSEG*) my_alloca(sizeof(HA_KEYSEG)*
+ (key_parts+share.base.keys))))
+ {
+ my_afree((gptr) keyinfo);
+ return 1;
+ }
+ if (!(recdef=(MI_COLUMNDEF*)
+ my_alloca(sizeof(MI_COLUMNDEF)*(share.base.fields+1))))
+ {
+ my_afree((gptr) keyinfo);
+ my_afree((gptr) keysegs);
+ return 1;
+ }
+ if (!(uniquedef=(MI_UNIQUEDEF*)
+ my_alloca(sizeof(MI_UNIQUEDEF)*(share.state.header.uniques+1))))
+ {
+ my_afree((gptr) recdef);
+ my_afree((gptr) keyinfo);
+ my_afree((gptr) keysegs);
+ return 1;
+ }
+
+ /* Copy the column definitions */
+ memcpy((byte*) recdef,(byte*) share.rec,
+ (size_t) (sizeof(MI_COLUMNDEF)*(share.base.fields+1)));
+ for (rec=recdef,end=recdef+share.base.fields; rec != end ; rec++)
+ {
+ if (unpack && !(share.options & HA_OPTION_PACK_RECORD) &&
+ rec->type != FIELD_BLOB &&
+ rec->type != FIELD_VARCHAR &&
+ rec->type != FIELD_CHECK)
+ rec->type=(int) FIELD_NORMAL;
+ }
+
+ /* Change the new key to point at the saved key segments */
+ memcpy((byte*) keysegs,(byte*) share.keyparts,
+ (size_t) (sizeof(HA_KEYSEG)*(key_parts+share.base.keys+
+ share.state.header.uniques)));
+ keyseg=keysegs;
+ for (key=keyinfo,key_end=keyinfo+share.base.keys; key != key_end ; key++)
+ {
+ key->seg=keyseg;
+ for (; keyseg->type ; keyseg++)
+ {
+ if (param->language)
+ keyseg->language=param->language; /* change language */
+ }
+ keyseg++; /* Skip end pointer */
+ }
+
+ /* Copy the unique definitions and change them to point at the new key
+ segments*/
+ memcpy((byte*) uniquedef,(byte*) share.uniqueinfo,
+ (size_t) (sizeof(MI_UNIQUEDEF)*(share.state.header.uniques)));
+ for (u_ptr=uniquedef,u_end=uniquedef+share.state.header.uniques;
+ u_ptr != u_end ; u_ptr++)
+ {
+ u_ptr->seg=keyseg;
+ keyseg+=u_ptr->keysegs+1;
+ }
+ if (share.options & HA_OPTION_COMPRESS_RECORD)
+ share.base.records=max_records=info.state->records;
+ else if (share.base.min_pack_length)
+ max_records=(ha_rows) (my_seek(info.dfile,0L,MY_SEEK_END,MYF(0)) /
+ (ulong) share.base.min_pack_length);
+ else
+ max_records=0;
+ unpack= (share.options & HA_OPTION_COMPRESS_RECORD) &&
+ (param->testflag & T_UNPACK);
+ share.options&= ~HA_OPTION_TEMP_COMPRESS_RECORD;
+
+ file_length=(ulonglong) my_seek(info.dfile,0L,MY_SEEK_END,MYF(0));
+ tmp_length= file_length+file_length/10;
+ set_if_bigger(file_length,param->max_data_file_length);
+ set_if_bigger(file_length,tmp_length);
+ set_if_bigger(file_length,(ulonglong) share.base.max_data_file_length);
+
+ VOID(mi_close(*org_info));
+ bzero((char*) &create_info,sizeof(create_info));
+ create_info.max_rows=max(max_records,share.base.records);
+ create_info.reloc_rows=share.base.reloc;
+ create_info.old_options=(share.options |
+ (unpack ? HA_OPTION_TEMP_COMPRESS_RECORD : 0));
+
+ create_info.data_file_length=file_length;
+ create_info.auto_increment=share.state.auto_increment;
+ create_info.raid_type= share.base.raid_type;
+ create_info.raid_chunks= share.base.raid_chunks;
+ create_info.raid_chunksize= share.base.raid_chunksize;
+ create_info.language = (param->language ? param->language :
+ share.state.header.language);
+ create_info.key_file_length= status_info.key_file_length;
+ /* We don't have to handle symlinks here because we are using
+ HA_DONT_TOUCH_DATA */
+ if (mi_create(filename,
+ share.base.keys - share.state.header.uniques,
+ keyinfo, share.base.fields, recdef,
+ share.state.header.uniques, uniquedef,
+ &create_info,
+ HA_DONT_TOUCH_DATA))
+ {
+ mi_check_print_error(param,"Got error %d when trying to recreate indexfile",my_errno);
+ goto end;
+ }
+ *org_info=mi_open(filename,O_RDWR,
+ (param->testflag & T_WAIT_FOREVER) ? HA_OPEN_WAIT_IF_LOCKED :
+ (param->testflag & T_DESCRIPT) ? HA_OPEN_IGNORE_IF_LOCKED :
+ HA_OPEN_ABORT_IF_LOCKED);
+ if (!*org_info)
+ {
+ mi_check_print_error(param,"Got error %d when trying to open re-created indexfile",
+ my_errno);
+ goto end;
+ }
+ /* We are modifing */
+ (*org_info)->s->options&= ~HA_OPTION_READ_ONLY_DATA;
+ VOID(_mi_readinfo(*org_info,F_WRLCK,0));
+ (*org_info)->state->records=info.state->records;
+ if (share.state.create_time)
+ (*org_info)->s->state.create_time=share.state.create_time;
+ (*org_info)->s->state.unique=(*org_info)->this_unique=
+ share.state.unique;
+ (*org_info)->s->state.checksum=share.state.checksum;
+ (*org_info)->state->del=info.state->del;
+ (*org_info)->s->state.dellink=share.state.dellink;
+ (*org_info)->state->empty=info.state->empty;
+ (*org_info)->state->data_file_length=info.state->data_file_length;
+ if (update_state_info(param,*org_info,UPDATE_TIME | UPDATE_STAT |
+ UPDATE_OPEN_COUNT))
+ goto end;
+ error=0;
+end:
+ my_afree((gptr) uniquedef);
+ my_afree((gptr) keyinfo);
+ my_afree((gptr) recdef);
+ my_afree((gptr) keysegs);
+ return error;
+}
+
+
+ /* write suffix to data file if neaded */
+
+int write_data_suffix(SORT_INFO *sort_info, my_bool fix_datafile)
+{
+ MI_INFO *info=sort_info->info;
+
+ if (info->s->options & HA_OPTION_COMPRESS_RECORD && fix_datafile)
+ {
+ char buff[MEMMAP_EXTRA_MARGIN];
+ bzero(buff,sizeof(buff));
+ if (my_b_write(&info->rec_cache,buff,sizeof(buff)))
+ {
+ mi_check_print_error(sort_info->param,
+ "%d when writing to datafile",my_errno);
+ return 1;
+ }
+ sort_info->param->read_cache.end_of_file+=sizeof(buff);
+ }
+ return 0;
+}
+
+ /* Update state and myisamchk_time of indexfile */
+
+int update_state_info(MI_CHECK *param, MI_INFO *info,uint update)
+{
+ MYISAM_SHARE *share=info->s;
+
+ if (update & UPDATE_OPEN_COUNT)
+ {
+ share->state.open_count=0;
+ share->global_changed=0;
+ }
+ if (update & UPDATE_STAT)
+ {
+ uint i, key_parts= mi_uint2korr(share->state.header.key_parts);
+ share->state.rec_per_key_rows=info->state->records;
+ share->state.changed&= ~STATE_NOT_ANALYZED;
+ if (info->state->records)
+ {
+ for (i=0; i<key_parts; i++)
+ {
+ if (!(share->state.rec_per_key_part[i]=param->rec_per_key_part[i]))
+ share->state.changed|= STATE_NOT_ANALYZED;
+ }
+ }
+ }
+ if (update & (UPDATE_STAT | UPDATE_SORT | UPDATE_TIME | UPDATE_AUTO_INC))
+ {
+ if (update & UPDATE_TIME)
+ {
+ share->state.check_time= (long) time((time_t*) 0);
+ if (!share->state.create_time)
+ share->state.create_time=share->state.check_time;
+ }
+ if (mi_state_info_write(share->kfile,&share->state,1+2))
+ goto err;
+ share->changed=0;
+ }
+ { /* Force update of status */
+ int error;
+ uint r_locks=share->r_locks,w_locks=share->w_locks;
+ share->r_locks= share->w_locks= share->tot_locks= 0;
+ error=_mi_writeinfo(info,WRITEINFO_NO_UNLOCK);
+ share->r_locks=r_locks;
+ share->w_locks=w_locks;
+ share->tot_locks=r_locks+w_locks;
+ if (!error)
+ return 0;
+ }
+err:
+ mi_check_print_error(param,"%d when updating keyfile",my_errno);
+ return 1;
+}
+
+ /*
+ Update auto increment value for a table
+ When setting the 'repair_only' flag we only want to change the
+ old auto_increment value if its wrong (smaller than some given key).
+ The reason is that we shouldn't change the auto_increment value
+ for a table without good reason when only doing a repair; If the
+ user have inserted and deleted rows, the auto_increment value
+ may be bigger than the biggest current row and this is ok.
+
+ If repair_only is not set, we will update the flag to the value in
+ param->auto_increment is bigger than the biggest key.
+ */
+
+void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
+ my_bool repair_only)
+{
+ byte *record;
+ if (!info->s->base.auto_key ||
+ !(((ulonglong) 1 << (info->s->base.auto_key-1)
+ & info->s->state.key_map)))
+ {
+ if (!(param->testflag & T_VERY_SILENT))
+ mi_check_print_info(param,
+ "Table: %s doesn't have an auto increment key\n",
+ param->isam_file_name);
+ return;
+ }
+ if (!(param->testflag & T_SILENT) &&
+ !(param->testflag & T_REP))
+ printf("Updating MyISAM file: %s\n", param->isam_file_name);
+ /*
+ We have to use an allocated buffer instead of info->rec_buff as
+ _mi_put_key_in_record() may use info->rec_buff
+ */
+ if (!(record= (byte*) my_malloc((uint) info->s->base.pack_reclength,
+ MYF(0))))
+ {
+ mi_check_print_error(param,"Not enough memory for extra record");
+ return;
+ }
+
+ mi_extra(info,HA_EXTRA_KEYREAD,0);
+ if (mi_rlast(info, record, info->s->base.auto_key-1))
+ {
+ if (my_errno != HA_ERR_END_OF_FILE)
+ {
+ mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
+ my_free((char*) record, MYF(0));
+ mi_check_print_error(param,"%d when reading last record",my_errno);
+ return;
+ }
+ if (!repair_only)
+ info->s->state.auto_increment=param->auto_increment_value;
+ }
+ else
+ {
+ ulonglong auto_increment= (repair_only ? info->s->state.auto_increment :
+ param->auto_increment_value);
+ info->s->state.auto_increment=0;
+ update_auto_increment(info, record);
+ set_if_bigger(info->s->state.auto_increment,auto_increment);
+ }
+ mi_extra(info,HA_EXTRA_NO_KEYREAD,0);
+ my_free((char*) record, MYF(0));
+ update_state_info(param, info, UPDATE_AUTO_INC);
+ return;
+}
+
+ /* calculate unique keys for each part key */
+
+void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
+ ulonglong *unique, ulonglong records)
+{
+ ulonglong count=0,tmp;
+ uint parts;
+ for (parts=0 ; parts < keyinfo->keysegs ; parts++)
+ {
+ count+=unique[parts];
+ if (count == 0)
+ tmp=records;
+ else
+ tmp= (records + (count+1)/2) / (count+1);
+ /* for some weird keys (e.g. FULLTEXT) tmp can be <1 here.
+ let's ensure it is not */
+ set_if_bigger(tmp,1);
+ if (tmp >= (ulonglong) ~(ulong) 0)
+ tmp=(ulonglong) ~(ulong) 0;
+ *rec_per_key_part=(ulong) tmp;
+ rec_per_key_part++;
+ }
+}
+
+
+static ha_checksum mi_byte_checksum(const byte *buf, uint length)
+{
+ ha_checksum crc;
+ const byte *end=buf+length;
+ for (crc=0; buf != end; buf++)
+ crc=((crc << 1) + *((uchar*) buf)) +
+ test(crc & (((ha_checksum) 1) << (8*sizeof(ha_checksum)-1)));
+ return crc;
+}
+
+static my_bool mi_too_big_key_for_sort(MI_KEYDEF *key, ha_rows rows)
+{
+ uint key_maxlength=key->maxlength;
+ if (key->flag & HA_FULLTEXT)
+ {
+ uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT*
+ key->seg->charset->mbmaxlen;
+ key_maxlength+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN;
+ }
+ return (key->flag & HA_SPATIAL) ||
+ (key->flag & (HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY | HA_FULLTEXT) &&
+ ((ulonglong) rows * key_maxlength >
+ (ulonglong) myisam_max_temp_length));
+}
+
+/*
+ Deactivate all not unique index that can be recreated fast
+ These include packed keys on which sorting will use more temporary
+ space than the max allowed file length or for which the unpacked keys
+ will take much more space than packed keys.
+ Note that 'rows' may be zero for the case when we don't know how many
+ rows we will put into the file.
+ */
+
+void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows)
+{
+ MYISAM_SHARE *share=info->s;
+ MI_KEYDEF *key=share->keyinfo;
+ uint i;
+
+ DBUG_ASSERT(info->state->records == 0 &&
+ (!rows || rows >= MI_MIN_ROWS_TO_DISABLE_INDEXES));
+ for (i=0 ; i < share->base.keys ; i++,key++)
+ {
+ if (!(key->flag & (HA_NOSAME | HA_SPATIAL | HA_AUTO_KEY)) &&
+ ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1)
+ {
+ share->state.key_map&= ~ ((ulonglong) 1 << i);
+ info->update|= HA_STATE_CHANGED;
+ }
+ }
+}
+
+
+/*
+ Return TRUE if we can use repair by sorting
+ One can set the force argument to force to use sorting
+ even if the temporary file would be quite big!
+*/
+
+my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows,
+ ulonglong key_map, my_bool force)
+{
+ MYISAM_SHARE *share=info->s;
+ MI_KEYDEF *key=share->keyinfo;
+ uint i;
+
+ /*
+ mi_repair_by_sort only works if we have at least one key. If we don't
+ have any keys, we should use the normal repair.
+ */
+ if (!key_map)
+ return FALSE; /* Can't use sort */
+ for (i=0 ; i < share->base.keys ; i++,key++)
+ {
+ if (!force && mi_too_big_key_for_sort(key,rows))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static void
+set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share)
+{
+ if ((sort_info->new_data_file_type=share->data_file_type) ==
+ COMPRESSED_RECORD && sort_info->param->testflag & T_UNPACK)
+ {
+ MYISAM_SHARE tmp;
+
+ if (share->options & HA_OPTION_PACK_RECORD)
+ sort_info->new_data_file_type = DYNAMIC_RECORD;
+ else
+ sort_info->new_data_file_type = STATIC_RECORD;
+
+ /* Set delete_function for sort_delete_record() */
+ memcpy((char*) &tmp, share, sizeof(*share));
+ tmp.options= ~HA_OPTION_COMPRESS_RECORD;
+ mi_setup_functions(&tmp);
+ share->delete_record=tmp.delete_record;
+ }
+}
diff --git a/storage/myisam/mi_checksum.c b/storage/myisam/mi_checksum.c
new file mode 100644
index 00000000000..33a51068fb0
--- /dev/null
+++ b/storage/myisam/mi_checksum.c
@@ -0,0 +1,65 @@
+/* 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 */
+
+/* Calculate a checksum for a row */
+
+#include "myisamdef.h"
+
+ha_checksum mi_checksum(MI_INFO *info, const byte *buf)
+{
+ uint i;
+ ha_checksum crc=0;
+ MI_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=_mi_calc_blob_length(rec->length-
+ mi_portable_sizeof_char_ptr,
+ buf);
+ memcpy((char*) &pos, buf+rec->length- mi_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 mi_static_checksum(MI_INFO *info, const byte *pos)
+{
+ return my_checksum(0, pos, info->s->base.reclength);
+}
diff --git a/storage/myisam/mi_close.c b/storage/myisam/mi_close.c
new file mode 100644
index 00000000000..62f5617de1a
--- /dev/null
+++ b/storage/myisam/mi_close.c
@@ -0,0 +1,118 @@
+/* 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 */
+
+/* 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 "myisamdef.h"
+
+int mi_close(register MI_INFO *info)
+{
+ int error=0,flag;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("mi_close");
+ DBUG_PRINT("enter",("base: %lx reopen: %u locks: %u",
+ info,(uint) share->reopen, (uint) share->tot_locks));
+
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ if (info->lock_type == F_EXTRA_LCK)
+ info->lock_type=F_UNLCK; /* HA_EXTRA_NO_USER_CHANGE */
+
+ if (share->reopen == 1 && share->kfile >= 0)
+ _mi_decrement_open_count(info);
+
+ if (info->lock_type != F_UNLCK)
+ {
+ if (mi_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;
+ myisam_open_list=list_delete(myisam_open_list,&info->open_list);
+ pthread_mutex_unlock(&share->intern_lock);
+
+ my_free(mi_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 && mi_is_crashed(info))
+ mi_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)
+ _mi_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;
+ 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_myisam);
+ if (info->dfile >= 0 && my_close(info->dfile,MYF(0)))
+ error = my_errno;
+
+ myisam_log_command(MI_LOG_CLOSE,info,NULL,0,error);
+ my_free((gptr) info,MYF(0));
+
+ if (error)
+ {
+ DBUG_RETURN(my_errno=error);
+ }
+ DBUG_RETURN(0);
+} /* mi_close */
diff --git a/storage/myisam/mi_create.c b/storage/myisam/mi_create.c
new file mode 100644
index 00000000000..8635d6bcf36
--- /dev/null
+++ b/storage/myisam/mi_create.c
@@ -0,0 +1,792 @@
+/* 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 */
+
+/* Create a MyISAM table */
+
+#include "ftdefs.h"
+#include "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 isamchk
+ */
+
+int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
+ uint columns, MI_COLUMNDEF *recinfo,
+ uint uniques, MI_UNIQUEDEF *uniquedefs,
+ MI_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;
+ 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;
+ MYISAM_SHARE share;
+ MI_KEYDEF *keydef,tmp_keydef;
+ MI_UNIQUEDEF *uniquedef;
+ HA_KEYSEG *keyseg,tmp_keyseg;
+ MI_COLUMNDEF *rec;
+ ulong *rec_per_key_part;
+ my_off_t key_root[MI_MAX_POSSIBLE_KEY],key_del[MI_MAX_KEY_BLOCK_SIZE];
+ MI_CREATE_INFO tmp_create_info;
+ DBUG_ENTER("mi_create");
+
+ if (!ci)
+ {
+ bzero((char*) &tmp_create_info,sizeof(tmp_create_info));
+ ci=&tmp_create_info;
+ }
+
+ if (keys + uniques > MI_MAX_KEY || columns == 0)
+ {
+ DBUG_RETURN(my_errno=HA_WRONG_CREATE_OPTION);
+ }
+ LINT_INIT(dfile);
+ LINT_INIT(file);
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ 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)*MI_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+mi_portable_sizeof_char_ptr)
+ pack_reclength= INT_MAX32;
+ else
+ pack_reclength+=(1 << ((rec->length-mi_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;
+
+ 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)
+ {
+ if (ci->max_rows == 0 || pack_reclength == INT_MAX32)
+ ci->data_file_length= INT_MAX32-1; /* Should be enough */
+ else if ((~(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=mi_get_pointer_length(ci->data_file_length,myisam_data_pointer_size);
+ else
+ pointer=mi_get_pointer_length(ci->max_rows,myisam_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= myisam_block_size;
+ max_key_length= MI_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 myisamchk - i.e. table structure was taken from
+ MYI file and SPATIAL key *does have* additional sp_segs keysegs.
+ We'd better delete them now
+ */
+ keydef->keysegs-=sp_segs;
+ }
+
+ 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 > MI_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;
+ keydef->block_length= MI_BLOCK_SIZE(length-real_length_diff,
+ pointer,MI_MAX_KEYPTR_SIZE);
+ if (keydef->block_length > MI_MAX_KEY_BLOCK_LENGTH ||
+ length >= MI_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/MI_MIN_KEY_BLOCK_LENGTH ; i-- ; )
+ key_del[i]=HA_OFFSET_ERROR;
+
+ unique_key_parts=0;
+ offset=reclength-uniques*MI_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) myisam_block_size-5)/
+ ((MI_UNIQUE_HASH_LENGTH + pointer)*2)))*
+ (ulong) myisam_block_size;
+ }
+ keys+=uniques; /* Each unique has 1 key */
+ key_segs+=uniques; /* Each unique has 1 key seg */
+
+ base_pos=(MI_STATE_INFO_SIZE + keys * MI_STATE_KEY_SIZE +
+ max_key_block_length/MI_MIN_KEY_BLOCK_LENGTH*
+ MI_STATE_KEYBLOCK_SIZE+
+ key_segs*MI_STATE_KEYSEG_SIZE);
+ info_length=base_pos+(uint) (MI_BASE_INFO_SIZE+
+ keys * MI_KEYDEF_SIZE+
+ uniques * MI_UNIQUEDEF_SIZE +
+ (key_segs + unique_key_parts)*HA_KEYSEG_SIZE+
+ columns*MI_COLUMNDEF_SIZE);
+
+ bmove(share.state.header.file_version,(byte*) myisam_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,MI_STATE_INFO_SIZE);
+ mi_int2store(share.state.header.base_info_length,MI_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=max_key_block_length/MI_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 *
+ MI_INDEX_BLOCK_MARGIN) / MI_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 myisampack.c:save_state)
+ */
+ share.base.key_reflength=
+ mi_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);
+
+ share.state.key_map = ((ulonglong) 1 << keys)-1;
+ share.base.keystart = share.state.state.key_file_length=
+ MY_ALIGN(info_length, myisam_block_size);
+ 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 < MI_EXTEND_BLOCK_LENGTH &&
+ ! share.base.blobs) ?
+ max(share.base.pack_reclength,MI_MIN_BLOCK_LENGTH) :
+ MI_EXTEND_BLOCK_LENGTH;
+ if (! (flags & HA_DONT_TOUCH_DATA))
+ share.state.create_time= (long) time((time_t*) 0);
+
+ if (ci->index_file_name)
+ {
+ fn_format(filename, ci->index_file_name,"",MI_NAME_IEXT,4);
+ fn_format(linkname,name, "",MI_NAME_IEXT,4);
+ 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,"",MI_NAME_IEXT,(4+ (flags & HA_DONT_TOUCH_DATA) ?
+ 32 : 0));
+ linkname_ptr=0;
+ /* Replace the current file */
+ create_flag=MY_DELETE_OLD;
+ }
+
+ /*
+ If a MRG_MyISAM table is in use, the mapped MyISAM 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 (test_if_reopen(filename))
+ {
+ my_printf_error(0, "MyISAM 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,"",MI_NAME_DEXT,2+4);
+ 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)
+ {
+ fn_format(filename, ci->data_file_name,"",MI_NAME_DEXT,4);
+ fn_format(linkname, name, "",MI_NAME_DEXT,4);
+ linkname_ptr=linkname;
+ create_flag=0;
+ }
+ else
+ {
+ fn_format(filename,name,"",MI_NAME_DEXT,4);
+ 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;
+ }
+
+ if (mi_state_info_write(file, &share.state, 2) ||
+ mi_base_info_write(file, &share.base))
+ goto err;
+#ifndef DBUG_OFF
+ if ((uint) my_tell(file,MYF(0)) != base_pos+ MI_BASE_INFO_SIZE)
+ {
+ uint pos=(uint) my_tell(file,MYF(0));
+ DBUG_PRINT("warning",("base_length: %d != used_length: %d",
+ base_pos+ MI_BASE_INFO_SIZE, pos));
+ }
+#endif
+
+ /* 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 (mi_keydef_write(file, &keydefs[i]))
+ goto err;
+ for (j=0 ; j < keydefs[i].keysegs-sp_segs ; j++)
+ if (mi_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 (mi_keyseg_write(file, &sseg))
+ goto err;
+ }
+#endif
+ }
+ /* Create extra keys for unique definitions */
+ offset=reclength-uniques*MI_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= myisam_block_size;
+ tmp_keydef.keylength= MI_UNIQUE_HASH_LENGTH + pointer;
+ tmp_keydef.minlength=tmp_keydef.maxlength=tmp_keydef.keylength;
+ tmp_keyseg.type= MI_UNIQUE_HASH_TYPE;
+ tmp_keyseg.length= MI_UNIQUE_HASH_LENGTH;
+ tmp_keyseg.start= offset;
+ offset+= MI_UNIQUE_HASH_LENGTH;
+ if (mi_keydef_write(file,&tmp_keydef) ||
+ mi_keyseg_write(file,(&tmp_keyseg)))
+ goto err;
+ }
+
+ /* Save unique definition */
+ for (i=0 ; i < share.state.header.uniques ; i++)
+ {
+ HA_KEYSEG *keyseg_end;
+ keyseg= uniquedefs[i].seg;
+ if (mi_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 (mi_keyseg_write(file, keyseg))
+ goto err;
+ }
+ }
+ for (i=0 ; i < share.base.fields ; i++)
+ if (mi_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 */
+ 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_myisam);
+ 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_myisam);
+ 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,"",MI_NAME_DEXT,2+4),
+ 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,"",MI_NAME_IEXT,2+4),
+ MYF(0));
+ }
+ my_free((char*) rec_per_key_part, MYF(0));
+ DBUG_RETURN(my_errno=save_errno); /* return the fatal errno */
+}
+
+
+uint mi_get_pointer_length(ulonglong file_length, uint def)
+{
+ if (file_length) /* If not default */
+ {
+ if (file_length >= (longlong) 1 << 56)
+ def=8;
+ 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;
+}
diff --git a/storage/myisam/mi_dbug.c b/storage/myisam/mi_dbug.c
new file mode 100644
index 00000000000..e782d21afe7
--- /dev/null
+++ b/storage/myisam/mi_dbug.c
@@ -0,0 +1,193 @@
+/* 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 */
+
+/* Support rutiner with are using with dbug */
+
+#include "myisamdef.h"
+
+ /* Print a key in user understandable format */
+
+void _mi_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)
+ {
+ if (!*key)
+ {
+ fprintf(stream,"NULL");
+ continue;
+ }
+ key++;
+ }
+
+ 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 check_table_is_closed(const char *name, const char *where)
+{
+ char filename[FN_REFLEN];
+ LIST *pos;
+ DBUG_ENTER("check_table_is_closed");
+
+ (void) fn_format(filename,name,"",MI_NAME_IEXT,4+16+32);
+ for (pos=myisam_open_list ; pos ; pos=pos->next)
+ {
+ MI_INFO *info=(MI_INFO*) pos->data;
+ MYISAM_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 */
diff --git a/storage/myisam/mi_delete.c b/storage/myisam/mi_delete.c
new file mode 100644
index 00000000000..cc4a17182f7
--- /dev/null
+++ b/storage/myisam/mi_delete.c
@@ -0,0 +1,887 @@
+/* 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 */
+
+/* Remove a row from a MyISAM table */
+
+#include "fulltext.h"
+#include "rt_index.h"
+
+static int d_search(MI_INFO *info,MI_KEYDEF *keyinfo,uint comp_flag,
+ uchar *key,uint key_length,my_off_t page,uchar *anc_buff);
+static int del(MI_INFO *info,MI_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(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *anc_buff,
+ my_off_t leaf_page,uchar *leaf_buff,uchar *keypos);
+static uint remove_key(MI_KEYDEF *keyinfo,uint nod_flag,uchar *keypos,
+ uchar *lastkey,uchar *page_end,
+ my_off_t *next_block);
+static int _mi_ck_real_delete(register MI_INFO *info,MI_KEYDEF *keyinfo,
+ uchar *key, uint key_length, my_off_t *root);
+
+
+int mi_delete(MI_INFO *info,const byte *record)
+{
+ uint i;
+ uchar *old_key;
+ int save_errno;
+ char lastpos[8];
+
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("mi_delete");
+
+ /* Test if record is in datafile */
+
+ DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage",
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ DBUG_RETURN(my_errno= HA_ERR_CRASHED););
+ DBUG_EXECUTE_IF("my_error_test_undefined_error",
+ mi_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 (_mi_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 (_mi_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 (((ulonglong) 1 << i) & info->s->state.key_map)
+ {
+ info->s->keyinfo[i].version++;
+ if (info->s->keyinfo[i].flag & HA_FULLTEXT )
+ {
+ if (_mi_ft_del(info,i,(char*) old_key,record,info->lastpos))
+ goto err;
+ }
+ else
+ {
+ if (info->s->keyinfo[i].ck_delete(info,i,old_key,
+ _mi_make_key(info,i,old_key,record,info->lastpos)))
+ goto err;
+ }
+ }
+ }
+
+ if ((*share->delete_record)(info))
+ goto err; /* Remove record from database */
+ info->s->state.checksum-=info->checksum;
+
+ info->update= HA_STATE_CHANGED+HA_STATE_DELETED+HA_STATE_ROW_CHANGED;
+ info->state->records--;
+
+ mi_sizestore(lastpos,info->lastpos);
+ myisam_log_command(MI_LOG_DELETE,info,(byte*) lastpos,sizeof(lastpos),0);
+ VOID(_mi_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);
+ myisam_log_command(MI_LOG_DELETE,info,(byte*) lastpos, sizeof(lastpos),0);
+ if (save_errno != HA_ERR_RECORD_CHANGED)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info); /* mark table crashed */
+ }
+ VOID(_mi_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)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ }
+
+ DBUG_RETURN(my_errno);
+} /* mi_delete */
+
+
+ /* Remove a key from the btree index */
+
+int _mi_ck_delete(register MI_INFO *info, uint keynr, uchar *key,
+ uint key_length)
+{
+ return _mi_ck_real_delete(info, info->s->keyinfo+keynr, key, key_length,
+ &info->s->state.key_root[keynr]);
+} /* _mi_ck_delete */
+
+
+static int _mi_ck_real_delete(register MI_INFO *info, MI_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("_mi_ck_real_delete");
+
+ if ((old_root=*root) == HA_OFFSET_ERROR)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ DBUG_RETURN(my_errno=HA_ERR_CRASHED);
+ }
+ if (!(root_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
+ MI_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 (!_mi_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_SAME),
+ key,key_length,old_root,root_buff)) >0)
+ {
+ if (error == 2)
+ {
+ DBUG_PRINT("test",("Enlarging of root when deleting"));
+ error=_mi_enlarge_root(info,keyinfo,key,root);
+ }
+ else /* error == 1 */
+ {
+ if (mi_getint(root_buff) <= (nod_flag=mi_test_if_nod(root_buff))+3)
+ {
+ error=0;
+ if (nod_flag)
+ *root=_mi_kpos(nod_flag,root_buff+2+nod_flag);
+ else
+ *root=HA_OFFSET_ERROR;
+ if (_mi_dispose(info,keyinfo,old_root,DFLT_INIT_HITS))
+ error= -1;
+ }
+ else
+ error=_mi_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);
+} /* _mi_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 MI_INFO *info, register MI_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[MI_MAX_KEY_BUFF];
+ DBUG_ENTER("d_search");
+ DBUG_DUMP("page",(byte*) anc_buff,mi_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 == MI_FOUND_WRONG_KEY)
+ {
+ DBUG_PRINT("error",("Found wrong key"));
+ DBUG_RETURN(-1);
+ }
+ nod_flag=mi_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)))
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno= HA_ERR_CRASHED;
+ DBUG_RETURN(-1);
+ }
+ root=_mi_dpos(info,nod_flag,kpos);
+ if (subkeys == -1)
+ {
+ /* the last entry in sub-tree */
+ _mi_dispose(info, keyinfo, root,DFLT_INIT_HITS);
+ /* 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=_mi_ck_real_delete(info, &info->s->ft2_keyinfo,
+ key, HA_FT_WLEN, &root);
+ _mi_dpointer(info, kpos+HA_FT_WLEN, root);
+ subkeys++;
+ ft_intXstore(kpos, subkeys);
+ if (!ret_value)
+ ret_value=_mi_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=_mi_kpos(nod_flag,keypos);
+ if (!(leaf_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
+ MI_MAX_KEY_BUFF*2)))
+ {
+ DBUG_PRINT("error",("Couldn't allocate memory"));
+ my_errno=ENOMEM;
+ DBUG_PRINT("exit",("Return: %d",-1));
+ DBUG_RETURN(-1);
+ }
+ if (!_mi_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"));
+ mi_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=mi_getint(anc_buff);
+ if (!(tmp= remove_key(keyinfo,nod_flag,keypos,lastkey,anc_buff+length,
+ &next_block)))
+ goto err;
+
+ length-= tmp;
+
+ mi_putint(anc_buff,length,nod_flag);
+ if (!nod_flag)
+ { /* On leaf page */
+ if (_mi_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 ? MI_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 (!_mi_get_last_key(info,keyinfo,anc_buff,lastkey,keypos,&length))
+ {
+ goto err;
+ }
+ ret_value=_mi_insert(info,keyinfo,key,anc_buff,keypos,lastkey,
+ (uchar*) 0,(uchar*) 0,(my_off_t) 0,(my_bool) 0);
+ }
+ }
+ if (ret_value == 0 && mi_getint(anc_buff) > keyinfo->block_length)
+ {
+ save_flag=1;
+ ret_value=_mi_split_page(info,keyinfo,key,anc_buff,lastkey,0) | 2;
+ }
+ if (save_flag && ret_value != 1)
+ ret_value|=_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,anc_buff);
+ else
+ {
+ DBUG_DUMP("page",(byte*) anc_buff,mi_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 MI_INFO *info, register MI_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[MI_MAX_KEY_BUFF],*endpos,*next_buff,*key_start, *prev_key;
+ MYISAM_SHARE *share=info->s;
+ MI_KEY_PARAM s_temp;
+ DBUG_ENTER("del");
+ DBUG_PRINT("enter",("leaf_page: %ld keypos: %lx",leaf_page,keypos));
+ DBUG_DUMP("leaf_buff",(byte*) leaf_buff,mi_getint(leaf_buff));
+
+ endpos=leaf_buff+mi_getint(leaf_buff);
+ if (!(key_start=_mi_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
+ &tmp)))
+ DBUG_RETURN(-1);
+
+ if ((nod_flag=mi_test_if_nod(leaf_buff)))
+ {
+ next_page= _mi_kpos(nod_flag,endpos);
+ if (!(next_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
+ MI_MAX_KEY_BUFF*2)))
+ DBUG_RETURN(-1);
+ if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,next_buff,0))
+ ret_value= -1;
+ else
+ {
+ DBUG_DUMP("next_page",(byte*) next_buff,mi_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+mi_getint(leaf_buff);
+ if (ret_value == 1)
+ {
+ ret_value=underflow(info,keyinfo,leaf_buff,next_page,
+ next_buff,endpos);
+ if (ret_value == 0 && mi_getint(leaf_buff) > keyinfo->block_length)
+ {
+ ret_value=_mi_split_page(info,keyinfo,key,leaf_buff,ret_key,0) | 2;
+ }
+ }
+ else
+ {
+ DBUG_PRINT("test",("Inserting of key when deleting"));
+ if (_mi_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos,
+ &tmp))
+ goto err;
+ ret_value=_mi_insert(info,keyinfo,key,leaf_buff,endpos,keybuff,
+ (uchar*) 0,(uchar*) 0,(my_off_t) 0,0);
+ }
+ }
+ if (_mi_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 */
+
+ mi_putint(leaf_buff,key_start-leaf_buff,nod_flag);
+ if (_mi_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=mi_getint(anc_buff);
+ endpos=anc_buff+a_length;
+ if (keypos != anc_buff+2+share->base.key_reflength &&
+ !_mi_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;
+ _mi_kpointer(info,keypos - share->base.key_reflength,next_block);
+ mi_putint(anc_buff,a_length+length,share->base.key_reflength);
+
+ DBUG_RETURN( mi_getint(leaf_buff) <=
+ (info->quick_mode ? MI_MIN_KEYBLOCK_LENGTH :
+ (uint) keyinfo->underflow_block_length));
+err:
+ DBUG_RETURN(-1);
+} /* del */
+
+
+ /* Balances adjacent pages if underflow occours */
+
+static int underflow(register MI_INFO *info, register MI_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[MI_MAX_KEY_BUFF],leaf_key[MI_MAX_KEY_BUFF],
+ *buff,*endpos,*next_keypos,*anc_pos,*half_pos,*temp_pos,*prev_key,
+ *after_key;
+ MI_KEY_PARAM s_temp;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("underflow");
+ DBUG_PRINT("enter",("leaf_page: %ld keypos: %lx",(long) leaf_page,keypos));
+ DBUG_DUMP("anc_buff",(byte*) anc_buff,mi_getint(anc_buff));
+ DBUG_DUMP("leaf_buff",(byte*) leaf_buff,mi_getint(leaf_buff));
+
+ buff=info->buff;
+ info->buff_used=1;
+ next_keypos=keypos;
+ nod_flag=mi_test_if_nod(leaf_buff);
+ p_length=nod_flag+2;
+ anc_length=mi_getint(anc_buff);
+ leaf_length=mi_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=_mi_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= _mi_kpos(key_reflength,next_keypos);
+ if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
+ goto err;
+ buff_length=mi_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 (!_mi_get_last_key(info,keyinfo,anc_buff,anc_key,next_keypos,&length)
+ || !_mi_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);
+ mi_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;
+ mi_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 (_mi_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 &&
+ !_mi_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length))
+ goto err;
+ if (!(half_pos=_mi_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);
+ mi_putint(leaf_buff,length,nod_flag);
+
+ /* Correct new keypointer to leaf_page */
+ half_pos=after_key;
+ _mi_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);
+ mi_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+mi_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);
+ mi_putint(buff,length+t_length+p_length,nod_flag);
+
+ if (_mi_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff))
+ goto err;
+ }
+ if (_mi_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
+ goto err;
+ DBUG_RETURN(anc_length <= ((info->quick_mode ? MI_MIN_BLOCK_LENGTH :
+ (uint) keyinfo->underflow_block_length)));
+ }
+
+ DBUG_PRINT("test",("use left page"));
+
+ keypos=_mi_get_last_key(info,keyinfo,anc_buff,anc_key,keypos,&length);
+ if (!keypos)
+ goto err;
+ next_page= _mi_kpos(key_reflength,keypos);
+ if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
+ goto err;
+ buff_length=mi_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 (!_mi_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;
+ mi_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;
+ mi_putint(anc_buff,anc_length,key_reflength);
+
+ if (buff_length <= keyinfo->block_length)
+ { /* Keys in one page */
+ if (_mi_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 (!_mi_get_last_key(info,keyinfo,anc_buff,anc_pos=anc_key,keypos,
+ &length))
+ goto err;
+ endpos=_mi_find_half_pos(nod_flag,keyinfo,buff,leaf_key,
+ &key_length, &half_pos);
+ if (!endpos)
+ goto err;
+ _mi_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);
+ mi_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);
+ mi_putint(leaf_buff,length+t_length+p_length,nod_flag);
+ if (_mi_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
+ goto err;
+ mi_putint(buff,endpos-buff,nod_flag);
+ }
+ if (_mi_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(MI_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= _mi_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= _mi_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 */
diff --git a/storage/myisam/mi_delete_all.c b/storage/myisam/mi_delete_all.c
new file mode 100644
index 00000000000..3033249886f
--- /dev/null
+++ b/storage/myisam/mi_delete_all.c
@@ -0,0 +1,72 @@
+/* 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 */
+
+/* Remove all rows from a MyISAM table */
+/* This clears the status information and truncates files */
+
+#include "myisamdef.h"
+
+int mi_delete_all_rows(MI_INFO *info)
+{
+ uint i;
+ MYISAM_SHARE *share=info->s;
+ MI_STATE_INFO *state=&share->state;
+ DBUG_ENTER("mi_delete_all_rows");
+
+ if (share->options & HA_OPTION_READ_ONLY_DATA)
+ {
+ DBUG_RETURN(my_errno=EACCES);
+ }
+ if (_mi_readinfo(info,F_WRLCK,1))
+ DBUG_RETURN(my_errno);
+ if (_mi_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;
+ state->checksum=0;
+
+ for (i=share->base.max_key_block_length/MI_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;
+
+ myisam_log_command(MI_LOG_DELETE_ALL,info,(byte*) 0,0,0);
+ /*
+ 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(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
+ allow_break(); /* Allow SIGHUP & SIGINT */
+ DBUG_RETURN(0);
+
+err:
+ {
+ int save_errno=my_errno;
+ VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
+ info->update|=HA_STATE_WRITTEN; /* Buffer changed */
+ allow_break(); /* Allow SIGHUP & SIGINT */
+ DBUG_RETURN(my_errno=save_errno);
+ }
+} /* mi_delete */
diff --git a/storage/myisam/mi_delete_table.c b/storage/myisam/mi_delete_table.c
new file mode 100644
index 00000000000..6843881568d
--- /dev/null
+++ b/storage/myisam/mi_delete_table.c
@@ -0,0 +1,58 @@
+/* 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 */
+
+/*
+ deletes a table
+*/
+
+#include "fulltext.h"
+
+int mi_delete_table(const char *name)
+{
+ char from[FN_REFLEN];
+#ifdef USE_RAID
+ uint raid_type=0,raid_chunks=0;
+#endif
+ DBUG_ENTER("mi_delete_table");
+
+#ifdef EXTRA_DEBUG
+ check_table_is_closed(name,"delete");
+#endif
+#ifdef USE_RAID
+ {
+ MI_INFO *info;
+ /* we use 'open_for_repair' to be able to delete a crashed table */
+ if (!(info=mi_open(name, O_RDONLY, HA_OPEN_FOR_REPAIR)))
+ DBUG_RETURN(my_errno);
+ raid_type = info->s->base.raid_type;
+ raid_chunks = info->s->base.raid_chunks;
+ mi_close(info);
+ }
+#ifdef EXTRA_DEBUG
+ check_table_is_closed(name,"delete");
+#endif
+#endif /* USE_RAID */
+
+ fn_format(from,name,"",MI_NAME_IEXT,4);
+ if (my_delete_with_symlink(from, MYF(MY_WME)))
+ DBUG_RETURN(my_errno);
+ fn_format(from,name,"",MI_NAME_DEXT,4);
+#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);
+}
diff --git a/storage/myisam/mi_dynrec.c b/storage/myisam/mi_dynrec.c
new file mode 100644
index 00000000000..9d8e161b8fe
--- /dev/null
+++ b/storage/myisam/mi_dynrec.c
@@ -0,0 +1,1641 @@
+/* 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 */
+
+/*
+ Functions to handle space-packed-records and blobs
+
+ A row may be stored in one or more linked blocks.
+ The block size is between MI_MIN_BLOCK_LENGTH and MI_MAX_BLOCK_LENGTH.
+ Each block is aligned on MI_DYN_ALIGN_SIZE.
+ The reson for the max block size is to not have too many different types
+ of blocks. For the differnet block types, look at _mi_get_block_info()
+*/
+
+#include "myisamdef.h"
+
+/* Enough for comparing if number is zero */
+static char zero_string[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+static int write_dynamic_record(MI_INFO *info,const byte *record,
+ ulong reclength);
+static int _mi_find_writepos(MI_INFO *info,ulong reclength,my_off_t *filepos,
+ ulong *length);
+static int update_dynamic_record(MI_INFO *info,my_off_t filepos,byte *record,
+ ulong reclength);
+static int delete_dynamic_record(MI_INFO *info,my_off_t filepos,
+ uint second_read);
+static int _mi_cmp_buffer(File file, const byte *buff, my_off_t filepos,
+ uint length);
+
+#ifdef THREAD
+/* Play it safe; We have a small stack when using threads */
+#undef my_alloca
+#undef my_afree
+#define my_alloca(A) my_malloc((A),MYF(0))
+#define my_afree(A) my_free((A),MYF(0))
+#endif
+
+ /* Interface function from MI_INFO */
+
+int _mi_write_dynamic_record(MI_INFO *info, const byte *record)
+{
+ ulong reclength=_mi_rec_pack(info,info->rec_buff,record);
+ return (write_dynamic_record(info,info->rec_buff,reclength));
+}
+
+int _mi_update_dynamic_record(MI_INFO *info, my_off_t pos, const byte *record)
+{
+ uint length=_mi_rec_pack(info,info->rec_buff,record);
+ return (update_dynamic_record(info,pos,info->rec_buff,length));
+}
+
+int _mi_write_blob_record(MI_INFO *info, const byte *record)
+{
+ byte *rec_buff;
+ int error;
+ ulong reclength,reclength2,extra;
+
+ extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
+ MI_DYN_DELETE_BLOCK_HEADER+1);
+ reclength= (info->s->base.pack_reclength +
+ _my_calc_total_blob_length(info,record)+ extra);
+#ifdef NOT_USED /* We now support big rows */
+ if (reclength > MI_DYN_MAX_ROW_LENGTH)
+ {
+ my_errno=HA_ERR_TO_BIG_ROW;
+ return -1;
+ }
+#endif
+ if (!(rec_buff=(byte*) my_alloca(reclength)))
+ {
+ my_errno=ENOMEM;
+ return(-1);
+ }
+ reclength2= _mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
+ record);
+ DBUG_PRINT("info",("reclength: %lu reclength2: %lu",
+ reclength, reclength2));
+ DBUG_ASSERT(reclength2 <= reclength);
+ error=write_dynamic_record(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
+ reclength2);
+ my_afree(rec_buff);
+ return(error);
+}
+
+
+int _mi_update_blob_record(MI_INFO *info, my_off_t pos, const byte *record)
+{
+ byte *rec_buff;
+ int error;
+ ulong reclength,extra;
+
+ extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
+ MI_DYN_DELETE_BLOCK_HEADER);
+ reclength= (info->s->base.pack_reclength+
+ _my_calc_total_blob_length(info,record)+ extra);
+#ifdef NOT_USED /* We now support big rows */
+ if (reclength > MI_DYN_MAX_ROW_LENGTH)
+ {
+ my_errno=HA_ERR_TO_BIG_ROW;
+ return -1;
+ }
+#endif
+ if (!(rec_buff=(byte*) my_alloca(reclength)))
+ {
+ my_errno=ENOMEM;
+ return(-1);
+ }
+ reclength=_mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
+ record);
+ error=update_dynamic_record(info,pos,
+ rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
+ reclength);
+ my_afree(rec_buff);
+ return(error);
+}
+
+
+int _mi_delete_dynamic_record(MI_INFO *info)
+{
+ return delete_dynamic_record(info,info->lastpos,0);
+}
+
+
+ /* Write record to data-file */
+
+static int write_dynamic_record(MI_INFO *info, const byte *record,
+ ulong reclength)
+{
+ int flag;
+ ulong length;
+ my_off_t filepos;
+ DBUG_ENTER("write_dynamic_record");
+
+ flag=0;
+ do
+ {
+ if (_mi_find_writepos(info,reclength,&filepos,&length))
+ goto err;
+ if (_mi_write_part_record(info,filepos,length,info->s->state.dellink,
+ (byte**) &record,&reclength,&flag))
+ goto err;
+ } while (reclength);
+
+ DBUG_RETURN(0);
+err:
+ DBUG_RETURN(1);
+}
+
+
+ /* Get a block for data ; The given data-area must be used !! */
+
+static int _mi_find_writepos(MI_INFO *info,
+ ulong reclength, /* record length */
+ my_off_t *filepos, /* Return file pos */
+ ulong *length) /* length of block at filepos */
+{
+ MI_BLOCK_INFO block_info;
+ ulong tmp;
+ DBUG_ENTER("_mi_find_writepos");
+
+ if (info->s->state.dellink != HA_OFFSET_ERROR)
+ {
+ /* Deleted blocks exists; Get last used block */
+ *filepos=info->s->state.dellink;
+ block_info.second_read=0;
+ info->rec_cache.seek_not_done=1;
+ if (!(_mi_get_block_info(&block_info,info->dfile,info->s->state.dellink) &
+ BLOCK_DELETED))
+ {
+ DBUG_PRINT("error",("Delete link crashed"));
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ DBUG_RETURN(-1);
+ }
+ info->s->state.dellink=block_info.next_filepos;
+ info->state->del--;
+ info->state->empty-= block_info.block_len;
+ *length= block_info.block_len;
+ }
+ else
+ {
+ /* No deleted blocks; Allocate a new block */
+ *filepos=info->state->data_file_length;
+ if ((tmp=reclength+3 + test(reclength >= (65520-3))) <
+ info->s->base.min_block_length)
+ tmp= info->s->base.min_block_length;
+ else
+ tmp= ((tmp+MI_DYN_ALIGN_SIZE-1) &
+ (~ (ulong) (MI_DYN_ALIGN_SIZE-1)));
+ if (info->state->data_file_length >
+ (info->s->base.max_data_file_length - tmp))
+ {
+ my_errno=HA_ERR_RECORD_FILE_FULL;
+ DBUG_RETURN(-1);
+ }
+ if (tmp > MI_MAX_BLOCK_LENGTH)
+ tmp=MI_MAX_BLOCK_LENGTH;
+ *length= tmp;
+ info->state->data_file_length+= tmp;
+ info->s->state.split++;
+ info->update|=HA_STATE_WRITE_AT_END;
+ }
+ DBUG_RETURN(0);
+} /* _mi_find_writepos */
+
+
+
+/*
+ Unlink a deleted block from the deleted list.
+ This block will be combined with the preceding or next block to form
+ a big block.
+*/
+
+static bool unlink_deleted_block(MI_INFO *info, MI_BLOCK_INFO *block_info)
+{
+ DBUG_ENTER("unlink_deleted_block");
+ if (block_info->filepos == info->s->state.dellink)
+ {
+ /* First deleted block; We can just use this ! */
+ info->s->state.dellink=block_info->next_filepos;
+ }
+ else
+ {
+ MI_BLOCK_INFO tmp;
+ tmp.second_read=0;
+ /* Unlink block from the previous block */
+ if (!(_mi_get_block_info(&tmp,info->dfile,block_info->prev_filepos)
+ & BLOCK_DELETED))
+ DBUG_RETURN(1); /* Something is wrong */
+ mi_sizestore(tmp.header+4,block_info->next_filepos);
+ if (my_pwrite(info->dfile,(char*) tmp.header+4,8,
+ block_info->prev_filepos+4, MYF(MY_NABP)))
+ DBUG_RETURN(1);
+ /* Unlink block from next block */
+ if (block_info->next_filepos != HA_OFFSET_ERROR)
+ {
+ if (!(_mi_get_block_info(&tmp,info->dfile,block_info->next_filepos)
+ & BLOCK_DELETED))
+ DBUG_RETURN(1); /* Something is wrong */
+ mi_sizestore(tmp.header+12,block_info->prev_filepos);
+ if (my_pwrite(info->dfile,(char*) tmp.header+12,8,
+ block_info->next_filepos+12,
+ MYF(MY_NABP)))
+ DBUG_RETURN(1);
+ }
+ }
+ /* We now have one less deleted block */
+ info->state->del--;
+ info->state->empty-= block_info->block_len;
+ info->s->state.split--;
+
+ /*
+ If this was a block that we where accessing through table scan
+ (mi_rrnd() or mi_scan(), then ensure that we skip over this block
+ when doing next mi_rrnd() or mi_scan().
+ */
+ if (info->nextpos == block_info->filepos)
+ info->nextpos+=block_info->block_len;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Add a backward link to delete block
+
+ SYNOPSIS
+ update_backward_delete_link()
+ info MyISAM handler
+ delete_block Position to delete block to update.
+ If this is 'HA_OFFSET_ERROR', nothing will be done
+ filepos Position to block that 'delete_block' should point to
+
+ RETURN
+ 0 ok
+ 1 error. In this case my_error is set.
+*/
+
+static int update_backward_delete_link(MI_INFO *info, my_off_t delete_block,
+ my_off_t filepos)
+{
+ MI_BLOCK_INFO block_info;
+ DBUG_ENTER("update_backward_delete_link");
+
+ if (delete_block != HA_OFFSET_ERROR)
+ {
+ block_info.second_read=0;
+ if (_mi_get_block_info(&block_info,info->dfile,delete_block)
+ & BLOCK_DELETED)
+ {
+ char buff[8];
+ mi_sizestore(buff,filepos);
+ if (my_pwrite(info->dfile,buff, 8, delete_block+12, MYF(MY_NABP)))
+ DBUG_RETURN(1); /* Error on write */
+ }
+ else
+ {
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ DBUG_RETURN(1); /* Wrong delete link */
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+ /* Delete datarecord from database */
+ /* info->rec_cache.seek_not_done is updated in cmp_record */
+
+static int delete_dynamic_record(MI_INFO *info, my_off_t filepos,
+ uint second_read)
+{
+ uint length,b_type;
+ MI_BLOCK_INFO block_info,del_block;
+ int error;
+ my_bool remove_next_block;
+ DBUG_ENTER("delete_dynamic_record");
+
+ /* First add a link from the last block to the new one */
+ error= update_backward_delete_link(info, info->s->state.dellink, filepos);
+
+ block_info.second_read=second_read;
+ do
+ {
+ /* Remove block at 'filepos' */
+ if ((b_type=_mi_get_block_info(&block_info,info->dfile,filepos))
+ & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
+ BLOCK_FATAL_ERROR) ||
+ (length=(uint) (block_info.filepos-filepos) +block_info.block_len) <
+ MI_MIN_BLOCK_LENGTH)
+ {
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ DBUG_RETURN(1);
+ }
+ /* Check if next block is a delete block */
+ del_block.second_read=0;
+ remove_next_block=0;
+ if (_mi_get_block_info(&del_block,info->dfile,filepos+length) &
+ BLOCK_DELETED && del_block.block_len+length < MI_DYN_MAX_BLOCK_LENGTH)
+ {
+ /* We can't remove this yet as this block may be the head block */
+ remove_next_block=1;
+ length+=del_block.block_len;
+ }
+
+ block_info.header[0]=0;
+ mi_int3store(block_info.header+1,length);
+ mi_sizestore(block_info.header+4,info->s->state.dellink);
+ if (b_type & BLOCK_LAST)
+ bfill(block_info.header+12,8,255);
+ else
+ mi_sizestore(block_info.header+12,block_info.next_filepos);
+ if (my_pwrite(info->dfile,(byte*) block_info.header,20,filepos,
+ MYF(MY_NABP)))
+ DBUG_RETURN(1);
+ info->s->state.dellink = filepos;
+ info->state->del++;
+ info->state->empty+=length;
+ filepos=block_info.next_filepos;
+
+ /* Now it's safe to unlink the deleted block directly after this one */
+ if (remove_next_block && unlink_deleted_block(info,&del_block))
+ error=1;
+ } while (!(b_type & BLOCK_LAST));
+
+ DBUG_RETURN(error);
+}
+
+
+ /* Write a block to datafile */
+
+int _mi_write_part_record(MI_INFO *info,
+ my_off_t filepos, /* points at empty block */
+ ulong length, /* length of block */
+ my_off_t next_filepos,/* Next empty block */
+ byte **record, /* pointer to record ptr */
+ ulong *reclength, /* length of *record */
+ int *flag) /* *flag == 0 if header */
+{
+ ulong head_length,res_length,extra_length,long_block,del_length;
+ byte *pos,*record_end;
+ my_off_t next_delete_block;
+ uchar temp[MI_SPLIT_LENGTH+MI_DYN_DELETE_BLOCK_HEADER];
+ DBUG_ENTER("_mi_write_part_record");
+
+ next_delete_block=HA_OFFSET_ERROR;
+
+ res_length=extra_length=0;
+ if (length > *reclength + MI_SPLIT_LENGTH)
+ { /* Splitt big block */
+ res_length=MY_ALIGN(length- *reclength - MI_EXTEND_BLOCK_LENGTH,
+ MI_DYN_ALIGN_SIZE);
+ length-= res_length; /* Use this for first part */
+ }
+ long_block= (length < 65520L && *reclength < 65520L) ? 0 : 1;
+ if (length == *reclength+ 3 + long_block)
+ {
+ /* Block is exactly of the right length */
+ temp[0]=(uchar) (1+ *flag)+(uchar) long_block; /* Flag is 0 or 6 */
+ if (long_block)
+ {
+ mi_int3store(temp+1,*reclength);
+ head_length=4;
+ }
+ else
+ {
+ mi_int2store(temp+1,*reclength);
+ head_length=3;
+ }
+ }
+ else if (length-long_block < *reclength+4)
+ { /* To short block */
+ if (next_filepos == HA_OFFSET_ERROR)
+ next_filepos=info->s->state.dellink != HA_OFFSET_ERROR ?
+ info->s->state.dellink : info->state->data_file_length;
+ if (*flag == 0) /* First block */
+ {
+ if (*reclength > MI_MAX_BLOCK_LENGTH)
+ {
+ head_length= 16;
+ temp[0]=13;
+ mi_int4store(temp+1,*reclength);
+ mi_int3store(temp+5,length-head_length);
+ mi_sizestore((byte*) temp+8,next_filepos);
+ }
+ else
+ {
+ head_length=5+8+long_block*2;
+ temp[0]=5+(uchar) long_block;
+ if (long_block)
+ {
+ mi_int3store(temp+1,*reclength);
+ mi_int3store(temp+4,length-head_length);
+ mi_sizestore((byte*) temp+7,next_filepos);
+ }
+ else
+ {
+ mi_int2store(temp+1,*reclength);
+ mi_int2store(temp+3,length-head_length);
+ mi_sizestore((byte*) temp+5,next_filepos);
+ }
+ }
+ }
+ else
+ {
+ head_length=3+8+long_block;
+ temp[0]=11+(uchar) long_block;
+ if (long_block)
+ {
+ mi_int3store(temp+1,length-head_length);
+ mi_sizestore((byte*) temp+4,next_filepos);
+ }
+ else
+ {
+ mi_int2store(temp+1,length-head_length);
+ mi_sizestore((byte*) temp+3,next_filepos);
+ }
+ }
+ }
+ else
+ { /* Block with empty info last */
+ head_length=4+long_block;
+ extra_length= length- *reclength-head_length;
+ temp[0]= (uchar) (3+ *flag)+(uchar) long_block; /* 3,4 or 9,10 */
+ if (long_block)
+ {
+ mi_int3store(temp+1,*reclength);
+ temp[4]= (uchar) (extra_length);
+ }
+ else
+ {
+ mi_int2store(temp+1,*reclength);
+ temp[3]= (uchar) (extra_length);
+ }
+ length= *reclength+head_length; /* Write only what is needed */
+ }
+ DBUG_DUMP("header",(byte*) temp,head_length);
+
+ /* Make a long block for one write */
+ record_end= *record+length-head_length;
+ del_length=(res_length ? MI_DYN_DELETE_BLOCK_HEADER : 0);
+ bmove((byte*) (*record-head_length),(byte*) temp,head_length);
+ memcpy(temp,record_end,(size_t) (extra_length+del_length));
+ bzero((byte*) record_end,extra_length);
+
+ if (res_length)
+ {
+ /* Check first if we can join this block with the next one */
+ MI_BLOCK_INFO del_block;
+ my_off_t next_block=filepos+length+extra_length+res_length;
+
+ del_block.second_read=0;
+ if (next_block < info->state->data_file_length &&
+ info->s->state.dellink != HA_OFFSET_ERROR)
+ {
+ if ((_mi_get_block_info(&del_block,info->dfile,next_block)
+ & BLOCK_DELETED) &&
+ res_length + del_block.block_len < MI_DYN_MAX_BLOCK_LENGTH)
+ {
+ if (unlink_deleted_block(info,&del_block))
+ goto err;
+ res_length+=del_block.block_len;
+ }
+ }
+
+ /* Create a delete link of the last part of the block */
+ pos=record_end+extra_length;
+ pos[0]= '\0';
+ mi_int3store(pos+1,res_length);
+ mi_sizestore(pos+4,info->s->state.dellink);
+ bfill(pos+12,8,255); /* End link */
+ next_delete_block=info->s->state.dellink;
+ info->s->state.dellink= filepos+length+extra_length;
+ info->state->del++;
+ info->state->empty+=res_length;
+ info->s->state.split++;
+ }
+ if (info->opt_flag & WRITE_CACHE_USED &&
+ info->update & HA_STATE_WRITE_AT_END)
+ {
+ if (info->update & HA_STATE_EXTEND_BLOCK)
+ {
+ info->update&= ~HA_STATE_EXTEND_BLOCK;
+ if (my_block_write(&info->rec_cache,(byte*) *record-head_length,
+ length+extra_length+del_length,filepos))
+ goto err;
+ }
+ else if (my_b_write(&info->rec_cache,(byte*) *record-head_length,
+ length+extra_length+del_length))
+ goto err;
+ }
+ else
+ {
+ info->rec_cache.seek_not_done=1;
+ if (my_pwrite(info->dfile,(byte*) *record-head_length,length+extra_length+
+ del_length,filepos,info->s->write_flag))
+ goto err;
+ }
+ memcpy(record_end,temp,(size_t) (extra_length+del_length));
+ *record=record_end;
+ *reclength-=(length-head_length);
+ *flag=6;
+
+ if (del_length)
+ {
+ /* link the next delete block to this */
+ if (update_backward_delete_link(info, next_delete_block,
+ info->s->state.dellink))
+ goto err;
+ }
+
+ DBUG_RETURN(0);
+err:
+ DBUG_PRINT("exit",("errno: %d",my_errno));
+ DBUG_RETURN(1);
+} /*_mi_write_part_record */
+
+
+ /* update record from datafile */
+
+static int update_dynamic_record(MI_INFO *info, my_off_t filepos, byte *record,
+ ulong reclength)
+{
+ int flag;
+ uint error;
+ ulong length;
+ MI_BLOCK_INFO block_info;
+ DBUG_ENTER("update_dynamic_record");
+
+ flag=block_info.second_read=0;
+ while (reclength > 0)
+ {
+ if (filepos != info->s->state.dellink)
+ {
+ block_info.next_filepos= HA_OFFSET_ERROR;
+ if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
+ & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
+ BLOCK_FATAL_ERROR))
+ {
+ DBUG_PRINT("error",("Got wrong block info"));
+ if (!(error & BLOCK_FATAL_ERROR))
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ goto err;
+ }
+ length=(ulong) (block_info.filepos-filepos) + block_info.block_len;
+ if (length < reclength)
+ {
+ uint tmp=MY_ALIGN(reclength - length + 3 +
+ test(reclength >= 65520L),MI_DYN_ALIGN_SIZE);
+ /* Don't create a block bigger than MI_MAX_BLOCK_LENGTH */
+ tmp= min(length+tmp, MI_MAX_BLOCK_LENGTH)-length;
+ /* Check if we can extend this block */
+ if (block_info.filepos + block_info.block_len ==
+ info->state->data_file_length &&
+ info->state->data_file_length <
+ info->s->base.max_data_file_length-tmp)
+ {
+ /* extend file */
+ DBUG_PRINT("info",("Extending file with %d bytes",tmp));
+ if (info->nextpos == info->state->data_file_length)
+ info->nextpos+= tmp;
+ info->state->data_file_length+= tmp;
+ info->update|= HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK;
+ length+=tmp;
+ }
+ else if (length < MI_MAX_BLOCK_LENGTH - MI_MIN_BLOCK_LENGTH)
+ {
+ /*
+ Check if next block is a deleted block
+ Above we have MI_MIN_BLOCK_LENGTH to avoid the problem where
+ the next block is so small it can't be splited which could
+ casue problems
+ */
+
+ MI_BLOCK_INFO del_block;
+ del_block.second_read=0;
+ if (_mi_get_block_info(&del_block,info->dfile,
+ block_info.filepos + block_info.block_len) &
+ BLOCK_DELETED)
+ {
+ /* Use; Unlink it and extend the current block */
+ DBUG_PRINT("info",("Extending current block"));
+ if (unlink_deleted_block(info,&del_block))
+ goto err;
+ if ((length+=del_block.block_len) > MI_MAX_BLOCK_LENGTH)
+ {
+ /*
+ New block was too big, link overflow part back to
+ delete list
+ */
+ my_off_t next_pos;
+ ulong rest_length= length-MI_MAX_BLOCK_LENGTH;
+ set_if_bigger(rest_length, MI_MIN_BLOCK_LENGTH);
+ next_pos= del_block.filepos+ del_block.block_len - rest_length;
+
+ if (update_backward_delete_link(info, info->s->state.dellink,
+ next_pos))
+ DBUG_RETURN(1);
+
+ /* create delete link for data that didn't fit into the page */
+ del_block.header[0]=0;
+ mi_int3store(del_block.header+1, rest_length);
+ mi_sizestore(del_block.header+4,info->s->state.dellink);
+ bfill(del_block.header+12,8,255);
+ if (my_pwrite(info->dfile,(byte*) del_block.header,20, next_pos,
+ MYF(MY_NABP)))
+ DBUG_RETURN(1);
+ info->s->state.dellink= next_pos;
+ info->s->state.split++;
+ info->state->del++;
+ info->state->empty+= rest_length;
+ length-= rest_length;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (_mi_find_writepos(info,reclength,&filepos,&length))
+ goto err;
+ }
+ if (_mi_write_part_record(info,filepos,length,block_info.next_filepos,
+ &record,&reclength,&flag))
+ goto err;
+ if ((filepos=block_info.next_filepos) == HA_OFFSET_ERROR)
+ {
+ /* Start writing data on deleted blocks */
+ filepos=info->s->state.dellink;
+ }
+ }
+
+ if (block_info.next_filepos != HA_OFFSET_ERROR)
+ if (delete_dynamic_record(info,block_info.next_filepos,1))
+ goto err;
+ DBUG_RETURN(0);
+err:
+ DBUG_RETURN(1);
+}
+
+
+ /* Pack a record. Return new reclength */
+
+uint _mi_rec_pack(MI_INFO *info, register byte *to, register const byte *from)
+{
+ uint length,new_length,flag,bit,i;
+ char *pos,*end,*startpos,*packpos;
+ enum en_fieldtype type;
+ reg3 MI_COLUMNDEF *rec;
+ MI_BLOB *blob;
+ DBUG_ENTER("_mi_rec_pack");
+
+ flag=0 ; bit=1;
+ startpos=packpos=to; to+= info->s->base.pack_bits; blob=info->blobs;
+ rec=info->s->rec;
+
+ for (i=info->s->base.fields ; i-- > 0; from+= length,rec++)
+ {
+ length=(uint) rec->length;
+ if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
+ {
+ if (type == FIELD_BLOB)
+ {
+ if (!blob->length)
+ flag|=bit;
+ else
+ {
+ char *temp_pos;
+ size_t tmp_length=length-mi_portable_sizeof_char_ptr;
+ memcpy((byte*) to,from,tmp_length);
+ memcpy_fixed(&temp_pos,from+tmp_length,sizeof(char*));
+ memcpy(to+tmp_length,temp_pos,(size_t) blob->length);
+ to+=tmp_length+blob->length;
+ }
+ blob++;
+ }
+ else if (type == FIELD_SKIP_ZERO)
+ {
+ if (memcmp((byte*) from,zero_string,length) == 0)
+ flag|=bit;
+ else
+ {
+ memcpy((byte*) to,from,(size_t) length); to+=length;
+ }
+ }
+ else if (type == FIELD_SKIP_ENDSPACE ||
+ type == FIELD_SKIP_PRESPACE)
+ {
+ pos= (byte*) from; end= (byte*) from + length;
+ if (type == FIELD_SKIP_ENDSPACE)
+ { /* Pack trailing spaces */
+ while (end > from && *(end-1) == ' ')
+ end--;
+ }
+ else
+ { /* Pack pref-spaces */
+ while (pos < end && *pos == ' ')
+ pos++;
+ }
+ new_length=(uint) (end-pos);
+ if (new_length +1 + test(rec->length > 255 && new_length > 127)
+ < length)
+ {
+ if (rec->length > 255 && new_length > 127)
+ {
+ to[0]=(char) ((new_length & 127)+128);
+ to[1]=(char) (new_length >> 7);
+ to+=2;
+ }
+ else
+ *to++= (char) new_length;
+ memcpy((byte*) to,pos,(size_t) new_length); to+=new_length;
+ flag|=bit;
+ }
+ else
+ {
+ memcpy(to,from,(size_t) length); to+=length;
+ }
+ }
+ else if (type == FIELD_VARCHAR)
+ {
+ uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
+ uint tmp_length;
+ if (pack_length == 1)
+ {
+ tmp_length= (uint) *(uchar*) from;
+ *to++= *from;
+ }
+ else
+ {
+ tmp_length= uint2korr(from);
+ store_key_length_inc(to,tmp_length);
+ }
+ memcpy(to, from+pack_length,tmp_length);
+ to+= tmp_length;
+ continue;
+ }
+ else
+ {
+ memcpy(to,from,(size_t) length); to+=length;
+ continue; /* Normal field */
+ }
+ if ((bit= bit << 1) >= 256)
+ {
+ *packpos++ = (char) (uchar) flag;
+ bit=1; flag=0;
+ }
+ }
+ else
+ {
+ memcpy(to,from,(size_t) length); to+=length;
+ }
+ }
+ if (bit != 1)
+ *packpos= (char) (uchar) flag;
+ if (info->s->calc_checksum)
+ *to++=(char) info->checksum;
+ DBUG_PRINT("exit",("packed length: %d",(int) (to-startpos)));
+ DBUG_RETURN((uint) (to-startpos));
+} /* _mi_rec_pack */
+
+
+
+/*
+ Check if a record was correctly packed. Used only by myisamchk
+ Returns 0 if record is ok.
+*/
+
+my_bool _mi_rec_check(MI_INFO *info,const char *record, byte *rec_buff,
+ ulong packed_length)
+{
+ uint length,new_length,flag,bit,i;
+ char *pos,*end,*packpos,*to;
+ enum en_fieldtype type;
+ reg3 MI_COLUMNDEF *rec;
+ DBUG_ENTER("_mi_rec_check");
+
+ packpos=rec_buff; to= rec_buff+info->s->base.pack_bits;
+ rec=info->s->rec;
+ flag= *packpos; bit=1;
+
+ for (i=info->s->base.fields ; i-- > 0; record+= length, rec++)
+ {
+ length=(uint) rec->length;
+ if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
+ {
+ if (type == FIELD_BLOB)
+ {
+ uint blob_length=
+ _mi_calc_blob_length(length-mi_portable_sizeof_char_ptr,record);
+ if (!blob_length && !(flag & bit))
+ goto err;
+ if (blob_length)
+ to+=length - mi_portable_sizeof_char_ptr+ blob_length;
+ }
+ else if (type == FIELD_SKIP_ZERO)
+ {
+ if (memcmp((byte*) record,zero_string,length) == 0)
+ {
+ if (!(flag & bit))
+ goto err;
+ }
+ else
+ to+=length;
+ }
+ else if (type == FIELD_SKIP_ENDSPACE ||
+ type == FIELD_SKIP_PRESPACE)
+ {
+ pos= (byte*) record; end= (byte*) record + length;
+ if (type == FIELD_SKIP_ENDSPACE)
+ { /* Pack trailing spaces */
+ while (end > record && *(end-1) == ' ')
+ end--;
+ }
+ else
+ { /* Pack pre-spaces */
+ while (pos < end && *pos == ' ')
+ pos++;
+ }
+ new_length=(uint) (end-pos);
+ if (new_length +1 + test(rec->length > 255 && new_length > 127)
+ < length)
+ {
+ if (!(flag & bit))
+ goto err;
+ if (rec->length > 255 && new_length > 127)
+ {
+ if (to[0] != (char) ((new_length & 127)+128) ||
+ to[1] != (char) (new_length >> 7))
+ goto err;
+ to+=2;
+ }
+ else if (*to++ != (char) new_length)
+ goto err;
+ to+=new_length;
+ }
+ else
+ to+=length;
+ }
+ else if (type == FIELD_VARCHAR)
+ {
+ uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
+ uint tmp_length;
+ if (pack_length == 1)
+ {
+ tmp_length= (uint) *(uchar*) record;
+ to+= 1+ tmp_length;
+ continue;
+ }
+ else
+ {
+ tmp_length= uint2korr(record);
+ to+= get_pack_length(tmp_length)+tmp_length;
+ }
+ continue;
+ }
+ else
+ {
+ to+=length;
+ continue; /* Normal field */
+ }
+ if ((bit= bit << 1) >= 256)
+ {
+ flag= *++packpos;
+ bit=1;
+ }
+ }
+ else
+ to+= length;
+ }
+ if (packed_length != (uint) (to - rec_buff) + test(info->s->calc_checksum) ||
+ (bit != 1 && (flag & ~(bit - 1))))
+ goto err;
+ if (info->s->calc_checksum)
+ {
+ if ((uchar) info->checksum != (uchar) *to)
+ {
+ DBUG_PRINT("error",("wrong checksum for row"));
+ goto err;
+ }
+ }
+ DBUG_RETURN(0);
+
+err:
+ DBUG_RETURN(1);
+}
+
+
+
+ /* Unpacks a record */
+ /* Returns -1 and my_errno =HA_ERR_RECORD_DELETED if reclength isn't */
+ /* right. Returns reclength (>0) if ok */
+
+ulong _mi_rec_unpack(register MI_INFO *info, register byte *to, byte *from,
+ ulong found_length)
+{
+ uint flag,bit,length,rec_length,min_pack_length;
+ enum en_fieldtype type;
+ byte *from_end,*to_end,*packpos;
+ reg3 MI_COLUMNDEF *rec,*end_field;
+ DBUG_ENTER("_mi_rec_unpack");
+
+ to_end=to + info->s->base.reclength;
+ from_end=from+found_length;
+ flag= (uchar) *from; bit=1; packpos=from;
+ if (found_length < info->s->base.min_pack_length)
+ goto err;
+ from+= info->s->base.pack_bits;
+ min_pack_length=info->s->base.min_pack_length - info->s->base.pack_bits;
+
+ for (rec=info->s->rec , end_field=rec+info->s->base.fields ;
+ rec < end_field ; to+= rec_length, rec++)
+ {
+ rec_length=rec->length;
+ if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL &&
+ (type != FIELD_CHECK))
+ {
+ if (type == FIELD_VARCHAR)
+ {
+ uint pack_length= HA_VARCHAR_PACKLENGTH(rec_length-1);
+ if (pack_length == 1)
+ {
+ length= (uint) *(uchar*) from;
+ if (length > rec_length-1)
+ goto err;
+ *to= *from++;
+ }
+ else
+ {
+ get_key_length(length, from);
+ if (length > rec_length-2)
+ goto err;
+ int2store(to,length);
+ }
+ if (from+length > from_end)
+ goto err;
+ memcpy(to+pack_length, from, length);
+ from+= length;
+ min_pack_length--;
+ continue;
+ }
+ if (flag & bit)
+ {
+ if (type == FIELD_BLOB || type == FIELD_SKIP_ZERO)
+ bzero((byte*) to,rec_length);
+ else if (type == FIELD_SKIP_ENDSPACE ||
+ type == FIELD_SKIP_PRESPACE)
+ {
+ if (rec->length > 255 && *from & 128)
+ {
+ if (from + 1 >= from_end)
+ goto err;
+ length= (*from & 127)+ ((uint) (uchar) *(from+1) << 7); from+=2;
+ }
+ else
+ {
+ if (from == from_end)
+ goto err;
+ length= (uchar) *from++;
+ }
+ min_pack_length--;
+ if (length >= rec_length ||
+ min_pack_length + length > (uint) (from_end - from))
+ goto err;
+ if (type == FIELD_SKIP_ENDSPACE)
+ {
+ memcpy(to,(byte*) from,(size_t) length);
+ bfill((byte*) to+length,rec_length-length,' ');
+ }
+ else
+ {
+ bfill((byte*) to,rec_length-length,' ');
+ memcpy(to+rec_length-length,(byte*) from,(size_t) length);
+ }
+ from+=length;
+ }
+ }
+ else if (type == FIELD_BLOB)
+ {
+ uint size_length=rec_length- mi_portable_sizeof_char_ptr;
+ ulong blob_length=_mi_calc_blob_length(size_length,from);
+ if ((ulong) (from_end-from) - size_length < blob_length ||
+ min_pack_length > (uint) (from_end -(from+size_length+blob_length)))
+ goto err;
+ memcpy((byte*) to,(byte*) from,(size_t) size_length);
+ from+=size_length;
+ memcpy_fixed((byte*) to+size_length,(byte*) &from,sizeof(char*));
+ from+=blob_length;
+ }
+ else
+ {
+ if (type == FIELD_SKIP_ENDSPACE || type == FIELD_SKIP_PRESPACE)
+ min_pack_length--;
+ if (min_pack_length + rec_length > (uint) (from_end - from))
+ goto err;
+ memcpy(to,(byte*) from,(size_t) rec_length); from+=rec_length;
+ }
+ if ((bit= bit << 1) >= 256)
+ {
+ flag= (uchar) *++packpos; bit=1;
+ }
+ }
+ else
+ {
+ if (min_pack_length > (uint) (from_end - from))
+ goto err;
+ min_pack_length-=rec_length;
+ memcpy(to, (byte*) from, (size_t) rec_length);
+ from+=rec_length;
+ }
+ }
+ if (info->s->calc_checksum)
+ from++;
+ if (to == to_end && from == from_end && (bit == 1 || !(flag & ~(bit-1))))
+ DBUG_RETURN(found_length);
+
+err:
+ my_errno= HA_ERR_WRONG_IN_RECORD;
+ DBUG_PRINT("error",("to_end: %lx -> %lx from_end: %lx -> %lx",
+ to,to_end,from,from_end));
+ DBUG_DUMP("from",(byte*) info->rec_buff,info->s->base.min_pack_length);
+ DBUG_RETURN(MY_FILE_ERROR);
+} /* _mi_rec_unpack */
+
+
+ /* Calc length of blob. Update info in blobs->length */
+
+ulong _my_calc_total_blob_length(MI_INFO *info, const byte *record)
+{
+ ulong length;
+ MI_BLOB *blob,*end;
+
+ for (length=0, blob= info->blobs, end=blob+info->s->base.blobs ;
+ blob != end;
+ blob++)
+ {
+ blob->length=_mi_calc_blob_length(blob->pack_length,record + blob->offset);
+ length+=blob->length;
+ }
+ return length;
+}
+
+
+ulong _mi_calc_blob_length(uint length, const byte *pos)
+{
+ switch (length) {
+ case 1:
+ return (uint) (uchar) *pos;
+ case 2:
+ return (uint) uint2korr(pos);
+ case 3:
+ return uint3korr(pos);
+ case 4:
+ return uint4korr(pos);
+ default:
+ break;
+ }
+ return 0; /* Impossible */
+}
+
+
+void _my_store_blob_length(byte *pos,uint pack_length,uint length)
+{
+ switch (pack_length) {
+ case 1:
+ *pos= (uchar) length;
+ break;
+ case 2:
+ int2store(pos,length);
+ break;
+ case 3:
+ int3store(pos,length);
+ break;
+ case 4:
+ int4store(pos,length);
+ default:
+ break;
+ }
+ return;
+}
+
+
+ /* Read record from datafile */
+ /* Returns 0 if ok, -1 if error */
+
+int _mi_read_dynamic_record(MI_INFO *info, my_off_t filepos, byte *buf)
+{
+ int flag;
+ uint b_type,left_length;
+ byte *to;
+ MI_BLOCK_INFO block_info;
+ File file;
+ DBUG_ENTER("mi_read_dynamic_record");
+
+ if (filepos != HA_OFFSET_ERROR)
+ {
+ LINT_INIT(to);
+ LINT_INIT(left_length);
+ file=info->dfile;
+ block_info.next_filepos=filepos; /* for easyer loop */
+ flag=block_info.second_read=0;
+ do
+ {
+ if (info->opt_flag & WRITE_CACHE_USED &&
+ info->rec_cache.pos_in_file <= block_info.next_filepos &&
+ flush_io_cache(&info->rec_cache))
+ goto err;
+ info->rec_cache.seek_not_done=1;
+ if ((b_type=_mi_get_block_info(&block_info,file,
+ block_info.next_filepos))
+ & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
+ BLOCK_FATAL_ERROR))
+ {
+ if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
+ my_errno=HA_ERR_RECORD_DELETED;
+ goto err;
+ }
+ if (flag == 0) /* First block */
+ {
+ flag=1;
+ if (block_info.rec_len > (uint) info->s->base.max_pack_length)
+ goto panic;
+ if (info->s->base.blobs)
+ {
+ if (!(to=mi_alloc_rec_buff(info, block_info.rec_len,
+ &info->rec_buff)))
+ goto err;
+ }
+ else
+ to= info->rec_buff;
+ left_length=block_info.rec_len;
+ }
+ if (left_length < block_info.data_len || ! block_info.data_len)
+ goto panic; /* Wrong linked record */
+ if (my_pread(file,(byte*) to,block_info.data_len,block_info.filepos,
+ MYF(MY_NABP)))
+ goto panic;
+ left_length-=block_info.data_len;
+ to+=block_info.data_len;
+ } while (left_length);
+
+ info->update|= HA_STATE_AKTIV; /* We have a aktive record */
+ fast_mi_writeinfo(info);
+ DBUG_RETURN(_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
+ MY_FILE_ERROR ? 0 : -1);
+ }
+ fast_mi_writeinfo(info);
+ DBUG_RETURN(-1); /* Wrong data to read */
+
+panic:
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+err:
+ VOID(_mi_writeinfo(info,0));
+ DBUG_RETURN(-1);
+}
+
+ /* compare unique constraint between stored rows */
+
+int _mi_cmp_dynamic_unique(MI_INFO *info, MI_UNIQUEDEF *def,
+ const byte *record, my_off_t pos)
+{
+ byte *rec_buff,*old_record;
+ int error;
+ DBUG_ENTER("_mi_cmp_dynamic_unique");
+
+ if (!(old_record=my_alloca(info->s->base.reclength)))
+ DBUG_RETURN(1);
+
+ /* Don't let the compare destroy blobs that may be in use */
+ rec_buff=info->rec_buff;
+ if (info->s->base.blobs)
+ info->rec_buff=0;
+ error=_mi_read_dynamic_record(info,pos,old_record);
+ if (!error)
+ error=mi_unique_comp(def, record, old_record, def->null_are_equal);
+ if (info->s->base.blobs)
+ {
+ my_free(mi_get_rec_buff_ptr(info, info->rec_buff), MYF(MY_ALLOW_ZERO_PTR));
+ info->rec_buff=rec_buff;
+ }
+ my_afree(old_record);
+ DBUG_RETURN(error);
+}
+
+
+ /* Compare of record one disk with packed record in memory */
+
+int _mi_cmp_dynamic_record(register MI_INFO *info, register const byte *record)
+{
+ uint flag,reclength,b_type;
+ my_off_t filepos;
+ byte *buffer;
+ MI_BLOCK_INFO block_info;
+ DBUG_ENTER("_mi_cmp_dynamic_record");
+
+ /* We are going to do changes; dont let anybody disturb */
+ dont_break(); /* Dont allow SIGHUP or SIGINT */
+
+ if (info->opt_flag & WRITE_CACHE_USED)
+ {
+ info->update&= ~(HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK);
+ if (flush_io_cache(&info->rec_cache))
+ DBUG_RETURN(-1);
+ }
+ info->rec_cache.seek_not_done=1;
+
+ /* If nobody have touched the database we don't have to test rec */
+
+ buffer=info->rec_buff;
+ if ((info->opt_flag & READ_CHECK_USED))
+ { /* If check isn't disabled */
+ if (info->s->base.blobs)
+ {
+ if (!(buffer=(byte*) my_alloca(info->s->base.pack_reclength+
+ _my_calc_total_blob_length(info,record))))
+ DBUG_RETURN(-1);
+ }
+ reclength=_mi_rec_pack(info,buffer,record);
+ record= buffer;
+
+ filepos=info->lastpos;
+ flag=block_info.second_read=0;
+ block_info.next_filepos=filepos;
+ while (reclength > 0)
+ {
+ if ((b_type=_mi_get_block_info(&block_info,info->dfile,
+ block_info.next_filepos))
+ & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
+ BLOCK_FATAL_ERROR))
+ {
+ if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
+ my_errno=HA_ERR_RECORD_CHANGED;
+ goto err;
+ }
+ if (flag == 0) /* First block */
+ {
+ flag=1;
+ if (reclength != block_info.rec_len)
+ {
+ my_errno=HA_ERR_RECORD_CHANGED;
+ goto err;
+ }
+ } else if (reclength < block_info.data_len)
+ {
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ goto err;
+ }
+ reclength-=block_info.data_len;
+ if (_mi_cmp_buffer(info->dfile,record,block_info.filepos,
+ block_info.data_len))
+ {
+ my_errno=HA_ERR_RECORD_CHANGED;
+ goto err;
+ }
+ flag=1;
+ record+=block_info.data_len;
+ }
+ }
+ my_errno=0;
+err:
+ if (buffer != info->rec_buff)
+ my_afree((gptr) buffer);
+ DBUG_RETURN(my_errno);
+}
+
+
+ /* Compare file to buffert */
+
+static int _mi_cmp_buffer(File file, const byte *buff, my_off_t filepos,
+ uint length)
+{
+ uint next_length;
+ char temp_buff[IO_SIZE*2];
+ DBUG_ENTER("_mi_cmp_buffer");
+
+ next_length= IO_SIZE*2 - (uint) (filepos & (IO_SIZE-1));
+
+ while (length > IO_SIZE*2)
+ {
+ if (my_pread(file,temp_buff,next_length,filepos, MYF(MY_NABP)) ||
+ memcmp((byte*) buff,temp_buff,next_length))
+ goto err;
+ filepos+=next_length;
+ buff+=next_length;
+ length-= next_length;
+ next_length=IO_SIZE*2;
+ }
+ if (my_pread(file,temp_buff,length,filepos,MYF(MY_NABP)))
+ goto err;
+ DBUG_RETURN(memcmp((byte*) buff,temp_buff,length));
+err:
+ DBUG_RETURN(1);
+}
+
+
+int _mi_read_rnd_dynamic_record(MI_INFO *info, byte *buf,
+ register my_off_t filepos,
+ my_bool skip_deleted_blocks)
+{
+ int flag,info_read,save_errno;
+ uint left_len,b_type;
+ byte *to;
+ MI_BLOCK_INFO block_info;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_read_rnd_dynamic_record");
+
+ info_read=0;
+ LINT_INIT(to);
+
+ if (info->lock_type == F_UNLCK)
+ {
+#ifndef UNSAFE_LOCKING
+ if (share->tot_locks == 0)
+ {
+ if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE) | info->lock_wait))
+ DBUG_RETURN(my_errno);
+ }
+#else
+ info->tmp_lock_type=F_RDLCK;
+#endif
+ }
+ else
+ info_read=1; /* memory-keyinfoblock is ok */
+
+ flag=block_info.second_read=0;
+ left_len=1;
+ do
+ {
+ if (filepos >= info->state->data_file_length)
+ {
+ if (!info_read)
+ { /* Check if changed */
+ info_read=1;
+ info->rec_cache.seek_not_done=1;
+ if (mi_state_info_read_dsk(share->kfile,&share->state,1))
+ goto panic;
+ }
+ if (filepos >= info->state->data_file_length)
+ {
+ my_errno= HA_ERR_END_OF_FILE;
+ goto err;
+ }
+ }
+ if (info->opt_flag & READ_CACHE_USED)
+ {
+ if (_mi_read_cache(&info->rec_cache,(byte*) block_info.header,filepos,
+ sizeof(block_info.header),
+ (!flag && skip_deleted_blocks ? READING_NEXT : 0) |
+ READING_HEADER))
+ goto panic;
+ b_type=_mi_get_block_info(&block_info,-1,filepos);
+ }
+ else
+ {
+ if (info->opt_flag & WRITE_CACHE_USED &&
+ info->rec_cache.pos_in_file <= filepos &&
+ flush_io_cache(&info->rec_cache))
+ DBUG_RETURN(my_errno);
+ info->rec_cache.seek_not_done=1;
+ b_type=_mi_get_block_info(&block_info,info->dfile,filepos);
+ }
+
+ if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
+ BLOCK_FATAL_ERROR))
+ {
+ if ((b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
+ && skip_deleted_blocks)
+ {
+ filepos=block_info.filepos+block_info.block_len;
+ block_info.second_read=0;
+ continue; /* Search after next_record */
+ }
+ if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
+ {
+ my_errno=HA_ERR_RECORD_DELETED;
+ info->lastpos=block_info.filepos;
+ info->nextpos=block_info.filepos+block_info.block_len;
+ }
+ goto err;
+ }
+ if (flag == 0) /* First block */
+ {
+ if (block_info.rec_len > (uint) share->base.max_pack_length)
+ goto panic;
+ info->lastpos=filepos;
+ if (share->base.blobs)
+ {
+ if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
+ &info->rec_buff)))
+ goto err;
+ }
+ else
+ to= info->rec_buff;
+ left_len=block_info.rec_len;
+ }
+ if (left_len < block_info.data_len)
+ goto panic; /* Wrong linked record */
+
+ /* copy information that is already read */
+ {
+ uint offset=(uint) (block_info.filepos - filepos);
+ uint tmp_length= (sizeof(block_info.header) - offset);
+ filepos=block_info.filepos;
+
+ if (tmp_length > block_info.data_len)
+ tmp_length= block_info.data_len;
+ if (tmp_length)
+ {
+ memcpy((byte*) to, block_info.header+offset,tmp_length);
+ block_info.data_len-=tmp_length;
+ left_len-=tmp_length;
+ to+=tmp_length;
+ filepos+=tmp_length;
+ }
+ }
+ /* read rest of record from file */
+ if (block_info.data_len)
+ {
+ if (info->opt_flag & READ_CACHE_USED)
+ {
+ if (_mi_read_cache(&info->rec_cache,(byte*) to,filepos,
+ block_info.data_len,
+ (!flag && skip_deleted_blocks) ? READING_NEXT :0))
+ goto panic;
+ }
+ else
+ {
+ /* VOID(my_seek(info->dfile,filepos,MY_SEEK_SET,MYF(0))); */
+ if (my_read(info->dfile,(byte*) to,block_info.data_len,MYF(MY_NABP)))
+ {
+ if (my_errno == -1)
+ my_errno= HA_ERR_WRONG_IN_RECORD; /* Unexpected end of file */
+ goto err;
+ }
+ }
+ }
+ if (flag++ == 0)
+ {
+ info->nextpos=block_info.filepos+block_info.block_len;
+ skip_deleted_blocks=0;
+ }
+ left_len-=block_info.data_len;
+ to+=block_info.data_len;
+ filepos=block_info.next_filepos;
+ } while (left_len);
+
+ info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
+ fast_mi_writeinfo(info);
+ if (_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
+ MY_FILE_ERROR)
+ DBUG_RETURN(0);
+ DBUG_RETURN(my_errno); /* Wrong record */
+
+panic:
+ my_errno=HA_ERR_WRONG_IN_RECORD; /* Something is fatal wrong */
+err:
+ save_errno=my_errno;
+ VOID(_mi_writeinfo(info,0));
+ DBUG_RETURN(my_errno=save_errno);
+}
+
+
+ /* Read and process header from a dynamic-record-file */
+
+uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos)
+{
+ uint return_val=0;
+ uchar *header=info->header;
+
+ if (file >= 0)
+ {
+ VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
+ if (my_read(file,(char*) header,sizeof(info->header),MYF(0)) !=
+ sizeof(info->header))
+ goto err;
+ }
+ DBUG_DUMP("header",(byte*) header,MI_BLOCK_INFO_HEADER_LENGTH);
+ if (info->second_read)
+ {
+ if (info->header[0] <= 6 || info->header[0] == 13)
+ return_val=BLOCK_SYNC_ERROR;
+ }
+ else
+ {
+ if (info->header[0] > 6 && info->header[0] != 13)
+ return_val=BLOCK_SYNC_ERROR;
+ }
+ info->next_filepos= HA_OFFSET_ERROR; /* Dummy if no next block */
+
+ switch (info->header[0]) {
+ case 0:
+ if ((info->block_len=(uint) mi_uint3korr(header+1)) <
+ MI_MIN_BLOCK_LENGTH ||
+ (info->block_len & (MI_DYN_ALIGN_SIZE -1)))
+ goto err;
+ info->filepos=filepos;
+ info->next_filepos=mi_sizekorr(header+4);
+ info->prev_filepos=mi_sizekorr(header+12);
+#if SIZEOF_OFF_T == 4
+ if ((mi_uint4korr(header+4) != 0 &&
+ (mi_uint4korr(header+4) != (ulong) ~0 ||
+ info->next_filepos != (ulong) ~0)) ||
+ (mi_uint4korr(header+12) != 0 &&
+ (mi_uint4korr(header+12) != (ulong) ~0 ||
+ info->prev_filepos != (ulong) ~0)))
+ goto err;
+#endif
+ return return_val | BLOCK_DELETED; /* Deleted block */
+
+ case 1:
+ info->rec_len=info->data_len=info->block_len=mi_uint2korr(header+1);
+ info->filepos=filepos+3;
+ return return_val | BLOCK_FIRST | BLOCK_LAST;
+ case 2:
+ info->rec_len=info->data_len=info->block_len=mi_uint3korr(header+1);
+ info->filepos=filepos+4;
+ return return_val | BLOCK_FIRST | BLOCK_LAST;
+
+ case 13:
+ info->rec_len=mi_uint4korr(header+1);
+ info->block_len=info->data_len=mi_uint3korr(header+5);
+ info->next_filepos=mi_sizekorr(header+8);
+ info->second_read=1;
+ info->filepos=filepos+16;
+ return return_val | BLOCK_FIRST;
+
+ case 3:
+ info->rec_len=info->data_len=mi_uint2korr(header+1);
+ info->block_len=info->rec_len+ (uint) header[3];
+ info->filepos=filepos+4;
+ return return_val | BLOCK_FIRST | BLOCK_LAST;
+ case 4:
+ info->rec_len=info->data_len=mi_uint3korr(header+1);
+ info->block_len=info->rec_len+ (uint) header[4];
+ info->filepos=filepos+5;
+ return return_val | BLOCK_FIRST | BLOCK_LAST;
+
+ case 5:
+ info->rec_len=mi_uint2korr(header+1);
+ info->block_len=info->data_len=mi_uint2korr(header+3);
+ info->next_filepos=mi_sizekorr(header+5);
+ info->second_read=1;
+ info->filepos=filepos+13;
+ return return_val | BLOCK_FIRST;
+ case 6:
+ info->rec_len=mi_uint3korr(header+1);
+ info->block_len=info->data_len=mi_uint3korr(header+4);
+ info->next_filepos=mi_sizekorr(header+7);
+ info->second_read=1;
+ info->filepos=filepos+15;
+ return return_val | BLOCK_FIRST;
+
+ /* The following blocks are identical to 1-6 without rec_len */
+ case 7:
+ info->data_len=info->block_len=mi_uint2korr(header+1);
+ info->filepos=filepos+3;
+ return return_val | BLOCK_LAST;
+ case 8:
+ info->data_len=info->block_len=mi_uint3korr(header+1);
+ info->filepos=filepos+4;
+ return return_val | BLOCK_LAST;
+
+ case 9:
+ info->data_len=mi_uint2korr(header+1);
+ info->block_len=info->data_len+ (uint) header[3];
+ info->filepos=filepos+4;
+ return return_val | BLOCK_LAST;
+ case 10:
+ info->data_len=mi_uint3korr(header+1);
+ info->block_len=info->data_len+ (uint) header[4];
+ info->filepos=filepos+5;
+ return return_val | BLOCK_LAST;
+
+ case 11:
+ info->data_len=info->block_len=mi_uint2korr(header+1);
+ info->next_filepos=mi_sizekorr(header+3);
+ info->second_read=1;
+ info->filepos=filepos+11;
+ return return_val;
+ case 12:
+ info->data_len=info->block_len=mi_uint3korr(header+1);
+ info->next_filepos=mi_sizekorr(header+4);
+ info->second_read=1;
+ info->filepos=filepos+12;
+ return return_val;
+ }
+
+err:
+ my_errno=HA_ERR_WRONG_IN_RECORD; /* Garbage */
+ return BLOCK_ERROR;
+}
diff --git a/storage/myisam/mi_extra.c b/storage/myisam/mi_extra.c
new file mode 100644
index 00000000000..ba32bb9115a
--- /dev/null
+++ b/storage/myisam/mi_extra.c
@@ -0,0 +1,405 @@
+/* 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 */
+
+#include "myisamdef.h"
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+static void mi_extra_keyflag(MI_INFO *info, enum ha_extra_function function);
+
+
+/*
+ Set options and buffers to optimize table handling
+
+ SYNOPSIS
+ mi_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 mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg)
+{
+ int error=0;
+ ulong cache_size;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("mi_extra");
+ DBUG_PRINT("enter",("function: %d",(int) function));
+
+ switch (function) {
+ case HA_EXTRA_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)
+ mi_alloc_rec_buff(info, -1, &info->rec_buff);
+#if defined(HAVE_MMAP) && defined(HAVE_MADVICE)
+ 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;
+ /* Fall through */
+
+ 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 defined(HAVE_MMAP) && defined(HAVE_MADVICE)
+ if ((share->options & HA_OPTION_COMPRESS_RECORD))
+ {
+ pthread_mutex_lock(&share->intern_lock);
+ if (_mi_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_MADVICE)
+ 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)))
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_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=_mi_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 (share->state.key_map)
+ {
+ MI_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)
+ {
+ share->state.key_map&= ~ ((ulonglong) 1 << 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=mi_state_info_write(share->kfile,&share->state,1 | 2);
+ }
+ break;
+ case HA_EXTRA_FORCE_REOPEN:
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ share->last_version= 0L; /* Impossible version */
+ pthread_mutex_unlock(&THR_LOCK_myisam);
+ break;
+ case HA_EXTRA_PREPARE_FOR_DELETE:
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ 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;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_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 (mi_lock_database(info,F_UNLCK))
+ error=my_errno;
+ info->lock_type = F_UNLCK;
+ }
+ if (share->kfile >= 0)
+ _mi_decrement_open_count(info);
+ if (share->kfile >= 0 && my_close(share->kfile,MYF(0)))
+ error=my_errno;
+ {
+ LIST *list_element ;
+ for (list_element=myisam_open_list ;
+ list_element ;
+ list_element=list_element->next)
+ {
+ MI_INFO *tmpinfo=(MI_INFO*) 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_myisam);
+ break;
+ case HA_EXTRA_FLUSH:
+ if (!share->temporary)
+ flush_key_blocks(share->key_cache, share->kfile, FLUSH_KEEP);
+#ifdef HAVE_PWRITE
+ _mi_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;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info); /* Fatal error found */
+ }
+ }
+ if (share->base.blobs)
+ mi_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:
+ mi_extra_keyflag(info, function);
+ break;
+ case HA_EXTRA_KEY_CACHE:
+ case HA_EXTRA_NO_KEY_CACHE:
+ default:
+ break;
+ }
+ {
+ char tmp[1];
+ tmp[0]=function;
+ myisam_log_command(MI_LOG_EXTRA,info,(byte*) tmp,1,error);
+ }
+ DBUG_RETURN(error);
+} /* mi_extra */
+
+
+/*
+ Start/Stop Inserting Duplicates Into a Table, WL#1648.
+ */
+static void mi_extra_keyflag(MI_INFO *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;
+ }
+ }
+}
+
diff --git a/storage/myisam/mi_info.c b/storage/myisam/mi_info.c
new file mode 100644
index 00000000000..bdece9c2ee3
--- /dev/null
+++ b/storage/myisam/mi_info.c
@@ -0,0 +1,140 @@
+/* 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 */
+
+/* Return useful base information for an open table */
+
+#include "myisamdef.h"
+#ifdef __WIN__
+#include <sys/stat.h>
+#endif
+
+ /* Get position to last record */
+
+my_off_t mi_position(MI_INFO *info)
+{
+ return info->lastpos;
+}
+
+
+/* Get information about the table */
+/* if flag == 2 one get current info (no sync from database */
+
+int mi_status(MI_INFO *info, register MI_ISAMINFO *x, uint flag)
+{
+ MY_STAT state;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("mi_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(_mi_readinfo(info,F_RDLCK,0));
+ fast_mi_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= mi_get_pointer_length(share->base.max_data_file_length,
+ myisam_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;
+ /*
+ The following should be included even if we are not compiling with
+ USE_RAID as the client must be able to request it!
+ */
+ x->raid_type= share->base.raid_type;
+ x->raid_chunks= share->base.raid_chunks;
+ x->raid_chunksize= share->base.raid_chunksize;
+ }
+ 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
+ mi_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 mi_report_error(int errcode, const char *file_name)
+{
+ size_t lgt;
+ DBUG_ENTER("mi_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;
+}
+
diff --git a/storage/myisam/mi_key.c b/storage/myisam/mi_key.c
new file mode 100644
index 00000000000..ab5ddd3a378
--- /dev/null
+++ b/storage/myisam/mi_key.c
@@ -0,0 +1,574 @@
+/* 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 */
+
+/* Functions to handle keys */
+
+#include "myisamdef.h"
+#include "m_ctype.h"
+#include "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 _mi_put_key_in_record(MI_INFO *info,uint keynr,byte *record);
+
+/*
+ Make a intern key from a record
+
+ SYNOPSIS
+ _mi_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 _mi_make_key(register MI_INFO *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("_mi_make_key");
+
+ if (info->s->keyinfo[keynr].flag & HA_SPATIAL)
+ {
+ /*
+ TODO: nulls processing
+ */
+#ifdef HAVE_SPATIAL
+ return sp_make_key(info,keynr,key,record,filepos);
+#else
+ DBUG_ASSERT(0); /* mi_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;
+ 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=_mi_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;
+ }
+ _mi_dpointer(info,key,filepos);
+ DBUG_PRINT("exit",("keynr: %d",keynr));
+ DBUG_DUMP("key",(byte*) start,(uint) (key-start)+keyseg->length);
+ DBUG_EXECUTE("key",
+ _mi_print_key(DBUG_FILE,info->s->keyinfo[keynr].seg,start,
+ (uint) (key-start)););
+ DBUG_RETURN((uint) (key-start)); /* Return keylength */
+} /* _mi_make_key */
+
+
+/*
+ Pack a key to intern format from given format (c_rkey)
+
+ SYNOPSIS
+ _mi_pack_key()
+ info MyISAM 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 _mi_pack_key(register MI_INFO *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("_mi_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 */
+ 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 mi_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));
+} /* _mi_pack_key */
+
+
+ /* Put a key in record */
+ /* Used when only-keyread is wanted */
+
+static int _mi_put_key_in_record(register MI_INFO *info, uint keynr,
+ byte *record)
+{
+ reg2 byte *key;
+ byte *pos,*key_end;
+ reg1 HA_KEYSEG *keyseg;
+ byte *blob_ptr;
+ DBUG_ENTER("_mi_put_key_in_record");
+
+ if (info->s->base.blobs && info->s->keyinfo[keynr].flag & HA_VAR_LENGTH_KEY)
+ {
+ if (!(blob_ptr=
+ mi_alloc_rec_buff(info, info->s->keyinfo[keynr].keylength,
+ &info->rec_buff)))
+ goto err;
+ }
+ key=(byte*) info->lastkey;
+ 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;
+ _my_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 */
+} /* _mi_put_key_in_record */
+
+
+ /* Here when key reads are used */
+
+int _mi_read_key_record(MI_INFO *info, my_off_t filepos, byte *buf)
+{
+ fast_mi_writeinfo(info);
+ if (filepos != HA_OFFSET_ERROR)
+ {
+ if (info->lastinx >= 0)
+ { /* Read only key */
+ if (_mi_put_key_in_record(info,(uint) info->lastinx,buf))
+ {
+ mi_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 */
+}
+
+
+/*
+ Update auto_increment info
+
+ SYNOPSIS
+ update_auto_increment()
+ info MyISAM handler
+ record Row to update
+
+ IMPLEMENTATION
+ Only replace the auto_increment value if it is higher than the previous
+ one. For signed columns we don't update the auto increment value if it's
+ less than zero.
+*/
+
+void update_auto_increment(MI_INFO *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.
+ */
+ set_if_bigger(info->s->state.auto_increment,
+ (s_value > 0) ? (ulonglong) s_value : value);
+}
diff --git a/storage/myisam/mi_keycache.c b/storage/myisam/mi_keycache.c
new file mode 100644
index 00000000000..fb13f3703a2
--- /dev/null
+++ b/storage/myisam/mi_keycache.c
@@ -0,0 +1,162 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Key cache assignments
+*/
+
+#include "myisamdef.h"
+
+/*
+ Assign pages of the index file for a table to a key cache
+
+ SYNOPSIS
+ mi_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 mi_assign_to_key_cache(MI_INFO *info,
+ ulonglong key_map __attribute__((unused)),
+ KEY_CACHE *key_cache)
+{
+ int error= 0;
+ MYISAM_SHARE* share= info->s;
+ DBUG_ENTER("mi_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
+ myisam 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;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_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 myisam 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 MyISAM entries that uses one key cache to another key cache
+
+ SYNOPSIS
+ mi_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 MyISAM
+ 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 MyISAM table list structure.
+
+ This is safe as long as it's only MyISAM that is using this specific
+ key cache.
+*/
+
+
+void mi_change_key_cache(KEY_CACHE *old_key_cache,
+ KEY_CACHE *new_key_cache)
+{
+ LIST *pos;
+ DBUG_ENTER("mi_change_key_cache");
+
+ /*
+ Lock list to ensure that no one can close the table while we manipulate it
+ */
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ for (pos=myisam_open_list ; pos ; pos=pos->next)
+ {
+ MI_INFO *info= (MI_INFO*) pos->data;
+ MYISAM_SHARE *share= info->s;
+ if (share->key_cache == old_key_cache)
+ mi_assign_to_key_cache(info, (ulonglong) ~0, new_key_cache);
+ }
+
+ /*
+ We have to do the following call while we have the lock on the
+ MyISAM 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_myisam);
+}
diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c
new file mode 100644
index 00000000000..789d74680ef
--- /dev/null
+++ b/storage/myisam/mi_locking.c
@@ -0,0 +1,510 @@
+/* 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 */
+
+/*
+ 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 "myisamdef.h"
+
+ /* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
+
+int mi_lock_database(MI_INFO *info, int lock_type)
+{
+ int error;
+ uint count;
+ MYISAM_SHARE *share=info->s;
+ uint flag;
+ DBUG_ENTER("mi_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);
+ }
+
+ flag=error=0;
+ pthread_mutex_lock(&share->intern_lock);
+ if (share->kfile >= 0) /* May only be false on windows */
+ {
+ switch (lock_type) {
+ case F_UNLCK:
+ 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;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_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;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_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)
+ {
+ 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 (mi_state_info_write(share->kfile, &share->state, 1))
+ error=my_errno;
+ share->changed=0;
+ if (myisam_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)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info);
+ }
+ }
+ if (info->lock_type != F_EXTRA_LCK)
+ {
+ if (share->r_locks)
+ { /* Only read locks left */
+ flag=1;
+ if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
+ MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
+ error=my_errno;
+ }
+ else if (!share->w_locks)
+ { /* No more locks */
+ flag=1;
+ if (my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
+ error=my_errno;
+ }
+ }
+ }
+ 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.
+ */
+ if (share->w_locks == 1)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE)))
+ {
+ error=my_errno;
+ break;
+ }
+ }
+ share->w_locks--;
+ share->r_locks++;
+ info->lock_type=lock_type;
+ break;
+ }
+ if (!share->r_locks && !share->w_locks)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE))
+ {
+ error=my_errno;
+ break;
+ }
+ if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
+ {
+ error=my_errno;
+ VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE)));
+ my_errno=error;
+ break;
+ }
+ }
+ VOID(_mi_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)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ MYF(info->lock_wait | MY_SEEK_NOT_DONE)))
+ {
+ error=my_errno;
+ break;
+ }
+ share->r_locks--;
+ share->w_locks++;
+ info->lock_type=lock_type;
+ break;
+ }
+ }
+ if (!(share->options & HA_OPTION_READ_ONLY_DATA))
+ {
+ if (!share->w_locks)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE))
+ {
+ error=my_errno;
+ break;
+ }
+ if (!share->r_locks)
+ {
+ if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
+ {
+ error=my_errno;
+ VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE));
+ my_errno=error;
+ break;
+ }
+ }
+ }
+ }
+ VOID(_mi_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);
+#if defined(FULL_LOG) || defined(_lint)
+ lock_type|=(int) (flag << 8); /* Set bit to set if real lock */
+ myisam_log_command(MI_LOG_LOCK,info,(byte*) &lock_type,sizeof(lock_type),
+ error);
+#endif
+ DBUG_RETURN(error);
+} /* mi_lock_database */
+
+
+/****************************************************************************
+ The following functions are called by thr_lock() in threaded applications
+****************************************************************************/
+
+void mi_get_status(void* param)
+{
+ MI_INFO *info=(MI_INFO*) param;
+ DBUG_ENTER("mi_get_status");
+ DBUG_PRINT("info",("key_file: %ld data_file: %ld",
+ (long) info->s->state.state.key_file_length,
+ (long) info->s->state.state.data_file_length));
+#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;
+ DBUG_VOID_RETURN;
+}
+
+void mi_update_status(void* param)
+{
+ MI_INFO *info=(MI_INFO*) 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;
+ }
+
+ /*
+ We have to flush the write cache here as other threads may start
+ reading the table before mi_lock_database() is called
+ */
+ if (info->opt_flag & WRITE_CACHE_USED)
+ {
+ if (end_io_cache(&info->rec_cache))
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info);
+ }
+ info->opt_flag&= ~WRITE_CACHE_USED;
+ }
+}
+
+void mi_copy_status(void* to,void *from)
+{
+ ((MI_INFO*) to)->state= &((MI_INFO*) from)->save_state;
+}
+
+
+/*
+ Check if should allow concurrent inserts
+
+ IMPLEMENTATION
+ Don't allow concurrent inserts if we have a hole in the table.
+
+ NOTES
+ Rtree indexes are disabled in mi_open()
+
+ RETURN
+ 0 ok to use concurrent inserts
+ 1 not ok
+*/
+
+my_bool mi_check_status(void* param)
+{
+ MI_INFO *info=(MI_INFO*) param;
+ return (my_bool) (info->s->state.dellink != HA_OFFSET_ERROR);
+}
+
+
+/****************************************************************************
+ ** functions to read / write the state
+****************************************************************************/
+
+int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer)
+{
+ DBUG_ENTER("_mi_readinfo");
+
+ if (info->lock_type == F_UNLCK)
+ {
+ MYISAM_SHARE *share=info->s;
+ if (!share->tot_locks)
+ {
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE))
+ DBUG_RETURN(1);
+ if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
+ {
+ int error=my_errno ? my_errno : -1;
+ VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE)));
+ my_errno=error;
+ DBUG_RETURN(1);
+ }
+ }
+ if (check_keybuffer)
+ VOID(_mi_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);
+} /* _mi_readinfo */
+
+
+/*
+ Every isam-function that uppdates the isam-database MUST end with this
+ request
+*/
+
+int _mi_writeinfo(register MI_INFO *info, uint operation)
+{
+ int error,olderror;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_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=mi_state_info_write(share->kfile, &share->state, 1)))
+ olderror=my_errno;
+#ifdef __WIN__
+ if (myisam_flush)
+ {
+ _commit(share->kfile);
+ _commit(info->dfile);
+ }
+#endif
+ }
+ if (!(operation & WRITEINFO_NO_UNLOCK) &&
+ my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
+ DBUG_RETURN(1);
+ my_errno=olderror;
+ }
+ else if (operation)
+ share->changed= 1; /* Mark keyfile changed */
+ DBUG_RETURN(error);
+} /* _mi_writeinfo */
+
+
+ /* Test if someone has changed the database */
+ /* (Should be called after readinfo) */
+
+int _mi_test_if_changed(register MI_INFO *info)
+{
+ MYISAM_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 mi_is_changed */
+ return 1;
+ }
+ return (!(info->update & HA_STATE_AKTIV) ||
+ (info->update & (HA_STATE_WRITTEN | HA_STATE_DELETED |
+ HA_STATE_KEY_CHANGED)));
+} /* _mi_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 mi_mark_file_change(). (We have a write lock on the file
+ when this happens)
+ - In mi_close() it's decremented by _mi_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 MYISAM file wasn't properly closed. (This is true if
+ my_disable_locking is set).
+*/
+
+
+int _mi_mark_file_changed(MI_INFO *info)
+{
+ char buff[3];
+ register MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_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 _mi_decrement_open_count(MI_INFO *info)
+{
+ char buff[2];
+ register MYISAM_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=mi_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=mi_lock_database(info,old_lock);
+ }
+ return test(lock_error || write_error);
+}
diff --git a/storage/myisam/mi_log.c b/storage/myisam/mi_log.c
new file mode 100644
index 00000000000..13842c56828
--- /dev/null
+++ b/storage/myisam/mi_log.c
@@ -0,0 +1,164 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Logging of MyISAM commands and records on logfile for debugging
+ The log can be examined with help of the myisamlog command.
+*/
+
+#include "myisamdef.h"
+#if defined(MSDOS) || defined(__WIN__)
+#include <fcntl.h>
+#ifndef __WIN__
+#include <process.h>
+#endif
+#endif
+#ifdef VMS
+#include <processes.h>
+#endif
+
+#undef GETPID /* For HPUX */
+#ifdef THREAD
+#define GETPID() (log_type == 1 ? (long) myisam_pid : (long) my_thread_id());
+#else
+#define GETPID() myisam_pid
+#endif
+
+ /* Activate logging if flag is 1 and reset logging if flag is 0 */
+
+static int log_type=0;
+ulong myisam_pid=0;
+
+int mi_log(int activate_log)
+{
+ int error=0;
+ char buff[FN_REFLEN];
+ DBUG_ENTER("mi_log");
+
+ log_type=activate_log;
+ if (activate_log)
+ {
+ if (!myisam_pid)
+ myisam_pid=(ulong) getpid();
+ if (myisam_log_file < 0)
+ {
+ if ((myisam_log_file = my_create(fn_format(buff,myisam_log_filename,
+ "",".log",4),
+ 0,(O_RDWR | O_BINARY | O_APPEND),MYF(0)))
+ < 0)
+ DBUG_RETURN(my_errno);
+ }
+ }
+ else if (myisam_log_file >= 0)
+ {
+ error=my_close(myisam_log_file,MYF(0)) ? my_errno : 0 ;
+ myisam_log_file= -1;
+ }
+ DBUG_RETURN(error);
+}
+
+
+ /* Logging of records and commands on logfile */
+ /* All logs starts with command(1) dfile(2) process(4) result(2) */
+
+void _myisam_log(enum myisam_log_commands command, MI_INFO *info,
+ const byte *buffert, uint length)
+{
+ char buff[11];
+ int error,old_errno;
+ ulong pid=(ulong) GETPID();
+ old_errno=my_errno;
+ bzero(buff,sizeof(buff));
+ buff[0]=(char) command;
+ mi_int2store(buff+1,info->dfile);
+ mi_int4store(buff+3,pid);
+ mi_int2store(buff+9,length);
+
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ error=my_lock(myisam_log_file,F_WRLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE));
+ VOID(my_write(myisam_log_file,buff,sizeof(buff),MYF(0)));
+ VOID(my_write(myisam_log_file,buffert,length,MYF(0)));
+ if (!error)
+ error=my_lock(myisam_log_file,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE));
+ pthread_mutex_unlock(&THR_LOCK_myisam);
+ my_errno=old_errno;
+}
+
+
+void _myisam_log_command(enum myisam_log_commands command, MI_INFO *info,
+ const byte *buffert, uint length, int result)
+{
+ char buff[9];
+ int error,old_errno;
+ ulong pid=(ulong) GETPID();
+
+ old_errno=my_errno;
+ buff[0]=(char) command;
+ mi_int2store(buff+1,info->dfile);
+ mi_int4store(buff+3,pid);
+ mi_int2store(buff+7,result);
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ error=my_lock(myisam_log_file,F_WRLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE));
+ VOID(my_write(myisam_log_file,buff,sizeof(buff),MYF(0)));
+ if (buffert)
+ VOID(my_write(myisam_log_file,buffert,length,MYF(0)));
+ if (!error)
+ error=my_lock(myisam_log_file,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE));
+ pthread_mutex_unlock(&THR_LOCK_myisam);
+ my_errno=old_errno;
+}
+
+
+void _myisam_log_record(enum myisam_log_commands command, MI_INFO *info,
+ const byte *record, my_off_t filepos, int result)
+{
+ char buff[21],*pos;
+ int error,old_errno;
+ uint length;
+ ulong pid=(ulong) GETPID();
+
+ old_errno=my_errno;
+ if (!info->s->base.blobs)
+ length=info->s->base.reclength;
+ else
+ length=info->s->base.reclength+ _my_calc_total_blob_length(info,record);
+ buff[0]=(char) command;
+ mi_int2store(buff+1,info->dfile);
+ mi_int4store(buff+3,pid);
+ mi_int2store(buff+7,result);
+ mi_sizestore(buff+9,filepos);
+ mi_int4store(buff+17,length);
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ error=my_lock(myisam_log_file,F_WRLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE));
+ VOID(my_write(myisam_log_file,buff,sizeof(buff),MYF(0)));
+ VOID(my_write(myisam_log_file,(byte*) record,info->s->base.reclength,MYF(0)));
+ if (info->s->base.blobs)
+ {
+ MI_BLOB *blob,*end;
+
+ for (end=info->blobs+info->s->base.blobs, blob= info->blobs;
+ blob != end ;
+ blob++)
+ {
+ memcpy_fixed(&pos,record+blob->offset+blob->pack_length,sizeof(char*));
+ VOID(my_write(myisam_log_file,pos,blob->length,MYF(0)));
+ }
+ }
+ if (!error)
+ error=my_lock(myisam_log_file,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE));
+ pthread_mutex_unlock(&THR_LOCK_myisam);
+ my_errno=old_errno;
+}
diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c
new file mode 100644
index 00000000000..d65a46a92fb
--- /dev/null
+++ b/storage/myisam/mi_open.c
@@ -0,0 +1,1270 @@
+/* Copyright (C) 2000,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 */
+
+/* open a isam-database */
+
+#include "fulltext.h"
+#include "sp_defs.h"
+#include "rt_index.h"
+#include <m_ctype.h>
+
+#if defined(MSDOS) || defined(__WIN__)
+#ifdef __WIN__
+#include <fcntl.h>
+#else
+#include <process.h> /* Prototype for getpid */
+#endif
+#endif
+#ifdef VMS
+#include "static.c"
+#endif
+
+static void setup_key_functions(MI_KEYDEF *keyinfo);
+#define get_next_element(to,pos,size) { memcpy((char*) to,pos,(size_t) size); \
+ pos+=size;}
+
+
+#define disk_pos_assert(pos, end_pos) \
+if (pos > end_pos) \
+{ \
+ my_errno=HA_ERR_CRASHED; \
+ goto err; \
+}
+
+
+/******************************************************************************
+** Return the shared struct if the table is already open.
+** In MySQL the server will handle version issues.
+******************************************************************************/
+
+MI_INFO *test_if_reopen(char *filename)
+{
+ LIST *pos;
+
+ for (pos=myisam_open_list ; pos ; pos=pos->next)
+ {
+ MI_INFO *info=(MI_INFO*) pos->data;
+ MYISAM_SHARE *share=info->s;
+ if (!strcmp(share->unique_file_name,filename) && share->last_version)
+ return info;
+ }
+ return 0;
+}
+
+
+/******************************************************************************
+ open a MyISAM database.
+ See my_base.h for the handle_locking argument
+ if handle_locking and HA_OPEN_ABORT_IF_CRASHED then abort if the table
+ is marked crashed or if we are not using locking and the table doesn't
+ have an open count of 0.
+******************************************************************************/
+
+MI_INFO *mi_open(const char *name, int mode, uint open_flags)
+{
+ int lock_error,kfile,open_mode,save_errno,have_rtree=0;
+ uint i,j,len,errpos,head_length,base_pos,offset,info_length,keys,
+ key_parts,unique_key_parts,fulltext_keys,uniques;
+ char name_buff[FN_REFLEN], org_name [FN_REFLEN], index_name[FN_REFLEN],
+ data_name[FN_REFLEN];
+ char *disk_cache, *disk_pos, *end_pos;
+ MI_INFO info,*m_info,*old_info;
+ MYISAM_SHARE share_buff,*share;
+ ulong rec_per_key_part[MI_MAX_POSSIBLE_KEY*MI_MAX_KEY_SEG];
+ my_off_t key_root[MI_MAX_POSSIBLE_KEY],key_del[MI_MAX_KEY_BLOCK_SIZE];
+ ulonglong max_key_file_length, max_data_file_length;
+ DBUG_ENTER("mi_open");
+
+ LINT_INIT(m_info);
+ kfile= -1;
+ lock_error=1;
+ errpos=0;
+ head_length=sizeof(share_buff.state.header);
+ bzero((byte*) &info,sizeof(info));
+
+ my_realpath(name_buff, fn_format(org_name,name,"",MI_NAME_IEXT,4),MYF(0));
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ if (!(old_info=test_if_reopen(name_buff)))
+ {
+ share= &share_buff;
+ bzero((gptr) &share_buff,sizeof(share_buff));
+ share_buff.state.rec_per_key_part=rec_per_key_part;
+ share_buff.state.key_root=key_root;
+ share_buff.state.key_del=key_del;
+ share_buff.key_cache= multi_key_cache_search(name_buff, strlen(name_buff));
+
+ DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_open",
+ if (strstr(name, "/t1"))
+ {
+ my_errno= HA_ERR_CRASHED;
+ goto err;
+ });
+ if ((kfile=my_open(name_buff,(open_mode=O_RDWR) | O_SHARE,MYF(0))) < 0)
+ {
+ if ((errno != EROFS && errno != EACCES) ||
+ mode != O_RDONLY ||
+ (kfile=my_open(name_buff,(open_mode=O_RDONLY) | O_SHARE,MYF(0))) < 0)
+ goto err;
+ }
+ share->mode=open_mode;
+ errpos=1;
+ if (my_read(kfile,(char*) share->state.header.file_version,head_length,
+ MYF(MY_NABP)))
+ {
+ my_errno= HA_ERR_NOT_A_TABLE;
+ goto err;
+ }
+ if (memcmp((byte*) share->state.header.file_version,
+ (byte*) myisam_file_magic, 4))
+ {
+ DBUG_PRINT("error",("Wrong header in %s",name_buff));
+ DBUG_DUMP("error_dump",(char*) share->state.header.file_version,
+ head_length);
+ my_errno=HA_ERR_NOT_A_TABLE;
+ goto err;
+ }
+ share->options= mi_uint2korr(share->state.header.options);
+ if (share->options &
+ ~(HA_OPTION_PACK_RECORD | HA_OPTION_PACK_KEYS |
+ HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA |
+ HA_OPTION_TEMP_COMPRESS_RECORD | HA_OPTION_CHECKSUM |
+ HA_OPTION_TMP_TABLE | HA_OPTION_DELAY_KEY_WRITE))
+ {
+ DBUG_PRINT("error",("wrong options: 0x%lx", share->options));
+ my_errno=HA_ERR_OLD_FILE;
+ goto err;
+ }
+ /* Don't call realpath() if the name can't be a link */
+ if (!strcmp(name_buff, org_name) ||
+ my_readlink(index_name, org_name, MYF(0)) == -1)
+ (void) strmov(index_name, org_name);
+ (void) fn_format(data_name,org_name,"",MI_NAME_DEXT,2+4+16);
+
+ info_length=mi_uint2korr(share->state.header.header_length);
+ base_pos=mi_uint2korr(share->state.header.base_pos);
+ if (!(disk_cache=(char*) my_alloca(info_length+128)))
+ {
+ my_errno=ENOMEM;
+ goto err;
+ }
+ end_pos=disk_cache+info_length;
+ errpos=2;
+
+ VOID(my_seek(kfile,0L,MY_SEEK_SET,MYF(0)));
+ if (!(open_flags & HA_OPEN_TMP_TABLE))
+ {
+ if ((lock_error=my_lock(kfile,F_RDLCK,0L,F_TO_EOF,
+ MYF(open_flags & HA_OPEN_WAIT_IF_LOCKED ?
+ 0 : MY_DONT_WAIT))) &&
+ !(open_flags & HA_OPEN_IGNORE_IF_LOCKED))
+ goto err;
+ }
+ errpos=3;
+ if (my_read(kfile,disk_cache,info_length,MYF(MY_NABP)))
+ {
+ my_errno=HA_ERR_CRASHED;
+ goto err;
+ }
+ len=mi_uint2korr(share->state.header.state_info_length);
+ keys= (uint) share->state.header.keys;
+ uniques= (uint) share->state.header.uniques;
+ fulltext_keys= (uint) share->state.header.fulltext_keys;
+ key_parts= mi_uint2korr(share->state.header.key_parts);
+ unique_key_parts= mi_uint2korr(share->state.header.unique_key_parts);
+ if (len != MI_STATE_INFO_SIZE)
+ {
+ DBUG_PRINT("warning",
+ ("saved_state_info_length: %d state_info_length: %d",
+ len,MI_STATE_INFO_SIZE));
+ }
+ share->state_diff_length=len-MI_STATE_INFO_SIZE;
+
+ mi_state_info_read(disk_cache, &share->state);
+ len= mi_uint2korr(share->state.header.base_info_length);
+ if (len != MI_BASE_INFO_SIZE)
+ {
+ DBUG_PRINT("warning",("saved_base_info_length: %d base_info_length: %d",
+ len,MI_BASE_INFO_SIZE))
+ }
+ disk_pos=my_n_base_info_read(disk_cache+base_pos, &share->base);
+ share->state.state_length=base_pos;
+
+ if (!(open_flags & HA_OPEN_FOR_REPAIR) &&
+ ((share->state.changed & STATE_CRASHED) ||
+ ((open_flags & HA_OPEN_ABORT_IF_CRASHED) &&
+ (my_disable_locking && share->state.open_count))))
+ {
+ DBUG_PRINT("error",("Table is marked as crashed"));
+ my_errno=((share->state.changed & STATE_CRASHED_ON_REPAIR) ?
+ HA_ERR_CRASHED_ON_REPAIR : HA_ERR_CRASHED_ON_USAGE);
+ goto err;
+ }
+
+ /* sanity check */
+ if (share->base.keystart > 65535 || share->base.rec_reflength > 8)
+ {
+ my_errno=HA_ERR_CRASHED;
+ goto err;
+ }
+
+ key_parts+=fulltext_keys*FT_SEGS;
+ if (share->base.max_key_length > MI_MAX_KEY_BUFF || keys > MI_MAX_KEY ||
+ key_parts >= MI_MAX_KEY * MI_MAX_KEY_SEG)
+ {
+ DBUG_PRINT("error",("Wrong key info: Max_key_length: %d keys: %d key_parts: %d", share->base.max_key_length, keys, key_parts));
+ my_errno=HA_ERR_UNSUPPORTED;
+ goto err;
+ }
+
+ /* Correct max_file_length based on length of sizeof(off_t) */
+ max_data_file_length=
+ (share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ?
+ (((ulonglong) 1 << (share->base.rec_reflength*8))-1) :
+ (mi_safe_mul(share->base.pack_reclength,
+ (ulonglong) 1 << (share->base.rec_reflength*8))-1);
+ max_key_file_length=
+ mi_safe_mul(MI_MIN_KEY_BLOCK_LENGTH,
+ ((ulonglong) 1 << (share->base.key_reflength*8))-1);
+#if SIZEOF_OFF_T == 4
+ set_if_smaller(max_data_file_length, INT_MAX32);
+ set_if_smaller(max_key_file_length, INT_MAX32);
+#endif
+#if USE_RAID && SYSTEM_SIZEOF_OFF_T == 4
+ set_if_smaller(max_key_file_length, INT_MAX32);
+ if (!share->base.raid_type)
+ {
+ set_if_smaller(max_data_file_length, INT_MAX32);
+ }
+ else
+ {
+ set_if_smaller(max_data_file_length,
+ (ulonglong) share->base.raid_chunks << 31);
+ }
+#elif !defined(USE_RAID)
+ if (share->base.raid_type)
+ {
+ DBUG_PRINT("error",("Table uses RAID but we don't have RAID support"));
+ my_errno=HA_ERR_UNSUPPORTED;
+ goto err;
+ }
+#endif
+ share->base.max_data_file_length=(my_off_t) max_data_file_length;
+ share->base.max_key_file_length=(my_off_t) max_key_file_length;
+
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ share->base.max_key_length+=2; /* For safety */
+
+ if (!my_multi_malloc(MY_WME,
+ &share,sizeof(*share),
+ &share->state.rec_per_key_part,sizeof(long)*key_parts,
+ &share->keyinfo,keys*sizeof(MI_KEYDEF),
+ &share->uniqueinfo,uniques*sizeof(MI_UNIQUEDEF),
+ &share->keyparts,
+ (key_parts+unique_key_parts+keys+uniques) *
+ sizeof(HA_KEYSEG),
+ &share->rec,
+ (share->base.fields+1)*sizeof(MI_COLUMNDEF),
+ &share->blobs,sizeof(MI_BLOB)*share->base.blobs,
+ &share->unique_file_name,strlen(name_buff)+1,
+ &share->index_file_name,strlen(index_name)+1,
+ &share->data_file_name,strlen(data_name)+1,
+ &share->state.key_root,keys*sizeof(my_off_t),
+ &share->state.key_del,
+ (share->state.header.max_block_size*sizeof(my_off_t)),
+#ifdef THREAD
+ &share->key_root_lock,sizeof(rw_lock_t)*keys,
+#endif
+ NullS))
+ goto err;
+ errpos=4;
+ *share=share_buff;
+ memcpy((char*) share->state.rec_per_key_part,
+ (char*) rec_per_key_part, sizeof(long)*key_parts);
+ memcpy((char*) share->state.key_root,
+ (char*) key_root, sizeof(my_off_t)*keys);
+ memcpy((char*) share->state.key_del,
+ (char*) key_del, (sizeof(my_off_t) *
+ share->state.header.max_block_size));
+ strmov(share->unique_file_name, name_buff);
+ share->unique_name_length= strlen(name_buff);
+ strmov(share->index_file_name, index_name);
+ strmov(share->data_file_name, data_name);
+
+ share->blocksize=min(IO_SIZE,myisam_block_size);
+ {
+ HA_KEYSEG *pos=share->keyparts;
+ for (i=0 ; i < keys ; i++)
+ {
+ share->keyinfo[i].share= share;
+ disk_pos=mi_keydef_read(disk_pos, &share->keyinfo[i]);
+ disk_pos_assert(disk_pos + share->keyinfo[i].keysegs * HA_KEYSEG_SIZE,
+ end_pos);
+ if (share->keyinfo[i].key_alg == HA_KEY_ALG_RTREE)
+ have_rtree=1;
+ set_if_smaller(share->blocksize,share->keyinfo[i].block_length);
+ share->keyinfo[i].seg=pos;
+ for (j=0 ; j < share->keyinfo[i].keysegs; j++,pos++)
+ {
+ disk_pos=mi_keyseg_read(disk_pos, pos);
+
+ if (pos->type == HA_KEYTYPE_TEXT ||
+ pos->type == HA_KEYTYPE_VARTEXT1 ||
+ pos->type == HA_KEYTYPE_VARTEXT2)
+ {
+ if (!pos->language)
+ pos->charset=default_charset_info;
+ else if (!(pos->charset= get_charset(pos->language, MYF(MY_WME))))
+ {
+ my_errno=HA_ERR_UNKNOWN_CHARSET;
+ goto err;
+ }
+ }
+ }
+ if (share->keyinfo[i].flag & HA_SPATIAL)
+ {
+#ifdef HAVE_SPATIAL
+ uint sp_segs=SPDIMS*2;
+ share->keyinfo[i].seg=pos-sp_segs;
+ share->keyinfo[i].keysegs--;
+#else
+ my_errno=HA_ERR_UNSUPPORTED;
+ goto err;
+#endif
+ }
+ else if (share->keyinfo[i].flag & HA_FULLTEXT)
+ {
+ if (!fulltext_keys)
+ { /* 4.0 compatibility code, to be removed in 5.0 */
+ share->keyinfo[i].seg=pos-FT_SEGS;
+ share->keyinfo[i].keysegs-=FT_SEGS;
+ }
+ else
+ {
+ uint j;
+ share->keyinfo[i].seg=pos;
+ for (j=0; j < FT_SEGS; j++)
+ {
+ *pos=ft_keysegs[j];
+ pos[0].language= pos[-1].language;
+ if (!(pos[0].charset= pos[-1].charset))
+ {
+ my_errno=HA_ERR_CRASHED;
+ goto err;
+ }
+ pos++;
+ }
+ }
+ if (!share->ft2_keyinfo.seg)
+ {
+ memcpy(& share->ft2_keyinfo, & share->keyinfo[i], sizeof(MI_KEYDEF));
+ share->ft2_keyinfo.keysegs=1;
+ share->ft2_keyinfo.flag=0;
+ share->ft2_keyinfo.keylength=
+ share->ft2_keyinfo.minlength=
+ share->ft2_keyinfo.maxlength=HA_FT_WLEN+share->base.rec_reflength;
+ share->ft2_keyinfo.seg=pos-1;
+ share->ft2_keyinfo.end=pos;
+ setup_key_functions(& share->ft2_keyinfo);
+ }
+ }
+ setup_key_functions(share->keyinfo+i);
+ share->keyinfo[i].end=pos;
+ pos->type=HA_KEYTYPE_END; /* End */
+ pos->length=share->base.rec_reflength;
+ pos->null_bit=0;
+ pos->flag=0; /* For purify */
+ pos++;
+ }
+ for (i=0 ; i < uniques ; i++)
+ {
+ disk_pos=mi_uniquedef_read(disk_pos, &share->uniqueinfo[i]);
+ disk_pos_assert(disk_pos + share->uniqueinfo[i].keysegs *
+ HA_KEYSEG_SIZE, end_pos);
+ share->uniqueinfo[i].seg=pos;
+ for (j=0 ; j < share->uniqueinfo[i].keysegs; j++,pos++)
+ {
+ disk_pos=mi_keyseg_read(disk_pos, pos);
+ if (pos->type == HA_KEYTYPE_TEXT ||
+ pos->type == HA_KEYTYPE_VARTEXT1 ||
+ pos->type == HA_KEYTYPE_VARTEXT2)
+ {
+ if (!pos->language)
+ pos->charset=default_charset_info;
+ else if (!(pos->charset= get_charset(pos->language, MYF(MY_WME))))
+ {
+ my_errno=HA_ERR_UNKNOWN_CHARSET;
+ goto err;
+ }
+ }
+ }
+ share->uniqueinfo[i].end=pos;
+ pos->type=HA_KEYTYPE_END; /* End */
+ pos->null_bit=0;
+ pos->flag=0;
+ pos++;
+ }
+ }
+
+ disk_pos_assert(disk_pos + share->base.fields *MI_COLUMNDEF_SIZE, end_pos);
+ for (i=j=offset=0 ; i < share->base.fields ; i++)
+ {
+ disk_pos=mi_recinfo_read(disk_pos,&share->rec[i]);
+ share->rec[i].pack_type=0;
+ share->rec[i].huff_tree=0;
+ share->rec[i].offset=offset;
+ if (share->rec[i].type == (int) FIELD_BLOB)
+ {
+ share->blobs[j].pack_length=
+ share->rec[i].length-mi_portable_sizeof_char_ptr;;
+ share->blobs[j].offset=offset;
+ j++;
+ }
+ offset+=share->rec[i].length;
+ }
+ share->rec[i].type=(int) FIELD_LAST; /* End marker */
+
+ if (! lock_error)
+ {
+ VOID(my_lock(kfile,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE)));
+ lock_error=1; /* Database unlocked */
+ }
+
+ if (mi_open_datafile(&info, share, -1))
+ goto err;
+ errpos=5;
+
+ share->kfile=kfile;
+ share->this_process=(ulong) getpid();
+ share->last_process= share->state.process;
+ share->base.key_parts=key_parts;
+ share->base.all_key_parts=key_parts+unique_key_parts;
+ if (!(share->last_version=share->state.version))
+ share->last_version=1; /* Safety */
+ share->rec_reflength=share->base.rec_reflength; /* May be changed */
+ share->base.margin_key_file_length=(share->base.max_key_file_length -
+ (keys ? MI_INDEX_BLOCK_MARGIN *
+ share->blocksize * keys : 0));
+ share->blocksize=min(IO_SIZE,myisam_block_size);
+
+ share->data_file_type=STATIC_RECORD;
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ {
+ share->data_file_type = COMPRESSED_RECORD;
+ share->options|= HA_OPTION_READ_ONLY_DATA;
+ info.s=share;
+ if (_mi_read_pack_info(&info,
+ (pbool)
+ test(!(share->options &
+ (HA_OPTION_PACK_RECORD |
+ HA_OPTION_TEMP_COMPRESS_RECORD)))))
+ goto err;
+ }
+ else if (share->options & HA_OPTION_PACK_RECORD)
+ share->data_file_type = DYNAMIC_RECORD;
+ my_afree((gptr) disk_cache);
+ mi_setup_functions(share);
+#ifdef THREAD
+ thr_lock_init(&share->lock);
+ VOID(pthread_mutex_init(&share->intern_lock,MY_MUTEX_INIT_FAST));
+ for (i=0; i<keys; i++)
+ VOID(my_rwlock_init(&share->key_root_lock[i], NULL));
+ if (!thr_lock_inited)
+ {
+ /* Probably a single threaded program; Don't use concurrent inserts */
+ myisam_concurrent_insert=0;
+ }
+ else if (myisam_concurrent_insert)
+ {
+ share->concurrent_insert=
+ ((share->options & (HA_OPTION_READ_ONLY_DATA | HA_OPTION_TMP_TABLE |
+ HA_OPTION_COMPRESS_RECORD |
+ HA_OPTION_TEMP_COMPRESS_RECORD)) ||
+ (open_flags & HA_OPEN_TMP_TABLE) ||
+ have_rtree) ? 0 : 1;
+ if (share->concurrent_insert)
+ {
+ share->lock.get_status=mi_get_status;
+ share->lock.copy_status=mi_copy_status;
+ share->lock.update_status=mi_update_status;
+ share->lock.check_status=mi_check_status;
+ }
+ }
+#endif
+ }
+ else
+ {
+ share= old_info->s;
+ if (mode == O_RDWR && share->mode == O_RDONLY)
+ {
+ my_errno=EACCES; /* Can't open in write mode */
+ goto err;
+ }
+ if (mi_open_datafile(&info, share, old_info->dfile))
+ goto err;
+ errpos=5;
+ have_rtree= old_info->rtree_recursion_state != NULL;
+ }
+
+ /* alloc and set up private structure parts */
+ if (!my_multi_malloc(MY_WME,
+ &m_info,sizeof(MI_INFO),
+ &info.blobs,sizeof(MI_BLOB)*share->base.blobs,
+ &info.buff,(share->base.max_key_block_length*2+
+ share->base.max_key_length),
+ &info.lastkey,share->base.max_key_length*3+1,
+ &info.first_mbr_key, share->base.max_key_length,
+ &info.filename,strlen(org_name)+1,
+ &info.rtree_recursion_state,have_rtree ? 1024 : 0,
+ NullS))
+ goto err;
+ errpos=6;
+
+ if (!have_rtree)
+ info.rtree_recursion_state= NULL;
+
+ strmov(info.filename,org_name);
+ memcpy(info.blobs,share->blobs,sizeof(MI_BLOB)*share->base.blobs);
+ info.lastkey2=info.lastkey+share->base.max_key_length;
+
+ info.s=share;
+ info.lastpos= HA_OFFSET_ERROR;
+ info.update= (short) (HA_STATE_NEXT_FOUND+HA_STATE_PREV_FOUND);
+ info.opt_flag=READ_CHECK_USED;
+ info.this_unique= (ulong) info.dfile; /* Uniq number in process */
+ if (share->data_file_type == COMPRESSED_RECORD)
+ info.this_unique= share->state.unique;
+ info.this_loop=0; /* Update counter */
+ info.last_unique= share->state.unique;
+ info.last_loop= share->state.update_count;
+ if (mode == O_RDONLY)
+ share->options|=HA_OPTION_READ_ONLY_DATA;
+ info.lock_type=F_UNLCK;
+ info.quick_mode=0;
+ info.bulk_insert=0;
+ info.ft1_to_ft2=0;
+ info.errkey= -1;
+ info.page_changed=1;
+ pthread_mutex_lock(&share->intern_lock);
+ info.read_record=share->read_record;
+ share->reopen++;
+ share->write_flag=MYF(MY_NABP | MY_WAIT_IF_FULL);
+ if (share->options & HA_OPTION_READ_ONLY_DATA)
+ {
+ info.lock_type=F_RDLCK;
+ share->r_locks++;
+ share->tot_locks++;
+ }
+ if ((open_flags & HA_OPEN_TMP_TABLE) ||
+ (share->options & HA_OPTION_TMP_TABLE))
+ {
+ share->temporary=share->delay_key_write=1;
+ share->write_flag=MYF(MY_NABP);
+ share->w_locks++; /* We don't have to update status */
+ share->tot_locks++;
+ info.lock_type=F_WRLCK;
+ }
+ if (((open_flags & HA_OPEN_DELAY_KEY_WRITE) ||
+ (share->options & HA_OPTION_DELAY_KEY_WRITE)) &&
+ myisam_delay_key_write)
+ share->delay_key_write=1;
+ info.state= &share->state.state; /* Change global values by default */
+ pthread_mutex_unlock(&share->intern_lock);
+
+ /* Allocate buffer for one record */
+
+ /* prerequisites: bzero(info) && info->s=share; are met. */
+ if (!mi_alloc_rec_buff(&info, -1, &info.rec_buff))
+ goto err;
+ bzero(info.rec_buff, mi_get_rec_buff_len(&info, info.rec_buff));
+
+ *m_info=info;
+#ifdef THREAD
+ thr_lock_data_init(&share->lock,&m_info->lock,(void*) m_info);
+#endif
+ m_info->open_list.data=(void*) m_info;
+ myisam_open_list=list_add(myisam_open_list,&m_info->open_list);
+
+ pthread_mutex_unlock(&THR_LOCK_myisam);
+ if (myisam_log_file >= 0)
+ {
+ intern_filename(name_buff,share->index_file_name);
+ _myisam_log(MI_LOG_OPEN,m_info,name_buff,(uint) strlen(name_buff));
+ }
+ DBUG_RETURN(m_info);
+
+err:
+ save_errno=my_errno ? my_errno : HA_ERR_END_OF_FILE;
+ if ((save_errno == HA_ERR_CRASHED) ||
+ (save_errno == HA_ERR_CRASHED_ON_USAGE) ||
+ (save_errno == HA_ERR_CRASHED_ON_REPAIR))
+ mi_report_error(save_errno, name);
+ switch (errpos) {
+ case 6:
+ my_free((gptr) m_info,MYF(0));
+ /* fall through */
+ case 5:
+ VOID(my_close(info.dfile,MYF(0)));
+ if (old_info)
+ break; /* Don't remove open table */
+ /* fall through */
+ case 4:
+ my_free((gptr) share,MYF(0));
+ /* fall through */
+ case 3:
+ if (! lock_error)
+ VOID(my_lock(kfile, F_UNLCK, 0L, F_TO_EOF, MYF(MY_SEEK_NOT_DONE)));
+ /* fall through */
+ case 2:
+ my_afree((gptr) disk_cache);
+ /* fall through */
+ case 1:
+ VOID(my_close(kfile,MYF(0)));
+ /* fall through */
+ case 0:
+ default:
+ break;
+ }
+ pthread_mutex_unlock(&THR_LOCK_myisam);
+ my_errno=save_errno;
+ DBUG_RETURN (NULL);
+} /* mi_open */
+
+
+byte *mi_alloc_rec_buff(MI_INFO *info, ulong length, byte **buf)
+{
+ uint extra;
+ uint32 old_length;
+ LINT_INIT(old_length);
+
+ if (! *buf || length > (old_length=mi_get_rec_buff_len(info, *buf)))
+ {
+ byte *newptr = *buf;
+
+ /* to simplify initial init of info->rec_buf in mi_open and mi_extra */
+ if (length == (ulong) -1)
+ {
+ length= max(info->s->base.pack_reclength,
+ info->s->base.max_key_length);
+ /* Avoid unnecessary realloc */
+ if (newptr && length == old_length)
+ return newptr;
+ }
+
+ extra= ((info->s->options & HA_OPTION_PACK_RECORD) ?
+ ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
+ MI_REC_BUFF_OFFSET : 0);
+ if (extra && newptr)
+ newptr-= MI_REC_BUFF_OFFSET;
+ if (!(newptr=(byte*) my_realloc((gptr)newptr, length+extra+8,
+ MYF(MY_ALLOW_ZERO_PTR))))
+ return newptr;
+ *((uint32 *) newptr)= (uint32) length;
+ *buf= newptr+(extra ? MI_REC_BUFF_OFFSET : 0);
+ }
+ return *buf;
+}
+
+
+ulonglong mi_safe_mul(ulonglong a, ulonglong b)
+{
+ ulonglong max_val= ~ (ulonglong) 0; /* my_off_t is unsigned */
+
+ if (!a || max_val / a < b)
+ return max_val;
+ return a*b;
+}
+
+ /* Set up functions in structs */
+
+void mi_setup_functions(register MYISAM_SHARE *share)
+{
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ {
+ share->read_record=_mi_read_pack_record;
+ share->read_rnd=_mi_read_rnd_pack_record;
+ if (!(share->options & HA_OPTION_TEMP_COMPRESS_RECORD))
+ share->calc_checksum=0; /* No checksum */
+ else if (share->options & HA_OPTION_PACK_RECORD)
+ share->calc_checksum= mi_checksum;
+ else
+ share->calc_checksum= mi_static_checksum;
+ }
+ else if (share->options & HA_OPTION_PACK_RECORD)
+ {
+ share->read_record=_mi_read_dynamic_record;
+ share->read_rnd=_mi_read_rnd_dynamic_record;
+ share->delete_record=_mi_delete_dynamic_record;
+ share->compare_record=_mi_cmp_dynamic_record;
+ share->compare_unique=_mi_cmp_dynamic_unique;
+ share->calc_checksum= mi_checksum;
+
+ /* add bits used to pack data to pack_reclength for faster allocation */
+ share->base.pack_reclength+= share->base.pack_bits;
+ if (share->base.blobs)
+ {
+ share->update_record=_mi_update_blob_record;
+ share->write_record=_mi_write_blob_record;
+ }
+ else
+ {
+ share->write_record=_mi_write_dynamic_record;
+ share->update_record=_mi_update_dynamic_record;
+ }
+ }
+ else
+ {
+ share->read_record=_mi_read_static_record;
+ share->read_rnd=_mi_read_rnd_static_record;
+ share->delete_record=_mi_delete_static_record;
+ share->compare_record=_mi_cmp_static_record;
+ share->update_record=_mi_update_static_record;
+ share->write_record=_mi_write_static_record;
+ share->compare_unique=_mi_cmp_static_unique;
+ share->calc_checksum= mi_static_checksum;
+ }
+ if (!(share->options & HA_OPTION_CHECKSUM))
+ share->calc_checksum=0;
+ return;
+}
+
+
+static void setup_key_functions(register MI_KEYDEF *keyinfo)
+{
+ if (keyinfo->key_alg == HA_KEY_ALG_RTREE)
+ {
+#ifdef HAVE_RTREE_KEYS
+ keyinfo->ck_insert = rtree_insert;
+ keyinfo->ck_delete = rtree_delete;
+#else
+ DBUG_ASSERT(0); /* mi_open should check it never happens */
+#endif
+ }
+ else
+ {
+ keyinfo->ck_insert = _mi_ck_write;
+ keyinfo->ck_delete = _mi_ck_delete;
+ }
+ if (keyinfo->flag & HA_BINARY_PACK_KEY)
+ { /* Simple prefix compression */
+ keyinfo->bin_search=_mi_seq_search;
+ keyinfo->get_key=_mi_get_binary_pack_key;
+ keyinfo->pack_key=_mi_calc_bin_pack_key_length;
+ keyinfo->store_key=_mi_store_bin_pack_key;
+ }
+ else if (keyinfo->flag & HA_VAR_LENGTH_KEY)
+ {
+ keyinfo->get_key= _mi_get_pack_key;
+ if (keyinfo->seg[0].flag & HA_PACK_KEY)
+ { /* Prefix compression */
+ if (!keyinfo->seg->charset || use_strnxfrm(keyinfo->seg->charset) ||
+ (keyinfo->seg->flag & HA_NULL_PART))
+ keyinfo->bin_search=_mi_seq_search;
+ else
+ keyinfo->bin_search=_mi_prefix_search;
+ keyinfo->pack_key=_mi_calc_var_pack_key_length;
+ keyinfo->store_key=_mi_store_var_pack_key;
+ }
+ else
+ {
+ keyinfo->bin_search=_mi_seq_search;
+ keyinfo->pack_key=_mi_calc_var_key_length; /* Variable length key */
+ keyinfo->store_key=_mi_store_static_key;
+ }
+ }
+ else
+ {
+ keyinfo->bin_search=_mi_bin_search;
+ keyinfo->get_key=_mi_get_static_key;
+ keyinfo->pack_key=_mi_calc_static_key_length;
+ keyinfo->store_key=_mi_store_static_key;
+ }
+ return;
+}
+
+
+/*
+ Function to save and store the header in the index file (.MYI)
+*/
+
+uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite)
+{
+ uchar buff[MI_STATE_INFO_SIZE + MI_STATE_EXTRA_SIZE];
+ uchar *ptr=buff;
+ uint i, keys= (uint) state->header.keys,
+ key_blocks=state->header.max_block_size;
+ DBUG_ENTER("mi_state_info_write");
+
+ memcpy_fixed(ptr,&state->header,sizeof(state->header));
+ ptr+=sizeof(state->header);
+
+ /* open_count must be first because of _mi_mark_file_changed ! */
+ mi_int2store(ptr,state->open_count); ptr +=2;
+ *ptr++= (uchar)state->changed; *ptr++= state->sortkey;
+ mi_rowstore(ptr,state->state.records); ptr +=8;
+ mi_rowstore(ptr,state->state.del); ptr +=8;
+ mi_rowstore(ptr,state->split); ptr +=8;
+ mi_sizestore(ptr,state->dellink); ptr +=8;
+ mi_sizestore(ptr,state->state.key_file_length); ptr +=8;
+ mi_sizestore(ptr,state->state.data_file_length); ptr +=8;
+ mi_sizestore(ptr,state->state.empty); ptr +=8;
+ mi_sizestore(ptr,state->state.key_empty); ptr +=8;
+ mi_int8store(ptr,state->auto_increment); ptr +=8;
+ mi_int8store(ptr,(ulonglong) state->checksum);ptr +=8;
+ mi_int4store(ptr,state->process); ptr +=4;
+ mi_int4store(ptr,state->unique); ptr +=4;
+ mi_int4store(ptr,state->status); ptr +=4;
+ mi_int4store(ptr,state->update_count); ptr +=4;
+
+ ptr+=state->state_diff_length;
+
+ for (i=0; i < keys; i++)
+ {
+ mi_sizestore(ptr,state->key_root[i]); ptr +=8;
+ }
+ for (i=0; i < key_blocks; i++)
+ {
+ mi_sizestore(ptr,state->key_del[i]); ptr +=8;
+ }
+ if (pWrite & 2) /* From isamchk */
+ {
+ uint key_parts= mi_uint2korr(state->header.key_parts);
+ mi_int4store(ptr,state->sec_index_changed); ptr +=4;
+ mi_int4store(ptr,state->sec_index_used); ptr +=4;
+ mi_int4store(ptr,state->version); ptr +=4;
+ mi_int8store(ptr,state->key_map); ptr +=8;
+ mi_int8store(ptr,(ulonglong) state->create_time); ptr +=8;
+ mi_int8store(ptr,(ulonglong) state->recover_time); ptr +=8;
+ mi_int8store(ptr,(ulonglong) state->check_time); ptr +=8;
+ mi_sizestore(ptr,state->rec_per_key_rows); ptr+=8;
+ for (i=0 ; i < key_parts ; i++)
+ {
+ mi_int4store(ptr,state->rec_per_key_part[i]); ptr+=4;
+ }
+ }
+
+ if (pWrite & 1)
+ DBUG_RETURN(my_pwrite(file,(char*) buff, (uint) (ptr-buff), 0L,
+ MYF(MY_NABP | MY_THREADSAFE)));
+ DBUG_RETURN(my_write(file, (char*) buff, (uint) (ptr-buff),
+ MYF(MY_NABP)));
+}
+
+
+char *mi_state_info_read(char *ptr, MI_STATE_INFO *state)
+{
+ uint i,keys,key_parts,key_blocks;
+ memcpy_fixed(&state->header,ptr, sizeof(state->header));
+ ptr +=sizeof(state->header);
+ keys=(uint) state->header.keys;
+ key_parts=mi_uint2korr(state->header.key_parts);
+ key_blocks=state->header.max_block_size;
+
+ state->open_count = mi_uint2korr(ptr); ptr +=2;
+ state->changed= (bool) *ptr++;
+ state->sortkey = (uint) *ptr++;
+ state->state.records= mi_rowkorr(ptr); ptr +=8;
+ state->state.del = mi_rowkorr(ptr); ptr +=8;
+ state->split = mi_rowkorr(ptr); ptr +=8;
+ state->dellink= mi_sizekorr(ptr); ptr +=8;
+ state->state.key_file_length = mi_sizekorr(ptr); ptr +=8;
+ state->state.data_file_length= mi_sizekorr(ptr); ptr +=8;
+ state->state.empty = mi_sizekorr(ptr); ptr +=8;
+ state->state.key_empty= mi_sizekorr(ptr); ptr +=8;
+ state->auto_increment=mi_uint8korr(ptr); ptr +=8;
+ state->checksum=(ha_checksum) mi_uint8korr(ptr); ptr +=8;
+ state->process= mi_uint4korr(ptr); ptr +=4;
+ state->unique = mi_uint4korr(ptr); ptr +=4;
+ state->status = mi_uint4korr(ptr); ptr +=4;
+ state->update_count=mi_uint4korr(ptr); ptr +=4;
+
+ ptr+= state->state_diff_length;
+
+ for (i=0; i < keys; i++)
+ {
+ state->key_root[i]= mi_sizekorr(ptr); ptr +=8;
+ }
+ for (i=0; i < key_blocks; i++)
+ {
+ state->key_del[i] = mi_sizekorr(ptr); ptr +=8;
+ }
+ state->sec_index_changed = mi_uint4korr(ptr); ptr +=4;
+ state->sec_index_used = mi_uint4korr(ptr); ptr +=4;
+ state->version = mi_uint4korr(ptr); ptr +=4;
+ state->key_map = mi_uint8korr(ptr); ptr +=8;
+ state->create_time = (time_t) mi_sizekorr(ptr); ptr +=8;
+ state->recover_time =(time_t) mi_sizekorr(ptr); ptr +=8;
+ state->check_time = (time_t) mi_sizekorr(ptr); ptr +=8;
+ state->rec_per_key_rows=mi_sizekorr(ptr); ptr +=8;
+ for (i=0 ; i < key_parts ; i++)
+ {
+ state->rec_per_key_part[i]= mi_uint4korr(ptr); ptr+=4;
+ }
+ return ptr;
+}
+
+
+uint mi_state_info_read_dsk(File file, MI_STATE_INFO *state, my_bool pRead)
+{
+ char buff[MI_STATE_INFO_SIZE + MI_STATE_EXTRA_SIZE];
+
+ if (!myisam_single_user)
+ {
+ if (pRead)
+ {
+ if (my_pread(file, buff, state->state_length,0L, MYF(MY_NABP)))
+ return (MY_FILE_ERROR);
+ }
+ else if (my_read(file, buff, state->state_length,MYF(MY_NABP)))
+ return (MY_FILE_ERROR);
+ mi_state_info_read(buff, state);
+ }
+ return 0;
+}
+
+
+/****************************************************************************
+** store and read of MI_BASE_INFO
+****************************************************************************/
+
+uint mi_base_info_write(File file, MI_BASE_INFO *base)
+{
+ uchar buff[MI_BASE_INFO_SIZE], *ptr=buff;
+
+ mi_sizestore(ptr,base->keystart); ptr +=8;
+ mi_sizestore(ptr,base->max_data_file_length); ptr +=8;
+ mi_sizestore(ptr,base->max_key_file_length); ptr +=8;
+ mi_rowstore(ptr,base->records); ptr +=8;
+ mi_rowstore(ptr,base->reloc); ptr +=8;
+ mi_int4store(ptr,base->mean_row_length); ptr +=4;
+ mi_int4store(ptr,base->reclength); ptr +=4;
+ mi_int4store(ptr,base->pack_reclength); ptr +=4;
+ mi_int4store(ptr,base->min_pack_length); ptr +=4;
+ mi_int4store(ptr,base->max_pack_length); ptr +=4;
+ mi_int4store(ptr,base->min_block_length); ptr +=4;
+ mi_int4store(ptr,base->fields); ptr +=4;
+ mi_int4store(ptr,base->pack_fields); ptr +=4;
+ *ptr++=base->rec_reflength;
+ *ptr++=base->key_reflength;
+ *ptr++=base->keys;
+ *ptr++=base->auto_key;
+ mi_int2store(ptr,base->pack_bits); ptr +=2;
+ mi_int2store(ptr,base->blobs); ptr +=2;
+ mi_int2store(ptr,base->max_key_block_length); ptr +=2;
+ mi_int2store(ptr,base->max_key_length); ptr +=2;
+ mi_int2store(ptr,base->extra_alloc_bytes); ptr +=2;
+ *ptr++= base->extra_alloc_procent;
+ *ptr++= base->raid_type;
+ mi_int2store(ptr,base->raid_chunks); ptr +=2;
+ mi_int4store(ptr,base->raid_chunksize); ptr +=4;
+ bzero(ptr,6); ptr +=6; /* extra */
+ return my_write(file,(char*) buff, (uint) (ptr-buff), MYF(MY_NABP));
+}
+
+
+char *my_n_base_info_read(char *ptr, MI_BASE_INFO *base)
+{
+ base->keystart = mi_sizekorr(ptr); ptr +=8;
+ base->max_data_file_length = mi_sizekorr(ptr); ptr +=8;
+ base->max_key_file_length = mi_sizekorr(ptr); ptr +=8;
+ base->records = (ha_rows) mi_sizekorr(ptr); ptr +=8;
+ base->reloc = (ha_rows) mi_sizekorr(ptr); ptr +=8;
+ base->mean_row_length = mi_uint4korr(ptr); ptr +=4;
+ base->reclength = mi_uint4korr(ptr); ptr +=4;
+ base->pack_reclength = mi_uint4korr(ptr); ptr +=4;
+ base->min_pack_length = mi_uint4korr(ptr); ptr +=4;
+ base->max_pack_length = mi_uint4korr(ptr); ptr +=4;
+ base->min_block_length = mi_uint4korr(ptr); ptr +=4;
+ base->fields = mi_uint4korr(ptr); ptr +=4;
+ base->pack_fields = mi_uint4korr(ptr); ptr +=4;
+
+ base->rec_reflength = *ptr++;
+ base->key_reflength = *ptr++;
+ base->keys= *ptr++;
+ base->auto_key= *ptr++;
+ base->pack_bits = mi_uint2korr(ptr); ptr +=2;
+ base->blobs = mi_uint2korr(ptr); ptr +=2;
+ base->max_key_block_length= mi_uint2korr(ptr); ptr +=2;
+ base->max_key_length = mi_uint2korr(ptr); ptr +=2;
+ base->extra_alloc_bytes = mi_uint2korr(ptr); ptr +=2;
+ base->extra_alloc_procent = *ptr++;
+ base->raid_type= *ptr++;
+ base->raid_chunks= mi_uint2korr(ptr); ptr +=2;
+ base->raid_chunksize= mi_uint4korr(ptr); ptr +=4;
+ /* TO BE REMOVED: Fix for old RAID files */
+ if (base->raid_type == 0)
+ {
+ base->raid_chunks=0;
+ base->raid_chunksize=0;
+ }
+
+ ptr+=6;
+ return ptr;
+}
+
+/*--------------------------------------------------------------------------
+ mi_keydef
+---------------------------------------------------------------------------*/
+
+uint mi_keydef_write(File file, MI_KEYDEF *keydef)
+{
+ uchar buff[MI_KEYDEF_SIZE];
+ uchar *ptr=buff;
+
+ *ptr++ = (uchar) keydef->keysegs;
+ *ptr++ = keydef->key_alg; /* Rtree or Btree */
+ mi_int2store(ptr,keydef->flag); ptr +=2;
+ mi_int2store(ptr,keydef->block_length); ptr +=2;
+ mi_int2store(ptr,keydef->keylength); ptr +=2;
+ mi_int2store(ptr,keydef->minlength); ptr +=2;
+ mi_int2store(ptr,keydef->maxlength); ptr +=2;
+ return my_write(file,(char*) buff, (uint) (ptr-buff), MYF(MY_NABP));
+}
+
+char *mi_keydef_read(char *ptr, MI_KEYDEF *keydef)
+{
+ keydef->keysegs = (uint) *ptr++;
+ keydef->key_alg = *ptr++; /* Rtree or Btree */
+
+ keydef->flag = mi_uint2korr(ptr); ptr +=2;
+ keydef->block_length = mi_uint2korr(ptr); ptr +=2;
+ keydef->keylength = mi_uint2korr(ptr); ptr +=2;
+ keydef->minlength = mi_uint2korr(ptr); ptr +=2;
+ keydef->maxlength = mi_uint2korr(ptr); ptr +=2;
+ keydef->block_size = keydef->block_length/MI_MIN_KEY_BLOCK_LENGTH-1;
+ keydef->underflow_block_length=keydef->block_length/3;
+ keydef->version = 0; /* Not saved */
+ return ptr;
+}
+
+/***************************************************************************
+** mi_keyseg
+***************************************************************************/
+
+int mi_keyseg_write(File file, const HA_KEYSEG *keyseg)
+{
+ uchar buff[HA_KEYSEG_SIZE];
+ uchar *ptr=buff;
+ ulong pos;
+
+ *ptr++= keyseg->type;
+ *ptr++= keyseg->language;
+ *ptr++= keyseg->null_bit;
+ *ptr++= keyseg->bit_start;
+ *ptr++= keyseg->bit_end;
+ *ptr++= keyseg->bit_length;
+ mi_int2store(ptr,keyseg->flag); ptr+=2;
+ mi_int2store(ptr,keyseg->length); ptr+=2;
+ mi_int4store(ptr,keyseg->start); ptr+=4;
+ pos= keyseg->null_bit ? keyseg->null_pos : keyseg->bit_pos;
+ mi_int4store(ptr, pos);
+ ptr+=4;
+
+ return my_write(file,(char*) buff, (uint) (ptr-buff), MYF(MY_NABP));
+}
+
+
+char *mi_keyseg_read(char *ptr, HA_KEYSEG *keyseg)
+{
+ keyseg->type = *ptr++;
+ keyseg->language = *ptr++;
+ keyseg->null_bit = *ptr++;
+ keyseg->bit_start = *ptr++;
+ keyseg->bit_end = *ptr++;
+ keyseg->bit_length = *ptr++;
+ keyseg->flag = mi_uint2korr(ptr); ptr +=2;
+ keyseg->length = mi_uint2korr(ptr); ptr +=2;
+ keyseg->start = mi_uint4korr(ptr); ptr +=4;
+ keyseg->null_pos = mi_uint4korr(ptr); ptr +=4;
+ keyseg->charset=0; /* Will be filled in later */
+ if (keyseg->null_bit)
+ keyseg->bit_pos= (uint16)(keyseg->null_pos + (keyseg->null_bit == 7));
+ else
+ {
+ keyseg->bit_pos= (uint16)keyseg->null_pos;
+ keyseg->null_pos= 0;
+ }
+ return ptr;
+}
+
+/*--------------------------------------------------------------------------
+ mi_uniquedef
+---------------------------------------------------------------------------*/
+
+uint mi_uniquedef_write(File file, MI_UNIQUEDEF *def)
+{
+ uchar buff[MI_UNIQUEDEF_SIZE];
+ uchar *ptr=buff;
+
+ mi_int2store(ptr,def->keysegs); ptr+=2;
+ *ptr++= (uchar) def->key;
+ *ptr++ = (uchar) def->null_are_equal;
+
+ return my_write(file,(char*) buff, (uint) (ptr-buff), MYF(MY_NABP));
+}
+
+char *mi_uniquedef_read(char *ptr, MI_UNIQUEDEF *def)
+{
+ def->keysegs = mi_uint2korr(ptr);
+ def->key = ptr[2];
+ def->null_are_equal=ptr[3];
+ return ptr+4; /* 1 extra byte */
+}
+
+/***************************************************************************
+** MI_COLUMNDEF
+***************************************************************************/
+
+uint mi_recinfo_write(File file, MI_COLUMNDEF *recinfo)
+{
+ uchar buff[MI_COLUMNDEF_SIZE];
+ uchar *ptr=buff;
+
+ mi_int2store(ptr,recinfo->type); ptr +=2;
+ mi_int2store(ptr,recinfo->length); ptr +=2;
+ *ptr++ = recinfo->null_bit;
+ mi_int2store(ptr,recinfo->null_pos); ptr+= 2;
+ return my_write(file,(char*) buff, (uint) (ptr-buff), MYF(MY_NABP));
+}
+
+char *mi_recinfo_read(char *ptr, MI_COLUMNDEF *recinfo)
+{
+ recinfo->type= mi_sint2korr(ptr); ptr +=2;
+ recinfo->length=mi_uint2korr(ptr); ptr +=2;
+ recinfo->null_bit= (uint8) *ptr++;
+ recinfo->null_pos=mi_uint2korr(ptr); ptr +=2;
+ return ptr;
+}
+
+/**************************************************************************
+Open data file with or without RAID
+We can't use dup() here as the data file descriptors need to have different
+active seek-positions.
+
+The argument file_to_dup is here for the future if there would on some OS
+exist a dup()-like call that would give us two different file descriptors.
+*************************************************************************/
+
+int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, File file_to_dup __attribute__((unused)))
+{
+#ifdef USE_RAID
+ if (share->base.raid_type)
+ {
+ info->dfile=my_raid_open(share->data_file_name,
+ share->mode | O_SHARE,
+ share->base.raid_type,
+ share->base.raid_chunks,
+ share->base.raid_chunksize,
+ MYF(MY_WME | MY_RAID));
+ }
+ else
+#endif
+ info->dfile=my_open(share->data_file_name, share->mode | O_SHARE,
+ MYF(MY_WME));
+ return info->dfile >= 0 ? 0 : 1;
+}
+
+
+int mi_open_keyfile(MYISAM_SHARE *share)
+{
+ if ((share->kfile=my_open(share->unique_file_name, share->mode | O_SHARE,
+ MYF(MY_WME))) < 0)
+ return 1;
+ return 0;
+}
+
+
+/*
+ Disable all indexes.
+
+ SYNOPSIS
+ mi_disable_indexes()
+ info A pointer to the MyISAM storage engine MI_INFO struct.
+
+ DESCRIPTION
+ Disable all indexes.
+
+ RETURN
+ 0 ok
+*/
+
+int mi_disable_indexes(MI_INFO *info)
+{
+ MYISAM_SHARE *share= info->s;
+
+ share->state.key_map= 0;
+ return 0;
+}
+
+
+/*
+ Enable all indexes
+
+ SYNOPSIS
+ mi_enable_indexes()
+ info A pointer to the MyISAM storage engine MI_INFO struct.
+
+ DESCRIPTION
+ Enable all indexes. The indexes might have been disabled
+ by mi_disable_index() before.
+ The function works only if both data and indexes are empty,
+ otherwise a repair is required.
+ To be sure, call handler::delete_all_rows() before.
+
+ RETURN
+ 0 ok
+ HA_ERR_CRASHED data or index is non-empty.
+*/
+
+int mi_enable_indexes(MI_INFO *info)
+{
+ int error= 0;
+ MYISAM_SHARE *share= info->s;
+
+ if (share->state.state.data_file_length ||
+ (share->state.state.key_file_length != share->base.keystart))
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ error= HA_ERR_CRASHED;
+ }
+ else
+ share->state.key_map= ((ulonglong) 1L << share->base.keys) - 1;
+ return error;
+}
+
+
+/*
+ Test if indexes are disabled.
+
+ SYNOPSIS
+ mi_indexes_are_disabled()
+ info A pointer to the MyISAM storage engine MI_INFO struct.
+
+ DESCRIPTION
+ Test if indexes are disabled.
+
+ RETURN
+ 0 indexes are not disabled
+ 1 all indexes are disabled
+ [2 non-unique indexes are disabled - NOT YET IMPLEMENTED]
+*/
+
+int mi_indexes_are_disabled(MI_INFO *info)
+{
+ MYISAM_SHARE *share= info->s;
+
+ return (! share->state.key_map && share->base.keys);
+}
+
diff --git a/storage/myisam/mi_packrec.c b/storage/myisam/mi_packrec.c
new file mode 100644
index 00000000000..4b512dd89dd
--- /dev/null
+++ b/storage/myisam/mi_packrec.c
@@ -0,0 +1,1358 @@
+/* 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 */
+
+ /* Functions to compressed records */
+
+#include "myisamdef.h"
+
+#define IS_CHAR ((uint) 32768) /* Bit if char (not offset) in tree */
+
+#if INT_MAX > 65536L
+#define BITS_SAVED 32
+#define MAX_QUICK_TABLE_BITS 9 /* Because we may shift in 24 bits */
+#else
+#define BITS_SAVED 16
+#define MAX_QUICK_TABLE_BITS 6
+#endif
+
+#define get_bit(BU) ((BU)->bits ? \
+ (BU)->current_byte & ((mi_bit_type) 1 << --(BU)->bits) :\
+ (fill_buffer(BU), (BU)->bits= BITS_SAVED-1,\
+ (BU)->current_byte & ((mi_bit_type) 1 << (BITS_SAVED-1))))
+#define skip_to_next_byte(BU) ((BU)->bits&=~7)
+#define get_bits(BU,count) (((BU)->bits >= count) ? (((BU)->current_byte >> ((BU)->bits-=count)) & mask[count]) : fill_and_get_bits(BU,count))
+
+#define decode_bytes_test_bit(bit) \
+ if (low_byte & (1 << (7-bit))) \
+ pos++; \
+ if (*pos & IS_CHAR) \
+ { bits-=(bit+1); break; } \
+ pos+= *pos
+
+#define OFFSET_TABLE_SIZE 512
+
+static uint read_huff_table(MI_BIT_BUFF *bit_buff,MI_DECODE_TREE *decode_tree,
+ uint16 **decode_table,byte **intervall_buff,
+ uint16 *tmp_buff);
+static void make_quick_table(uint16 *to_table,uint16 *decode_table,
+ uint *next_free,uint value,uint bits,
+ uint max_bits);
+static void fill_quick_table(uint16 *table,uint bits, uint max_bits,
+ uint value);
+static uint copy_decode_table(uint16 *to_pos,uint offset,
+ uint16 *decode_table);
+static uint find_longest_bitstream(uint16 *table, uint16 *end);
+static void (*get_unpack_function(MI_COLUMNDEF *rec))(MI_COLUMNDEF *field,
+ MI_BIT_BUFF *buff,
+ uchar *to,
+ uchar *end);
+static void uf_zerofill_skip_zero(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_skip_zero(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_space_normal(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_space_endspace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end);
+static void uf_endspace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_space_endspace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_endspace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_space_prespace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end);
+static void uf_prespace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_space_prespace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_prespace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_zerofill_normal(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_constant(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_intervall(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_zero(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static void uf_blob(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end);
+static void uf_varchar1(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end);
+static void uf_varchar2(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end);
+static void decode_bytes(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
+ uchar *to,uchar *end);
+static uint decode_pos(MI_BIT_BUFF *bit_buff,MI_DECODE_TREE *decode_tree);
+static void init_bit_buffer(MI_BIT_BUFF *bit_buff,uchar *buffer,uint length);
+static uint fill_and_get_bits(MI_BIT_BUFF *bit_buff,uint count);
+static void fill_buffer(MI_BIT_BUFF *bit_buff);
+static uint max_bit(uint value);
+#ifdef HAVE_MMAP
+static uchar *_mi_mempack_get_block_info(MI_INFO *myisam,MI_BLOCK_INFO *info,
+ uchar *header);
+#endif
+
+static mi_bit_type mask[]=
+{
+ 0x00000000,
+ 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
+ 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+ 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
+ 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+#if BITS_SAVED > 16
+ 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
+ 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
+ 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
+ 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff,
+#endif
+ };
+
+
+ /* Read all packed info, allocate memory and fix field structs */
+
+my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys)
+{
+ File file;
+ int diff_length;
+ uint i,trees,huff_tree_bits,rec_reflength,length;
+ uint16 *decode_table,*tmp_buff;
+ ulong elements,intervall_length;
+ char *disk_cache,*intervall_buff;
+ uchar header[32];
+ MYISAM_SHARE *share=info->s;
+ MI_BIT_BUFF bit_buff;
+ DBUG_ENTER("_mi_read_pack_info");
+
+ if (myisam_quick_table_bits < 4)
+ myisam_quick_table_bits=4;
+ else if (myisam_quick_table_bits > MAX_QUICK_TABLE_BITS)
+ myisam_quick_table_bits=MAX_QUICK_TABLE_BITS;
+
+ file=info->dfile;
+ my_errno=0;
+ if (my_read(file,(byte*) header,sizeof(header),MYF(MY_NABP)))
+ {
+ if (!my_errno)
+ my_errno=HA_ERR_END_OF_FILE;
+ goto err0;
+ }
+ if (memcmp((byte*) header,(byte*) myisam_pack_file_magic,4))
+ {
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ goto err0;
+ }
+ share->pack.header_length= uint4korr(header+4);
+ share->min_pack_length=(uint) uint4korr(header+8);
+ share->max_pack_length=(uint) uint4korr(header+12);
+ set_if_bigger(share->base.pack_reclength,share->max_pack_length);
+ elements=uint4korr(header+16);
+ intervall_length=uint4korr(header+20);
+ trees=uint2korr(header+24);
+ share->pack.ref_length=header[26];
+ rec_reflength=header[27];
+ diff_length=(int) rec_reflength - (int) share->base.rec_reflength;
+ if (fix_keys)
+ share->rec_reflength=rec_reflength;
+ share->base.min_block_length=share->min_pack_length+1;
+ if (share->min_pack_length > 254)
+ share->base.min_block_length+=2;
+
+ if (!(share->decode_trees=(MI_DECODE_TREE*)
+ my_malloc((uint) (trees*sizeof(MI_DECODE_TREE)+
+ intervall_length*sizeof(byte)),
+ MYF(MY_WME))))
+ goto err0;
+ intervall_buff=(byte*) (share->decode_trees+trees);
+
+ length=(uint) (elements*2+trees*(1 << myisam_quick_table_bits));
+ if (!(share->decode_tables=(uint16*)
+ my_malloc((length+OFFSET_TABLE_SIZE)*sizeof(uint16)+
+ (uint) (share->pack.header_length+7),
+ MYF(MY_WME | MY_ZEROFILL))))
+ goto err1;
+ tmp_buff=share->decode_tables+length;
+ disk_cache=(byte*) (tmp_buff+OFFSET_TABLE_SIZE);
+
+ if (my_read(file,disk_cache,
+ (uint) (share->pack.header_length-sizeof(header)),
+ MYF(MY_NABP)))
+ goto err2;
+
+ huff_tree_bits=max_bit(trees ? trees-1 : 0);
+ init_bit_buffer(&bit_buff, (uchar*) disk_cache,
+ (uint) (share->pack.header_length-sizeof(header)));
+ /* Read new info for each field */
+ for (i=0 ; i < share->base.fields ; i++)
+ {
+ share->rec[i].base_type=(enum en_fieldtype) get_bits(&bit_buff,5);
+ share->rec[i].pack_type=(uint) get_bits(&bit_buff,6);
+ share->rec[i].space_length_bits=get_bits(&bit_buff,5);
+ share->rec[i].huff_tree=share->decode_trees+(uint) get_bits(&bit_buff,
+ huff_tree_bits);
+ share->rec[i].unpack=get_unpack_function(share->rec+i);
+ }
+ skip_to_next_byte(&bit_buff);
+ decode_table=share->decode_tables;
+ for (i=0 ; i < trees ; i++)
+ if (read_huff_table(&bit_buff,share->decode_trees+i,&decode_table,
+ &intervall_buff,tmp_buff))
+ goto err3;
+ decode_table=(uint16*)
+ my_realloc((gptr) share->decode_tables,
+ (uint) ((byte*) decode_table - (byte*) share->decode_tables),
+ MYF(MY_HOLD_ON_ERROR));
+ {
+ long diff=PTR_BYTE_DIFF(decode_table,share->decode_tables);
+ share->decode_tables=decode_table;
+ for (i=0 ; i < trees ; i++)
+ share->decode_trees[i].table=ADD_TO_PTR(share->decode_trees[i].table,
+ diff, uint16*);
+ }
+
+ /* Fix record-ref-length for keys */
+ if (fix_keys)
+ {
+ for (i=0 ; i < share->base.keys ; i++)
+ {
+ share->keyinfo[i].keylength+=(uint16) diff_length;
+ share->keyinfo[i].minlength+=(uint16) diff_length;
+ share->keyinfo[i].maxlength+=(uint16) diff_length;
+ share->keyinfo[i].seg[share->keyinfo[i].keysegs].length=
+ (uint16) rec_reflength;
+ }
+ }
+
+ if (bit_buff.error || bit_buff.pos < bit_buff.end)
+ goto err3;
+
+ DBUG_RETURN(0);
+
+err3:
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+err2:
+ my_free((gptr) share->decode_tables,MYF(0));
+err1:
+ my_free((gptr) share->decode_trees,MYF(0));
+err0:
+ DBUG_RETURN(1);
+}
+
+
+ /* Read on huff-code-table from datafile */
+
+static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree,
+ uint16 **decode_table, byte **intervall_buff,
+ uint16 *tmp_buff)
+{
+ uint min_chr,elements,char_bits,offset_bits,size,intervall_length,table_bits,
+ next_free_offset;
+ uint16 *ptr,*end;
+
+ LINT_INIT(ptr);
+ if (!get_bits(bit_buff,1))
+ {
+ min_chr=get_bits(bit_buff,8);
+ elements=get_bits(bit_buff,9);
+ char_bits=get_bits(bit_buff,5);
+ offset_bits=get_bits(bit_buff,5);
+ intervall_length=0;
+ ptr=tmp_buff;
+ }
+ else
+ {
+ min_chr=0;
+ elements=get_bits(bit_buff,15);
+ intervall_length=get_bits(bit_buff,16);
+ char_bits=get_bits(bit_buff,5);
+ offset_bits=get_bits(bit_buff,5);
+ decode_tree->quick_table_bits=0;
+ ptr= *decode_table;
+ }
+ size=elements*2-2;
+
+ for (end=ptr+size ; ptr < end ; ptr++)
+ {
+ if (get_bit(bit_buff))
+ *ptr= (uint16) get_bits(bit_buff,offset_bits);
+ else
+ *ptr= (uint16) (IS_CHAR + (get_bits(bit_buff,char_bits) + min_chr));
+ }
+ skip_to_next_byte(bit_buff);
+
+ decode_tree->table= *decode_table;
+ decode_tree->intervalls= *intervall_buff;
+ if (! intervall_length)
+ {
+ table_bits=find_longest_bitstream(tmp_buff, tmp_buff+OFFSET_TABLE_SIZE);
+ if (table_bits == (uint) ~0)
+ return 1;
+ if (table_bits > myisam_quick_table_bits)
+ table_bits=myisam_quick_table_bits;
+ next_free_offset= (1 << table_bits);
+ make_quick_table(*decode_table,tmp_buff,&next_free_offset,0,table_bits,
+ table_bits);
+ (*decode_table)+= next_free_offset;
+ decode_tree->quick_table_bits=table_bits;
+ }
+ else
+ {
+ (*decode_table)=end;
+ bit_buff->pos-= bit_buff->bits/8;
+ memcpy(*intervall_buff,bit_buff->pos,(size_t) intervall_length);
+ (*intervall_buff)+=intervall_length;
+ bit_buff->pos+=intervall_length;
+ bit_buff->bits=0;
+ }
+ return 0;
+}
+
+
+static void make_quick_table(uint16 *to_table, uint16 *decode_table,
+ uint *next_free_offset, uint value, uint bits,
+ uint max_bits)
+{
+ if (!bits--)
+ {
+ to_table[value]= (uint16) *next_free_offset;
+ *next_free_offset=copy_decode_table(to_table, *next_free_offset,
+ decode_table);
+ return;
+ }
+ if (!(*decode_table & IS_CHAR))
+ {
+ make_quick_table(to_table,decode_table+ *decode_table,
+ next_free_offset,value,bits,max_bits);
+ }
+ else
+ fill_quick_table(to_table+value,bits,max_bits,(uint) *decode_table);
+ decode_table++;
+ value|= (1 << bits);
+ if (!(*decode_table & IS_CHAR))
+ {
+ make_quick_table(to_table,decode_table+ *decode_table,
+ next_free_offset,value,bits,max_bits);
+ }
+ else
+ fill_quick_table(to_table+value,bits,max_bits,(uint) *decode_table);
+ return;
+}
+
+
+static void fill_quick_table(uint16 *table, uint bits, uint max_bits,
+ uint value)
+{
+ uint16 *end;
+ value|=(max_bits-bits) << 8;
+ for (end=table+ (1 << bits) ;
+ table < end ;
+ *table++ = (uint16) value | IS_CHAR) ;
+}
+
+
+static uint copy_decode_table(uint16 *to_pos, uint offset,
+ uint16 *decode_table)
+{
+ uint prev_offset;
+ prev_offset= offset;
+
+ if (!(*decode_table & IS_CHAR))
+ {
+ to_pos[offset]=2;
+ offset=copy_decode_table(to_pos,offset+2,decode_table+ *decode_table);
+ }
+ else
+ {
+ to_pos[offset]= *decode_table;
+ offset+=2;
+ }
+ decode_table++;
+
+ if (!(*decode_table & IS_CHAR))
+ {
+ to_pos[prev_offset+1]=(uint16) (offset-prev_offset-1);
+ offset=copy_decode_table(to_pos,offset,decode_table+ *decode_table);
+ }
+ else
+ to_pos[prev_offset+1]= *decode_table;
+ return offset;
+}
+
+
+static uint find_longest_bitstream(uint16 *table, uint16 *end)
+{
+ uint length=1,length2;
+ if (!(*table & IS_CHAR))
+ {
+ uint16 *next= table + *table;
+ if (next > end || next == table)
+ return ~0;
+ length=find_longest_bitstream(next, end)+1;
+ }
+ table++;
+ if (!(*table & IS_CHAR))
+ {
+ uint16 *next= table + *table;
+ if (next > end || next == table)
+ return ~0;
+ length2=find_longest_bitstream(table+ *table, end)+1;
+ length=max(length,length2);
+ }
+ return length;
+}
+
+
+ /* Read record from datafile */
+ /* Returns length of packed record, -1 if error */
+
+int _mi_read_pack_record(MI_INFO *info, my_off_t filepos, byte *buf)
+{
+ MI_BLOCK_INFO block_info;
+ File file;
+ DBUG_ENTER("mi_read_pack_record");
+
+ if (filepos == HA_OFFSET_ERROR)
+ DBUG_RETURN(-1); /* _search() didn't find record */
+
+ file=info->dfile;
+ if (_mi_pack_get_block_info(info, &block_info, file, filepos))
+ goto err;
+ if (my_read(file,(byte*) info->rec_buff + block_info.offset ,
+ block_info.rec_len - block_info.offset, MYF(MY_NABP)))
+ goto panic;
+ info->update|= HA_STATE_AKTIV;
+ DBUG_RETURN(_mi_pack_rec_unpack(info,buf,info->rec_buff,block_info.rec_len));
+panic:
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+err:
+ DBUG_RETURN(-1);
+}
+
+
+
+int _mi_pack_rec_unpack(register MI_INFO *info, register byte *to, byte *from,
+ ulong reclength)
+{
+ byte *end_field;
+ reg3 MI_COLUMNDEF *end;
+ MI_COLUMNDEF *current_field;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_pack_rec_unpack");
+
+ init_bit_buffer(&info->bit_buff, (uchar*) from,reclength);
+
+ for (current_field=share->rec, end=current_field+share->base.fields ;
+ current_field < end ;
+ current_field++,to=end_field)
+ {
+ end_field=to+current_field->length;
+ (*current_field->unpack)(current_field,&info->bit_buff,(uchar*) to,
+ (uchar*) end_field);
+ }
+ if (! info->bit_buff.error &&
+ info->bit_buff.pos - info->bit_buff.bits/8 == info->bit_buff.end)
+ DBUG_RETURN(0);
+ info->update&= ~HA_STATE_AKTIV;
+ DBUG_RETURN(my_errno=HA_ERR_WRONG_IN_RECORD);
+} /* _mi_pack_rec_unpack */
+
+
+ /* Return function to unpack field */
+
+static void (*get_unpack_function(MI_COLUMNDEF *rec))
+(MI_COLUMNDEF *, MI_BIT_BUFF *, uchar *, uchar *)
+{
+ switch (rec->base_type) {
+ case FIELD_SKIP_ZERO:
+ if (rec->pack_type & PACK_TYPE_ZERO_FILL)
+ return &uf_zerofill_skip_zero;
+ return &uf_skip_zero;
+ case FIELD_NORMAL:
+ if (rec->pack_type & PACK_TYPE_SPACE_FIELDS)
+ return &uf_space_normal;
+ if (rec->pack_type & PACK_TYPE_ZERO_FILL)
+ return &uf_zerofill_normal;
+ return &decode_bytes;
+ case FIELD_SKIP_ENDSPACE:
+ if (rec->pack_type & PACK_TYPE_SPACE_FIELDS)
+ {
+ if (rec->pack_type & PACK_TYPE_SELECTED)
+ return &uf_space_endspace_selected;
+ return &uf_space_endspace;
+ }
+ if (rec->pack_type & PACK_TYPE_SELECTED)
+ return &uf_endspace_selected;
+ return &uf_endspace;
+ case FIELD_SKIP_PRESPACE:
+ if (rec->pack_type & PACK_TYPE_SPACE_FIELDS)
+ {
+ if (rec->pack_type & PACK_TYPE_SELECTED)
+ return &uf_space_prespace_selected;
+ return &uf_space_prespace;
+ }
+ if (rec->pack_type & PACK_TYPE_SELECTED)
+ return &uf_prespace_selected;
+ return &uf_prespace;
+ case FIELD_CONSTANT:
+ return &uf_constant;
+ case FIELD_INTERVALL:
+ return &uf_intervall;
+ case FIELD_ZERO:
+ case FIELD_CHECK:
+ return &uf_zero;
+ case FIELD_BLOB:
+ return &uf_blob;
+ case FIELD_VARCHAR:
+ if (rec->length <= 256) /* 255 + 1 byte length */
+ return &uf_varchar1;
+ return &uf_varchar2;
+ case FIELD_LAST:
+ default:
+ return 0; /* This should never happend */
+ }
+}
+
+ /* The different functions to unpack a field */
+
+static void uf_zerofill_skip_zero(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end)
+{
+ if (get_bit(bit_buff))
+ bzero((char*) to,(uint) (end-to));
+ else
+ {
+ end-=rec->space_length_bits;
+ decode_bytes(rec,bit_buff,to,end);
+ bzero((char*) end,rec->space_length_bits);
+ }
+}
+
+static void uf_skip_zero(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ if (get_bit(bit_buff))
+ bzero((char*) to,(uint) (end-to));
+ else
+ decode_bytes(rec,bit_buff,to,end);
+}
+
+static void uf_space_normal(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ if (get_bit(bit_buff))
+ bfill((byte*) to,(end-to),' ');
+ else
+ decode_bytes(rec,bit_buff,to,end);
+}
+
+static void uf_space_endspace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end)
+{
+ uint spaces;
+ if (get_bit(bit_buff))
+ bfill((byte*) to,(end-to),' ');
+ else
+ {
+ if (get_bit(bit_buff))
+ {
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to,end-spaces);
+ bfill((byte*) end-spaces,spaces,' ');
+ }
+ else
+ decode_bytes(rec,bit_buff,to,end);
+ }
+}
+
+static void uf_endspace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end)
+{
+ uint spaces;
+ if (get_bit(bit_buff))
+ {
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to,end-spaces);
+ bfill((byte*) end-spaces,spaces,' ');
+ }
+ else
+ decode_bytes(rec,bit_buff,to,end);
+}
+
+static void uf_space_endspace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ uint spaces;
+ if (get_bit(bit_buff))
+ bfill((byte*) to,(end-to),' ');
+ else
+ {
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to,end-spaces);
+ bfill((byte*) end-spaces,spaces,' ');
+ }
+}
+
+static void uf_endspace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ uint spaces;
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to,end-spaces);
+ bfill((byte*) end-spaces,spaces,' ');
+}
+
+static void uf_space_prespace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end)
+{
+ uint spaces;
+ if (get_bit(bit_buff))
+ bfill((byte*) to,(end-to),' ');
+ else
+ {
+ if (get_bit(bit_buff))
+ {
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ bfill((byte*) to,spaces,' ');
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to+spaces,end);
+ }
+ else
+ decode_bytes(rec,bit_buff,to,end);
+ }
+}
+
+
+static void uf_prespace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end)
+{
+ uint spaces;
+ if (get_bit(bit_buff))
+ {
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ bfill((byte*) to,spaces,' ');
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to+spaces,end);
+ }
+ else
+ decode_bytes(rec,bit_buff,to,end);
+}
+
+
+static void uf_space_prespace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ uint spaces;
+ if (get_bit(bit_buff))
+ bfill((byte*) to,(end-to),' ');
+ else
+ {
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ bfill((byte*) to,spaces,' ');
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to+spaces,end);
+ }
+}
+
+static void uf_prespace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ uint spaces;
+ if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
+ {
+ bit_buff->error=1;
+ return;
+ }
+ bfill((byte*) to,spaces,' ');
+ if (to+spaces != end)
+ decode_bytes(rec,bit_buff,to+spaces,end);
+}
+
+static void uf_zerofill_normal(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ end-=rec->space_length_bits;
+ decode_bytes(rec,bit_buff,(uchar*) to,end);
+ bzero((char*) end,rec->space_length_bits);
+}
+
+static void uf_constant(MI_COLUMNDEF *rec,
+ MI_BIT_BUFF *bit_buff __attribute__((unused)),
+ uchar *to,
+ uchar *end)
+{
+ memcpy(to,rec->huff_tree->intervalls,(size_t) (end-to));
+}
+
+static void uf_intervall(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ reg1 uint field_length=(uint) (end-to);
+ memcpy(to,rec->huff_tree->intervalls+field_length*decode_pos(bit_buff,
+ rec->huff_tree),
+ (size_t) field_length);
+}
+
+
+/*ARGSUSED*/
+static void uf_zero(MI_COLUMNDEF *rec __attribute__((unused)),
+ MI_BIT_BUFF *bit_buff __attribute__((unused)),
+ uchar *to, uchar *end)
+{
+ bzero((char*) to,(uint) (end-to));
+}
+
+static void uf_blob(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end)
+{
+ if (get_bit(bit_buff))
+ bzero((byte*) to,(end-to));
+ else
+ {
+ ulong length=get_bits(bit_buff,rec->space_length_bits);
+ uint pack_length=(uint) (end-to)-mi_portable_sizeof_char_ptr;
+ if (bit_buff->blob_pos+length > bit_buff->blob_end)
+ {
+ bit_buff->error=1;
+ bzero((byte*) to,(end-to));
+ return;
+ }
+ decode_bytes(rec,bit_buff,bit_buff->blob_pos,bit_buff->blob_pos+length);
+ _my_store_blob_length((byte*) to,pack_length,length);
+ memcpy_fixed((char*) to+pack_length,(char*) &bit_buff->blob_pos,
+ sizeof(char*));
+ bit_buff->blob_pos+=length;
+ }
+}
+
+
+static void uf_varchar1(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end __attribute__((unused)))
+{
+ if (get_bit(bit_buff))
+ to[0]= 0; /* Zero lengths */
+ else
+ {
+ ulong length=get_bits(bit_buff,rec->space_length_bits);
+ *to= (uchar) length;
+ decode_bytes(rec,bit_buff,to+1,to+1+length);
+ }
+}
+
+
+static void uf_varchar2(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
+ uchar *to, uchar *end __attribute__((unused)))
+{
+ if (get_bit(bit_buff))
+ to[0]=to[1]=0; /* Zero lengths */
+ else
+ {
+ ulong length=get_bits(bit_buff,rec->space_length_bits);
+ int2store(to,length);
+ decode_bytes(rec,bit_buff,to+2,to+2+length);
+ }
+}
+
+ /* Functions to decode of buffer of bits */
+
+#if BITS_SAVED == 64
+
+static void decode_bytes(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,uchar *to,
+ uchar *end)
+{
+ reg1 uint bits,low_byte;
+ reg3 uint16 *pos;
+ reg4 uint table_bits,table_and;
+ MI_DECODE_TREE *decode_tree;
+
+ decode_tree=rec->decode_tree;
+ bits=bit_buff->bits; /* Save in reg for quicker access */
+ table_bits=decode_tree->quick_table_bits;
+ table_and= (1 << table_bits)-1;
+
+ do
+ {
+ if (bits <= 32)
+ {
+ if (bit_buff->pos > bit_buff->end+4)
+ {
+ bit_buff->error=1;
+ return; /* Can't be right */
+ }
+ bit_buff->current_byte= (bit_buff->current_byte << 32) +
+ ((((uint) bit_buff->pos[3])) +
+ (((uint) bit_buff->pos[2]) << 8) +
+ (((uint) bit_buff->pos[1]) << 16) +
+ (((uint) bit_buff->pos[0]) << 24));
+ bit_buff->pos+=4;
+ bits+=32;
+ }
+ /* First use info in quick_table */
+ low_byte=(uint) (bit_buff->current_byte >> (bits - table_bits)) & table_and;
+ low_byte=decode_tree->table[low_byte];
+ if (low_byte & IS_CHAR)
+ {
+ *to++ = (low_byte & 255); /* Found char in quick table */
+ bits-= ((low_byte >> 8) & 31); /* Remove bits used */
+ }
+ else
+ { /* Map through rest of decode-table */
+ pos=decode_tree->table+low_byte;
+ bits-=table_bits;
+ for (;;)
+ {
+ low_byte=(uint) (bit_buff->current_byte >> (bits-8));
+ decode_bytes_test_bit(0);
+ decode_bytes_test_bit(1);
+ decode_bytes_test_bit(2);
+ decode_bytes_test_bit(3);
+ decode_bytes_test_bit(4);
+ decode_bytes_test_bit(5);
+ decode_bytes_test_bit(6);
+ decode_bytes_test_bit(7);
+ bits-=8;
+ }
+ *to++ = *pos;
+ }
+ } while (to != end);
+
+ bit_buff->bits=bits;
+ return;
+}
+
+#else
+
+static void decode_bytes(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
+ uchar *end)
+{
+ reg1 uint bits,low_byte;
+ reg3 uint16 *pos;
+ reg4 uint table_bits,table_and;
+ MI_DECODE_TREE *decode_tree;
+
+ decode_tree=rec->huff_tree;
+ bits=bit_buff->bits; /* Save in reg for quicker access */
+ table_bits=decode_tree->quick_table_bits;
+ table_and= (1 << table_bits)-1;
+
+ do
+ {
+ if (bits < table_bits)
+ {
+ if (bit_buff->pos > bit_buff->end+1)
+ {
+ bit_buff->error=1;
+ return; /* Can't be right */
+ }
+#if BITS_SAVED == 32
+ bit_buff->current_byte= (bit_buff->current_byte << 24) +
+ (((uint) ((uchar) bit_buff->pos[2]))) +
+ (((uint) ((uchar) bit_buff->pos[1])) << 8) +
+ (((uint) ((uchar) bit_buff->pos[0])) << 16);
+ bit_buff->pos+=3;
+ bits+=24;
+#else
+ if (bits) /* We must have at leasts 9 bits */
+ {
+ bit_buff->current_byte= (bit_buff->current_byte << 8) +
+ (uint) ((uchar) bit_buff->pos[0]);
+ bit_buff->pos++;
+ bits+=8;
+ }
+ else
+ {
+ bit_buff->current_byte= ((uint) ((uchar) bit_buff->pos[0]) << 8) +
+ ((uint) ((uchar) bit_buff->pos[1]));
+ bit_buff->pos+=2;
+ bits+=16;
+ }
+#endif
+ }
+ /* First use info in quick_table */
+ low_byte=(bit_buff->current_byte >> (bits - table_bits)) & table_and;
+ low_byte=decode_tree->table[low_byte];
+ if (low_byte & IS_CHAR)
+ {
+ *to++ = (low_byte & 255); /* Found char in quick table */
+ bits-= ((low_byte >> 8) & 31); /* Remove bits used */
+ }
+ else
+ { /* Map through rest of decode-table */
+ pos=decode_tree->table+low_byte;
+ bits-=table_bits;
+ for (;;)
+ {
+ if (bits < 8)
+ { /* We don't need to check end */
+#if BITS_SAVED == 32
+ bit_buff->current_byte= (bit_buff->current_byte << 24) +
+ (((uint) ((uchar) bit_buff->pos[2]))) +
+ (((uint) ((uchar) bit_buff->pos[1])) << 8) +
+ (((uint) ((uchar) bit_buff->pos[0])) << 16);
+ bit_buff->pos+=3;
+ bits+=24;
+#else
+ bit_buff->current_byte= (bit_buff->current_byte << 8) +
+ (uint) ((uchar) bit_buff->pos[0]);
+ bit_buff->pos+=1;
+ bits+=8;
+#endif
+ }
+ low_byte=(uint) (bit_buff->current_byte >> (bits-8));
+ decode_bytes_test_bit(0);
+ decode_bytes_test_bit(1);
+ decode_bytes_test_bit(2);
+ decode_bytes_test_bit(3);
+ decode_bytes_test_bit(4);
+ decode_bytes_test_bit(5);
+ decode_bytes_test_bit(6);
+ decode_bytes_test_bit(7);
+ bits-=8;
+ }
+ *to++ = (uchar) *pos;
+ }
+ } while (to != end);
+
+ bit_buff->bits=bits;
+ return;
+}
+#endif /* BIT_SAVED == 64 */
+
+
+static uint decode_pos(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree)
+{
+ uint16 *pos=decode_tree->table;
+ for (;;)
+ {
+ if (get_bit(bit_buff))
+ pos++;
+ if (*pos & IS_CHAR)
+ return (uint) (*pos & ~IS_CHAR);
+ pos+= *pos;
+ }
+}
+
+
+int _mi_read_rnd_pack_record(MI_INFO *info, byte *buf,
+ register my_off_t filepos,
+ my_bool skip_deleted_blocks)
+{
+ uint b_type;
+ MI_BLOCK_INFO block_info;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_read_rnd_pack_record");
+
+ if (filepos >= info->state->data_file_length)
+ {
+ my_errno= HA_ERR_END_OF_FILE;
+ goto err;
+ }
+
+ if (info->opt_flag & READ_CACHE_USED)
+ {
+ if (_mi_read_cache(&info->rec_cache,(byte*) block_info.header,filepos,
+ share->pack.ref_length, skip_deleted_blocks))
+ goto err;
+ b_type=_mi_pack_get_block_info(info,&block_info,-1, filepos);
+ }
+ else
+ b_type=_mi_pack_get_block_info(info,&block_info,info->dfile,filepos);
+ if (b_type)
+ goto err; /* Error code is already set */
+#ifndef DBUG_OFF
+ if (block_info.rec_len > share->max_pack_length)
+ {
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ goto err;
+ }
+#endif
+
+ if (info->opt_flag & READ_CACHE_USED)
+ {
+ if (_mi_read_cache(&info->rec_cache,(byte*) info->rec_buff,
+ block_info.filepos, block_info.rec_len,
+ skip_deleted_blocks))
+ goto err;
+ }
+ else
+ {
+ if (my_read(info->dfile,(byte*) info->rec_buff + block_info.offset,
+ block_info.rec_len-block_info.offset,
+ MYF(MY_NABP)))
+ goto err;
+ }
+ info->packed_length=block_info.rec_len;
+ info->lastpos=filepos;
+ info->nextpos=block_info.filepos+block_info.rec_len;
+ info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
+
+ DBUG_RETURN (_mi_pack_rec_unpack(info,buf,info->rec_buff,
+ block_info.rec_len));
+ err:
+ DBUG_RETURN(my_errno);
+}
+
+
+ /* Read and process header from a huff-record-file */
+
+uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BLOCK_INFO *info, File file,
+ my_off_t filepos)
+{
+ uchar *header=info->header;
+ uint head_length,ref_length;
+ LINT_INIT(ref_length);
+
+ if (file >= 0)
+ {
+ ref_length=myisam->s->pack.ref_length;
+ /*
+ We can't use my_pread() here because mi_read_rnd_pack_record assumes
+ position is ok
+ */
+ VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
+ if (my_read(file,(char*) header,ref_length,MYF(MY_NABP)))
+ return BLOCK_FATAL_ERROR;
+ DBUG_DUMP("header",(byte*) header,ref_length);
+ }
+ if (header[0] < 254)
+ {
+ info->rec_len=header[0];
+ head_length=1;
+ }
+ else if (header[0] == 254)
+ {
+ info->rec_len=uint2korr(header+1);
+ head_length=3;
+ }
+ else
+ {
+ info->rec_len=uint3korr(header+1);
+ head_length=4;
+ }
+ if (myisam->s->base.blobs)
+ {
+ if (header[head_length] < 254)
+ {
+ info->blob_len=header[head_length];
+ head_length++;
+ }
+ else if (header[head_length] == 254)
+ {
+ info->blob_len=uint2korr(header+head_length+1);
+ head_length+=3;
+ }
+ else
+ {
+ info->blob_len=uint3korr(header+head_length+1);
+ head_length+=4;
+ }
+ if (!(mi_alloc_rec_buff(myisam,info->rec_len + info->blob_len,
+ &myisam->rec_buff)))
+ return BLOCK_FATAL_ERROR; /* not enough memory */
+ myisam->bit_buff.blob_pos=(uchar*) myisam->rec_buff+info->rec_len;
+ myisam->bit_buff.blob_end= myisam->bit_buff.blob_pos+info->blob_len;
+ myisam->blob_length=info->blob_len;
+ }
+ info->filepos=filepos+head_length;
+ if (file > 0)
+ {
+ info->offset=min(info->rec_len, ref_length - head_length);
+ memcpy(myisam->rec_buff, header+head_length, info->offset);
+ }
+ return 0;
+}
+
+
+ /* rutines for bit buffer */
+ /* Note buffer must be 6 byte bigger than longest row */
+
+static void init_bit_buffer(MI_BIT_BUFF *bit_buff, uchar *buffer, uint length)
+{
+ bit_buff->pos=buffer;
+ bit_buff->end=buffer+length;
+ bit_buff->bits=bit_buff->error=0;
+ bit_buff->current_byte=0; /* Avoid purify errors */
+}
+
+static uint fill_and_get_bits(MI_BIT_BUFF *bit_buff, uint count)
+{
+ uint tmp;
+ count-=bit_buff->bits;
+ tmp=(bit_buff->current_byte & mask[bit_buff->bits]) << count;
+ fill_buffer(bit_buff);
+ bit_buff->bits=BITS_SAVED - count;
+ return tmp+(bit_buff->current_byte >> (BITS_SAVED - count));
+}
+
+ /* Fill in empty bit_buff->current_byte from buffer */
+ /* Sets bit_buff->error if buffer is exhausted */
+
+static void fill_buffer(MI_BIT_BUFF *bit_buff)
+{
+ if (bit_buff->pos >= bit_buff->end)
+ {
+ bit_buff->error= 1;
+ bit_buff->current_byte=0;
+ return;
+ }
+#if BITS_SAVED == 64
+ bit_buff->current_byte= ((((uint) ((uchar) bit_buff->pos[7]))) +
+ (((uint) ((uchar) bit_buff->pos[6])) << 8) +
+ (((uint) ((uchar) bit_buff->pos[5])) << 16) +
+ (((uint) ((uchar) bit_buff->pos[4])) << 24) +
+ ((ulonglong)
+ ((((uint) ((uchar) bit_buff->pos[3]))) +
+ (((uint) ((uchar) bit_buff->pos[2])) << 8) +
+ (((uint) ((uchar) bit_buff->pos[1])) << 16) +
+ (((uint) ((uchar) bit_buff->pos[0])) << 24)) << 32));
+ bit_buff->pos+=8;
+#else
+#if BITS_SAVED == 32
+ bit_buff->current_byte= (((uint) ((uchar) bit_buff->pos[3])) +
+ (((uint) ((uchar) bit_buff->pos[2])) << 8) +
+ (((uint) ((uchar) bit_buff->pos[1])) << 16) +
+ (((uint) ((uchar) bit_buff->pos[0])) << 24));
+ bit_buff->pos+=4;
+#else
+ bit_buff->current_byte= (uint) (((uint) ((uchar) bit_buff->pos[1]))+
+ (((uint) ((uchar) bit_buff->pos[0])) << 8));
+ bit_buff->pos+=2;
+#endif
+#endif
+}
+
+ /* Get number of bits neaded to represent value */
+
+static uint max_bit(register uint value)
+{
+ reg2 uint power=1;
+
+ while ((value>>=1))
+ power++;
+ return (power);
+}
+
+
+/*****************************************************************************
+ Some redefined functions to handle files when we are using memmap
+*****************************************************************************/
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef HAVE_MMAP
+
+static int _mi_read_mempack_record(MI_INFO *info,my_off_t filepos,byte *buf);
+static int _mi_read_rnd_mempack_record(MI_INFO*, byte *,my_off_t, my_bool);
+
+#ifndef MAP_NORESERVE
+#define MAP_NORESERVE 0 /* For irix */
+#endif
+#ifndef MAP_FAILED
+#define MAP_FAILED -1
+#endif
+
+my_bool _mi_memmap_file(MI_INFO *info)
+{
+ byte *file_map;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("mi_memmap_file");
+
+ if (!info->s->file_map)
+ {
+ if (my_seek(info->dfile,0L,MY_SEEK_END,MYF(0)) <
+ share->state.state.data_file_length+MEMMAP_EXTRA_MARGIN)
+ {
+ DBUG_PRINT("warning",("File isn't extended for memmap"));
+ DBUG_RETURN(0);
+ }
+ file_map=(byte*)
+ my_mmap(0,(size_t)(share->state.state.data_file_length+MEMMAP_EXTRA_MARGIN),PROT_READ,
+ MAP_SHARED | MAP_NORESERVE,info->dfile,0L);
+ if (file_map == (byte*) MAP_FAILED)
+ {
+ DBUG_PRINT("warning",("mmap failed: errno: %d",errno));
+ my_errno=errno;
+ DBUG_RETURN(0);
+ }
+ info->s->file_map=file_map;
+ }
+ info->opt_flag|= MEMMAP_USED;
+ info->read_record=share->read_record=_mi_read_mempack_record;
+ share->read_rnd=_mi_read_rnd_mempack_record;
+ DBUG_RETURN(1);
+}
+
+
+void _mi_unmap_file(MI_INFO *info)
+{
+ VOID(my_munmap(info->s->file_map,
+ (size_t) info->s->state.state.data_file_length+
+ MEMMAP_EXTRA_MARGIN));
+}
+
+
+static uchar *_mi_mempack_get_block_info(MI_INFO *myisam,MI_BLOCK_INFO *info,
+ uchar *header)
+{
+ if (header[0] < 254)
+ info->rec_len= *header++;
+ else if (header[0] == 254)
+ {
+ info->rec_len=uint2korr(header+1);
+ header+=3;
+ }
+ else
+ {
+ info->rec_len=uint3korr(header+1);
+ header+=4;
+ }
+ if (myisam->s->base.blobs)
+ {
+ if (header[0] < 254)
+ {
+ info->blob_len= *header++;
+ }
+ else if (header[0] == 254)
+ {
+ info->blob_len=uint2korr(header+1);
+ header+=3;
+ }
+ else
+ {
+ info->blob_len=uint3korr(header+1);
+ header+=4;
+ }
+ /* mi_alloc_rec_buff sets my_errno on error */
+ if (!(mi_alloc_rec_buff(myisam, info->blob_len,
+ &myisam->rec_buff)))
+ return 0; /* not enough memory */
+ myisam->bit_buff.blob_pos=(uchar*) myisam->rec_buff;
+ myisam->bit_buff.blob_end= (uchar*) myisam->rec_buff + info->blob_len;
+ }
+ return header;
+}
+
+
+static int _mi_read_mempack_record(MI_INFO *info, my_off_t filepos, byte *buf)
+{
+ MI_BLOCK_INFO block_info;
+ MYISAM_SHARE *share=info->s;
+ byte *pos;
+ DBUG_ENTER("mi_read_mempack_record");
+
+ if (filepos == HA_OFFSET_ERROR)
+ DBUG_RETURN(-1); /* _search() didn't find record */
+
+ if (!(pos= (byte*) _mi_mempack_get_block_info(info,&block_info,
+ (uchar*) share->file_map+
+ filepos)))
+ DBUG_RETURN(-1);
+ DBUG_RETURN(_mi_pack_rec_unpack(info, buf, pos, block_info.rec_len));
+}
+
+
+/*ARGSUSED*/
+static int _mi_read_rnd_mempack_record(MI_INFO *info, byte *buf,
+ register my_off_t filepos,
+ my_bool skip_deleted_blocks
+ __attribute__((unused)))
+{
+ MI_BLOCK_INFO block_info;
+ MYISAM_SHARE *share=info->s;
+ byte *pos,*start;
+ DBUG_ENTER("_mi_read_rnd_mempack_record");
+
+ if (filepos >= share->state.state.data_file_length)
+ {
+ my_errno=HA_ERR_END_OF_FILE;
+ goto err;
+ }
+ if (!(pos= (byte*) _mi_mempack_get_block_info(info,&block_info,
+ (uchar*)
+ (start=share->file_map+
+ filepos))))
+ goto err;
+#ifndef DBUG_OFF
+ if (block_info.rec_len > info->s->max_pack_length)
+ {
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ goto err;
+ }
+#endif
+ info->packed_length=block_info.rec_len;
+ info->lastpos=filepos;
+ info->nextpos=filepos+(uint) (pos-start)+block_info.rec_len;
+ info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
+
+ DBUG_RETURN (_mi_pack_rec_unpack(info,buf,pos, block_info.rec_len));
+ err:
+ DBUG_RETURN(my_errno);
+}
+
+#endif /* HAVE_MMAP */
+
+ /* Save length of row */
+
+uint save_pack_length(byte *block_buff,ulong length)
+{
+ if (length < 254)
+ {
+ *(uchar*) block_buff= (uchar) length;
+ return 1;
+ }
+ if (length <= 65535)
+ {
+ *(uchar*) block_buff=254;
+ int2store(block_buff+1,(uint) length);
+ return 3;
+ }
+ *(uchar*) block_buff=255;
+ int3store(block_buff+1,(ulong) length);
+ return 4;
+}
diff --git a/storage/myisam/mi_page.c b/storage/myisam/mi_page.c
new file mode 100644
index 00000000000..5240c063fba
--- /dev/null
+++ b/storage/myisam/mi_page.c
@@ -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 */
+
+/* Read and write key blocks */
+
+#include "myisamdef.h"
+
+ /* Fetch a key-page in memory */
+
+uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo,
+ my_off_t page, int level,
+ uchar *buff, int return_buffer)
+{
+ uchar *tmp;
+ uint page_size;
+ DBUG_ENTER("_mi_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;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_RETURN(0);
+ }
+ info->last_keypage=page;
+ page_size=mi_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;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno = HA_ERR_CRASHED;
+ tmp = 0;
+ }
+ DBUG_RETURN(tmp);
+} /* _mi_fetch_keypage */
+
+
+ /* Write a key-page on disk */
+
+int _mi_write_keypage(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ my_off_t page, int level, uchar *buff)
+{
+ reg3 uint length;
+ DBUG_ENTER("_mi_write_keypage");
+
+#ifndef FAST /* Safety check */
+ if (page < info->s->base.keystart ||
+ page+keyinfo->block_length > info->state->key_file_length ||
+ (page & (MI_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,mi_getint(buff));
+#endif
+
+ if ((length=keyinfo->block_length) > IO_SIZE*2 &&
+ info->state->key_file_length != page+length)
+ length= ((mi_getint(buff)+IO_SIZE-1) & (uint) ~(IO_SIZE-1));
+#ifdef HAVE_purify
+ {
+ length=mi_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))));
+} /* mi_write_keypage */
+
+
+ /* Remove page from disk */
+
+int _mi_dispose(register MI_INFO *info, MI_KEYDEF *keyinfo, my_off_t pos,
+ int level)
+{
+ my_off_t old_link;
+ char buff[8];
+ DBUG_ENTER("_mi_dispose");
+ DBUG_PRINT("enter",("pos: %ld", (long) pos));
+
+ old_link=info->s->state.key_del[keyinfo->block_size];
+ info->s->state.key_del[keyinfo->block_size]=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)));
+} /* _mi_dispose */
+
+
+ /* Make new page on disk */
+
+my_off_t _mi_new(register MI_INFO *info, MI_KEYDEF *keyinfo, int level)
+{
+ my_off_t pos;
+ char buff[8];
+ DBUG_ENTER("_mi_new");
+
+ if ((pos=info->s->state.key_del[keyinfo->block_size]) == 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]=mi_sizekorr(buff);
+ }
+ info->s->state.changed|= STATE_NOT_SORTED_PAGES;
+ DBUG_PRINT("exit",("Pos: %ld",(long) pos));
+ DBUG_RETURN(pos);
+} /* _mi_new */
diff --git a/storage/myisam/mi_panic.c b/storage/myisam/mi_panic.c
new file mode 100644
index 00000000000..78698d88c54
--- /dev/null
+++ b/storage/myisam/mi_panic.c
@@ -0,0 +1,114 @@
+/* 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 */
+
+#include "fulltext.h"
+
+ /* if flag == HA_PANIC_CLOSE then all misam files are closed */
+ /* if flag == HA_PANIC_WRITE then all misam files are unlocked and
+ all changed data in single user misam is written to file */
+ /* if flag == HA_PANIC_READ then all misam files that was locked when
+ mi_panic(HA_PANIC_WRITE) was done is locked. A mi_readinfo() is
+ done for all single user files to get changes in database */
+
+
+int mi_panic(enum ha_panic_function flag)
+{
+ int error=0;
+ LIST *list_element,*next_open;
+ MI_INFO *info;
+ DBUG_ENTER("mi_panic");
+
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ for (list_element=myisam_open_list ; list_element ; list_element=next_open)
+ {
+ next_open=list_element->next; /* Save if close */
+ info=(MI_INFO*) list_element->data;
+ switch (flag) {
+ case HA_PANIC_CLOSE:
+ pthread_mutex_unlock(&THR_LOCK_myisam); /* Not exactly right... */
+ if (mi_close(info))
+ error=my_errno;
+ pthread_mutex_lock(&THR_LOCK_myisam);
+ 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 (mi_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 (mi_lock_database(info, info->was_locked))
+ error=my_errno;
+ info->was_locked=0;
+ }
+ break;
+ }
+ }
+ if (flag == HA_PANIC_CLOSE)
+ {
+ VOID(mi_log(0)); /* Close log if neaded */
+ ft_free_stopwords();
+ }
+ pthread_mutex_unlock(&THR_LOCK_myisam);
+ if (!error)
+ DBUG_RETURN(0);
+ DBUG_RETURN(my_errno=error);
+} /* mi_panic */
diff --git a/storage/myisam/mi_preload.c b/storage/myisam/mi_preload.c
new file mode 100644
index 00000000000..317ab4ad7fe
--- /dev/null
+++ b/storage/myisam/mi_preload.c
@@ -0,0 +1,118 @@
+/* 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 */
+
+/*
+ Preload indexes into key cache
+*/
+
+#include "myisamdef.h"
+
+
+/*
+ Preload pages of the index file for a table into the key cache
+
+ SYNOPSIS
+ mi_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 mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves)
+{
+ uint i;
+ ulong length, block_length= 0;
+ uchar *buff= NULL;
+ MYISAM_SHARE* share= info->s;
+ uint keys= share->state.header.keys;
+ MI_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("mi_preload");
+
+ if (!keys || !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 (mi_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);
+}
+
diff --git a/storage/myisam/mi_range.c b/storage/myisam/mi_range.c
new file mode 100644
index 00000000000..e78f3b11625
--- /dev/null
+++ b/storage/myisam/mi_range.c
@@ -0,0 +1,244 @@
+/* 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 */
+
+/*
+ Gives a approximated number of how many records there is between two keys.
+ Used when optimizing querries.
+ */
+
+#include "myisamdef.h"
+#include "rt_index.h"
+
+static ha_rows _mi_record_pos(MI_INFO *info,const byte *key,uint key_len,
+ enum ha_rkey_function search_flag);
+static double _mi_search_pos(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
+ uint key_len,uint nextflag,my_off_t pos);
+static uint _mi_keynr(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *page,
+ uchar *keypos,uint *ret_max_key);
+
+
+/*
+ Estimate how many records there is in a given range
+
+ SYNOPSIS
+ mi_records_in_range()
+ info MyISAM 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 mi_records_in_range(MI_INFO *info, int inx, key_range *min_key,
+ key_range *max_key)
+{
+ ha_rows start_pos,end_pos,res;
+ DBUG_ENTER("mi_records_in_range");
+
+ if ((inx = _mi_check_index(info,inx)) < 0)
+ DBUG_RETURN(HA_POS_ERROR);
+
+ if (fast_mi_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= _mi_pack_key(info,inx, key_buff,
+ (uchar*) min_key->key, min_key->length,
+ (HA_KEYSEG**) 0);
+ res= rtree_estimate(info, inx, key_buff, start_key_len,
+ myisam_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 ?
+ _mi_record_pos(info, min_key->key, min_key->length,
+ min_key->flag) :
+ (ha_rows) 0);
+ end_pos= (max_key ?
+ _mi_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_mi_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 _mi_record_pos(MI_INFO *info, const byte *key, uint key_len,
+ enum ha_rkey_function search_flag)
+{
+ uint inx=(uint) info->lastinx, nextflag;
+ MI_KEYDEF *keyinfo=info->s->keyinfo+inx;
+ uchar *key_buff;
+ double pos;
+
+ DBUG_ENTER("_mi_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=_mi_pack_key(info,inx,key_buff,(uchar*) key,key_len,
+ (HA_KEYSEG**) 0);
+ DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE,keyinfo->seg,
+ (uchar*) key_buff,key_len););
+ nextflag=myisam_read_vec[search_flag];
+ if (!(nextflag & (SEARCH_FIND | SEARCH_NO_FIND | SEARCH_LAST)))
+ key_len=USE_WHOLE_KEY;
+
+ pos=_mi_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 _mi_search */
+ /* Returns offset for key in indextable (decimal 0.0 <= x <= 1.0) */
+
+static double _mi_search_pos(register MI_INFO *info,
+ register MI_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("_mi_search_pos");
+
+ if (pos == HA_OFFSET_ERROR)
+ DBUG_RETURN(0.5);
+
+ if (!(buff=_mi_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=mi_test_if_nod(buff);
+ keynr=_mi_keynr(info,keyinfo,buff,keypos,&max_keynr);
+
+ if (flag)
+ {
+ if (flag == MI_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=_mi_search_pos(info,keyinfo,key,key_len,nextflag,
+ _mi_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=_mi_search_pos(info,keyinfo,key,key_len,SEARCH_FIND,
+ _mi_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 _mi_keynr(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page,
+ uchar *keypos, uint *ret_max_key)
+{
+ uint nod_flag,keynr,max_key;
+ uchar t_buff[MI_MAX_KEY_BUFF],*end;
+
+ end= page+mi_getint(page);
+ nod_flag=mi_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);
+}
diff --git a/storage/myisam/mi_rename.c b/storage/myisam/mi_rename.c
new file mode 100644
index 00000000000..8380ee1bfad
--- /dev/null
+++ b/storage/myisam/mi_rename.c
@@ -0,0 +1,61 @@
+/* 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 */
+
+/*
+ Rename a table
+*/
+
+#include "fulltext.h"
+
+int mi_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("mi_rename");
+
+#ifdef EXTRA_DEBUG
+ check_table_is_closed(old_name,"rename old_table");
+ check_table_is_closed(new_name,"rename new table2");
+#endif
+#ifdef USE_RAID
+ {
+ MI_INFO *info;
+ if (!(info=mi_open(old_name, O_RDONLY, 0)))
+ DBUG_RETURN(my_errno);
+ raid_type = info->s->base.raid_type;
+ raid_chunks = info->s->base.raid_chunks;
+ mi_close(info);
+ }
+#ifdef EXTRA_DEBUG
+ check_table_is_closed(old_name,"rename raidcheck");
+#endif
+#endif /* USE_RAID */
+
+ fn_format(from,old_name,"",MI_NAME_IEXT,4);
+ fn_format(to,new_name,"",MI_NAME_IEXT,4);
+ if (my_rename_with_symlink(from, to, MYF(MY_WME)))
+ DBUG_RETURN(my_errno);
+ fn_format(from,old_name,"",MI_NAME_DEXT,4);
+ fn_format(to,new_name,"",MI_NAME_DEXT,4);
+#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);
+}
diff --git a/storage/myisam/mi_rfirst.c b/storage/myisam/mi_rfirst.c
new file mode 100644
index 00000000000..e30f61801a0
--- /dev/null
+++ b/storage/myisam/mi_rfirst.c
@@ -0,0 +1,27 @@
+/* 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 */
+
+#include "myisamdef.h"
+
+ /* Read first row through a specfic key */
+
+int mi_rfirst(MI_INFO *info, byte *buf, int inx)
+{
+ DBUG_ENTER("mi_rfirst");
+ info->lastpos= HA_OFFSET_ERROR;
+ info->update|= HA_STATE_PREV_FOUND;
+ DBUG_RETURN(mi_rnext(info,buf,inx));
+} /* mi_rfirst */
diff --git a/storage/myisam/mi_rkey.c b/storage/myisam/mi_rkey.c
new file mode 100644
index 00000000000..635a7eb2c48
--- /dev/null
+++ b/storage/myisam/mi_rkey.c
@@ -0,0 +1,141 @@
+/* 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 */
+
+/* Read record based on a key */
+
+#include "myisamdef.h"
+#include "rt_index.h"
+
+ /* Read a record using key */
+ /* Ordinary search_flag is 0 ; Give error if no record with key */
+
+int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len,
+ enum ha_rkey_function search_flag)
+{
+ uchar *key_buff;
+ MYISAM_SHARE *share=info->s;
+ MI_KEYDEF *keyinfo;
+ HA_KEYSEG *last_used_keyseg;
+ uint pack_key_length, use_key_length, nextflag;
+ DBUG_ENTER("mi_rkey");
+ DBUG_PRINT("enter",("base: %lx inx: %d search_flag: %d",
+ info,inx,search_flag));
+
+ if ((inx = _mi_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;
+ key_buff=info->lastkey+info->s->base.max_key_length;
+ pack_key_length=_mi_pack_key(info,(uint) inx, key_buff, (uchar*) key,
+ key_len, &last_used_keyseg);
+ DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE, keyinfo->seg,
+ key_buff, pack_key_length););
+ }
+
+ if (fast_mi_readinfo(info))
+ goto err;
+ if (share->concurrent_insert)
+ rw_rdlock(&share->key_root_lock[inx]);
+
+ nextflag=myisam_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 (rtree_find_first(info,inx,key_buff,use_key_length,nextflag) < 0)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ goto err;
+ }
+ break;
+#endif
+ case HA_KEY_ALG_BTREE:
+ default:
+ if (!_mi_search(info, keyinfo, key_buff, use_key_length,
+ myisam_read_vec[search_flag], info->s->state.key_root[inx]))
+ {
+ 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 (_mi_search_next(info, keyinfo, info->lastkey,
+ info->lastkey_length,
+ myisam_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 mi_rnext_same */
+ if ((keyinfo->flag & HA_VAR_LENGTH_KEY) && last_used_keyseg &&
+ info->lastpos != HA_OFFSET_ERROR)
+ info->last_rkey_length= _mi_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);
+} /* _mi_rkey */
diff --git a/storage/myisam/mi_rlast.c b/storage/myisam/mi_rlast.c
new file mode 100644
index 00000000000..61c3ff58fd5
--- /dev/null
+++ b/storage/myisam/mi_rlast.c
@@ -0,0 +1,27 @@
+/* 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 */
+
+#include "myisamdef.h"
+
+ /* Read last row with the same key as the previous read. */
+
+int mi_rlast(MI_INFO *info, byte *buf, int inx)
+{
+ DBUG_ENTER("mi_rlast");
+ info->lastpos= HA_OFFSET_ERROR;
+ info->update|= HA_STATE_NEXT_FOUND;
+ DBUG_RETURN(mi_rprev(info,buf,inx));
+} /* mi_rlast */
diff --git a/storage/myisam/mi_rnext.c b/storage/myisam/mi_rnext.c
new file mode 100644
index 00000000000..69bf5c8deae
--- /dev/null
+++ b/storage/myisam/mi_rnext.c
@@ -0,0 +1,122 @@
+/* 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 */
+
+#include "myisamdef.h"
+
+#include "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 mi_rnext(MI_INFO *info, byte *buf, int inx)
+{
+ int error,changed;
+ uint flag;
+ DBUG_ENTER("mi_rnext");
+
+ if ((inx = _mi_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_mi_readinfo(info))
+ DBUG_RETURN(my_errno);
+ if (info->s->concurrent_insert)
+ rw_rdlock(&info->s->key_root_lock[inx]);
+ changed=_mi_test_if_changed(info);
+ if (!flag)
+ {
+ switch(info->s->keyinfo[inx].key_alg){
+#ifdef HAVE_RTREE_KEYS
+ case HA_KEY_ALG_RTREE:
+ error=rtree_get_first(info,inx,info->lastkey_length);
+ break;
+#endif
+ case HA_KEY_ALG_BTREE:
+ default:
+ error=_mi_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= rtree_get_next(info,inx,info->lastkey_length);
+ break;
+#endif
+ case HA_KEY_ALG_BTREE:
+ default:
+ if (!changed)
+ error= _mi_search_next(info,info->s->keyinfo+inx,info->lastkey,
+ info->lastkey_length,flag,
+ info->s->state.key_root[inx]);
+ else
+ error= _mi_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=_mi_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);
+} /* mi_rnext */
diff --git a/storage/myisam/mi_rnext_same.c b/storage/myisam/mi_rnext_same.c
new file mode 100644
index 00000000000..06408f57a3f
--- /dev/null
+++ b/storage/myisam/mi_rnext_same.c
@@ -0,0 +1,105 @@
+/* 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 */
+
+#include "myisamdef.h"
+#include "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 mi_rnext_same(MI_INFO *info, byte *buf)
+{
+ int error;
+ uint inx,not_used;
+ MI_KEYDEF *keyinfo;
+ DBUG_ENTER("mi_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_mi_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=rtree_find_next(info,inx,
+ myisam_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=_mi_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->lastkey2,info->lastkey,
+ 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);
+} /* mi_rnext */
diff --git a/storage/myisam/mi_rprev.c b/storage/myisam/mi_rprev.c
new file mode 100644
index 00000000000..b787210e037
--- /dev/null
+++ b/storage/myisam/mi_rprev.c
@@ -0,0 +1,88 @@
+/* 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 */
+
+#include "myisamdef.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 mi_rprev(MI_INFO *info, byte *buf, int inx)
+{
+ int error,changed;
+ register uint flag;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("mi_rprev");
+
+ if ((inx = _mi_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_mi_readinfo(info))
+ DBUG_RETURN(my_errno);
+ changed=_mi_test_if_changed(info);
+ if (share->concurrent_insert)
+ rw_rdlock(&share->key_root_lock[inx]);
+ if (!flag)
+ error=_mi_search_last(info, share->keyinfo+inx,
+ share->state.key_root[inx]);
+ else if (!changed)
+ error=_mi_search_next(info,share->keyinfo+inx,info->lastkey,
+ info->lastkey_length,flag,
+ share->state.key_root[inx]);
+ else
+ error=_mi_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=_mi_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);
+} /* mi_rprev */
diff --git a/storage/myisam/mi_rrnd.c b/storage/myisam/mi_rrnd.c
new file mode 100644
index 00000000000..f6a2f021662
--- /dev/null
+++ b/storage/myisam/mi_rrnd.c
@@ -0,0 +1,60 @@
+/* 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 */
+
+/* Read a record with random-access. The position to the record must
+ get by MI_INFO. The next record can be read with pos= MI_POS_ERROR */
+
+
+#include "myisamdef.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 mi_rrnd(MI_INFO *info, byte *buf, register my_off_t filepos)
+{
+ my_bool skip_deleted_blocks;
+ DBUG_ENTER("mi_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));
+}
diff --git a/storage/myisam/mi_rsame.c b/storage/myisam/mi_rsame.c
new file mode 100644
index 00000000000..56c8d1226ca
--- /dev/null
+++ b/storage/myisam/mi_rsame.c
@@ -0,0 +1,66 @@
+/* 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 */
+
+#include "myisamdef.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 mi_rsame(MI_INFO *info, byte *record, int inx)
+{
+ DBUG_ENTER("mi_rsame");
+
+ if (inx != -1 && ! (((ulonglong) 1 << inx) & info->s->state.key_map))
+ {
+ 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_mi_readinfo(info))
+ DBUG_RETURN(my_errno);
+
+ if (inx >= 0)
+ {
+ info->lastinx=inx;
+ info->lastkey_length=_mi_make_key(info,(uint) inx,info->lastkey,record,
+ info->lastpos);
+ if (info->s->concurrent_insert)
+ rw_rdlock(&info->s->key_root_lock[inx]);
+ VOID(_mi_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);
+} /* mi_rsame */
diff --git a/storage/myisam/mi_rsamepos.c b/storage/myisam/mi_rsamepos.c
new file mode 100644
index 00000000000..a1d96fb7104
--- /dev/null
+++ b/storage/myisam/mi_rsamepos.c
@@ -0,0 +1,56 @@
+/* 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 */
+
+/* read record through position and fix key-position */
+/* As mi_rsame but supply a position */
+
+#include "myisamdef.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 mi_rsame_with_pos(MI_INFO *info, byte *record, int inx, my_off_t filepos)
+{
+ DBUG_ENTER("mi_rsame_with_pos");
+
+ if (inx < -1 || ! (((ulonglong) 1 << inx) & info->s->state.key_map))
+ {
+ 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=_mi_make_key(info,(uint) inx,info->lastkey,record,
+ info->lastpos);
+ info->update|=HA_STATE_KEY_CHANGED; /* Don't use indexposition */
+ }
+ DBUG_RETURN(0);
+} /* mi_rsame_pos */
diff --git a/storage/myisam/mi_scan.c b/storage/myisam/mi_scan.c
new file mode 100644
index 00000000000..90bc3430ba7
--- /dev/null
+++ b/storage/myisam/mi_scan.c
@@ -0,0 +1,46 @@
+/* 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 */
+
+/* Read through all rows sequntially */
+
+#include "myisamdef.h"
+
+int mi_scan_init(register MI_INFO *info)
+{
+ DBUG_ENTER("mi_scan_init");
+ info->nextpos=info->s->pack.header_length; /* Read first record */
+ info->lastinx= -1; /* Can't forward or backward */
+ if (info->opt_flag & WRITE_CACHE_USED && flush_io_cache(&info->rec_cache))
+ DBUG_RETURN(my_errno);
+ DBUG_RETURN(0);
+}
+
+/*
+ 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_END_OF_FILE = EOF.
+*/
+
+int mi_scan(MI_INFO *info, byte *buf)
+{
+ DBUG_ENTER("mi_scan");
+ /* Init all but update-flag */
+ info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
+ DBUG_RETURN ((*info->s->read_rnd)(info,buf,info->nextpos,1));
+}
diff --git a/storage/myisam/mi_search.c b/storage/myisam/mi_search.c
new file mode 100644
index 00000000000..0c82a4c4502
--- /dev/null
+++ b/storage/myisam/mi_search.c
@@ -0,0 +1,1829 @@
+/* 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 */
+
+/* key handling functions */
+
+#include "fulltext.h"
+#include "m_ctype.h"
+
+static my_bool _mi_get_prev_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uchar *keypos,
+ uint *return_key_length);
+
+ /* Check index */
+
+int _mi_check_index(MI_INFO *info, int inx)
+{
+ if (inx == -1) /* Use last index */
+ inx=info->lastinx;
+ if (inx < 0 || ! (((ulonglong) 1 << inx) & info->s->state.key_map))
+ {
+ my_errno=HA_ERR_WRONG_INDEX;
+ return -1;
+ }
+ if (info->lastinx != inx) /* Index changed */
+ {
+ info->lastinx = inx;
+ info->page_changed=1;
+ info->update= ((info->update & (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED)) |
+ HA_STATE_NEXT_FOUND | HA_STATE_PREV_FOUND);
+ }
+ if (info->opt_flag & WRITE_CACHE_USED && flush_io_cache(&info->rec_cache))
+ return(-1);
+ return(inx);
+} /* mi_check_index */
+
+
+ /*
+ ** Search after row by a key
+ ** Position to row is stored in info->lastpos
+ ** Return: -1 if not found
+ ** 1 if one should continue search on higher level
+ */
+
+int _mi_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ uchar *key, uint key_len, uint nextflag, register my_off_t pos)
+{
+ my_bool last_key;
+ int error,flag;
+ uint nod_flag;
+ uchar *keypos,*maxpos;
+ uchar lastkey[MI_MAX_KEY_BUFF],*buff;
+ DBUG_ENTER("_mi_search");
+ DBUG_PRINT("enter",("pos: %lu nextflag: %u lastpos: %lu",
+ (ulong) pos, nextflag, (ulong) info->lastpos));
+ DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE,keyinfo->seg,key,key_len););
+
+ if (pos == HA_OFFSET_ERROR)
+ {
+ my_errno=HA_ERR_KEY_NOT_FOUND; /* Didn't find key */
+ info->lastpos= HA_OFFSET_ERROR;
+ if (!(nextflag & (SEARCH_SMALLER | SEARCH_BIGGER | SEARCH_LAST)))
+ DBUG_RETURN(-1); /* Not found ; return error */
+ DBUG_RETURN(1); /* Search at upper levels */
+ }
+
+ if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,
+ test(!(nextflag & SEARCH_SAVE_BUFF)))))
+ goto err;
+ DBUG_DUMP("page",(byte*) buff,mi_getint(buff));
+
+ flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag,
+ &keypos,lastkey, &last_key);
+ if (flag == MI_FOUND_WRONG_KEY)
+ DBUG_RETURN(-1);
+ nod_flag=mi_test_if_nod(buff);
+ maxpos=buff+mi_getint(buff)-1;
+
+ if (flag)
+ {
+ if ((error=_mi_search(info,keyinfo,key,key_len,nextflag,
+ _mi_kpos(nod_flag,keypos))) <= 0)
+ DBUG_RETURN(error);
+
+ if (flag >0)
+ {
+ if (nextflag & (SEARCH_SMALLER | SEARCH_LAST) &&
+ keypos == buff+2+nod_flag)
+ DBUG_RETURN(1); /* Bigger than key */
+ }
+ else if (nextflag & SEARCH_BIGGER && keypos >= maxpos)
+ DBUG_RETURN(1); /* Smaller than key */
+ }
+ else
+ {
+ if ((nextflag & SEARCH_FIND) && nod_flag &&
+ ((keyinfo->flag & (HA_NOSAME | HA_NULL_PART)) != HA_NOSAME ||
+ key_len != USE_WHOLE_KEY))
+ {
+ if ((error=_mi_search(info,keyinfo,key,key_len,SEARCH_FIND,
+ _mi_kpos(nod_flag,keypos))) >= 0 ||
+ my_errno != HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN(error);
+ info->last_keypage= HA_OFFSET_ERROR; /* Buffer not in mem */
+ }
+ }
+ if (pos != info->last_keypage)
+ {
+ uchar *old_buff=buff;
+ if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,
+ test(!(nextflag & SEARCH_SAVE_BUFF)))))
+ goto err;
+ keypos=buff+(keypos-old_buff);
+ maxpos=buff+(maxpos-old_buff);
+ }
+
+ if ((nextflag & (SEARCH_SMALLER | SEARCH_LAST)) && flag != 0)
+ {
+ uint not_used;
+ if (_mi_get_prev_key(info,keyinfo, buff, info->lastkey, keypos,
+ &info->lastkey_length))
+ goto err;
+ if (!(nextflag & SEARCH_SMALLER) &&
+ ha_key_cmp(keyinfo->seg, info->lastkey, key, key_len, SEARCH_FIND,
+ &not_used))
+ {
+ my_errno=HA_ERR_KEY_NOT_FOUND; /* Didn't find key */
+ goto err;
+ }
+ }
+ else
+ {
+ info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey);
+ if (!info->lastkey_length)
+ goto err;
+ memcpy(info->lastkey,lastkey,info->lastkey_length);
+ }
+ info->lastpos=_mi_dpos(info,0,info->lastkey+info->lastkey_length);
+ /* Save position for a possible read next / previous */
+ info->int_keypos=info->buff+ (keypos-buff);
+ info->int_maxpos=info->buff+ (maxpos-buff);
+ info->int_nod_flag=nod_flag;
+ info->int_keytree_version=keyinfo->version;
+ info->last_search_keypage=info->last_keypage;
+ info->page_changed=0;
+ info->buff_used= (info->buff != buff); /* If we have to reread buff */
+
+ DBUG_PRINT("exit",("found key at %lu",(ulong) info->lastpos));
+ DBUG_RETURN(0);
+
+err:
+ DBUG_PRINT("exit",("Error: %d",my_errno));
+ info->lastpos= HA_OFFSET_ERROR;
+ info->page_changed=1;
+ DBUG_RETURN (-1);
+} /* _mi_search */
+
+
+ /* Search after key in page-block */
+ /* If packed key puts smaller or identical key in buff */
+ /* ret_pos point to where find or bigger key starts */
+ /* ARGSUSED */
+
+int _mi_bin_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uint key_len, uint comp_flag, uchar **ret_pos,
+ uchar *buff __attribute__((unused)), my_bool *last_key)
+{
+ reg4 int start,mid,end,save_end;
+ int flag;
+ uint totlength,nod_flag,not_used;
+ DBUG_ENTER("_mi_bin_search");
+
+ LINT_INIT(flag);
+ totlength=keyinfo->keylength+(nod_flag=mi_test_if_nod(page));
+ start=0; mid=1;
+ save_end=end=(int) ((mi_getint(page)-2-nod_flag)/totlength-1);
+ DBUG_PRINT("test",("mi_getint: %d end: %d",mi_getint(page),end));
+ page+=2+nod_flag;
+
+ while (start != end)
+ {
+ mid= (start+end)/2;
+ if ((flag=ha_key_cmp(keyinfo->seg,page+(uint) mid*totlength,key,key_len,
+ comp_flag,&not_used))
+ >= 0)
+ end=mid;
+ else
+ start=mid+1;
+ }
+ if (mid != start)
+ flag=ha_key_cmp(keyinfo->seg,page+(uint) start*totlength,key,key_len,
+ comp_flag,&not_used);
+ if (flag < 0)
+ start++; /* point at next, bigger key */
+ *ret_pos=page+(uint) start*totlength;
+ *last_key= end == save_end;
+ DBUG_PRINT("exit",("flag: %d keypos: %d",flag,start));
+ DBUG_RETURN(flag);
+} /* _mi_bin_search */
+
+
+ /* Used instead of _mi_bin_search() when key is packed */
+ /* Puts smaller or identical key in buff */
+ /* Key is searched sequentially */
+
+int _mi_seq_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uint key_len, uint comp_flag, uchar **ret_pos,
+ uchar *buff, my_bool *last_key)
+{
+ int flag;
+ uint nod_flag,length,not_used;
+ uchar t_buff[MI_MAX_KEY_BUFF],*end;
+ DBUG_ENTER("_mi_seq_search");
+
+ LINT_INIT(flag); LINT_INIT(length);
+ end= page+mi_getint(page);
+ nod_flag=mi_test_if_nod(page);
+ page+=2+nod_flag;
+ *ret_pos=page;
+ t_buff[0]=0; /* Avoid bugs */
+ while (page < end)
+ {
+ length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff);
+ if (length == 0 || page > end)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_PRINT("error",("Found wrong key: length: %u page: %p end: %p",
+ length, page, end));
+ DBUG_RETURN(MI_FOUND_WRONG_KEY);
+ }
+ if ((flag=ha_key_cmp(keyinfo->seg,t_buff,key,key_len,comp_flag,
+ &not_used)) >= 0)
+ break;
+#ifdef EXTRA_DEBUG
+ DBUG_PRINT("loop",("page: %p key: '%s' flag: %d", page, t_buff, flag));
+#endif
+ memcpy(buff,t_buff,length);
+ *ret_pos=page;
+ }
+ if (flag == 0)
+ memcpy(buff,t_buff,length); /* Result is first key */
+ *last_key= page == end;
+ DBUG_PRINT("exit",("flag: %d ret_pos: %p", flag, *ret_pos));
+ DBUG_RETURN(flag);
+} /* _mi_seq_search */
+
+
+int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uint key_len, uint nextflag, uchar **ret_pos,
+ uchar *buff, my_bool *last_key)
+{
+ /*
+ my_flag is raw comparison result to be changed according to
+ SEARCH_NO_FIND,SEARCH_LAST and HA_REVERSE_SORT flags.
+ flag is the value returned by ha_key_cmp and as treated as final
+ */
+ int flag=0, my_flag=-1;
+ uint nod_flag, length, len, matched, cmplen, kseg_len;
+ uint prefix_len,suffix_len;
+ int key_len_skip, seg_len_pack, key_len_left;
+ uchar *end, *kseg, *vseg;
+ uchar *sort_order=keyinfo->seg->charset->sort_order;
+ uchar tt_buff[MI_MAX_KEY_BUFF+2], *t_buff=tt_buff+2;
+ uchar *saved_from, *saved_to, *saved_vseg;
+ uint saved_length=0, saved_prefix_len=0;
+ uint length_pack;
+ DBUG_ENTER("_mi_prefix_search");
+
+ LINT_INIT(length);
+ LINT_INIT(prefix_len);
+ LINT_INIT(seg_len_pack);
+ LINT_INIT(saved_from);
+ LINT_INIT(saved_to);
+ LINT_INIT(saved_vseg);
+
+ t_buff[0]=0; /* Avoid bugs */
+ end= page+mi_getint(page);
+ nod_flag=mi_test_if_nod(page);
+ page+=2+nod_flag;
+ *ret_pos=page;
+ kseg=key;
+
+ get_key_pack_length(kseg_len,length_pack,kseg);
+ key_len_skip=length_pack+kseg_len;
+ key_len_left=(int) key_len- (int) key_len_skip;
+ cmplen=(key_len_left>=0) ? kseg_len : key_len-length_pack;
+ DBUG_PRINT("info",("key: '%.*s'",kseg_len,kseg));
+
+ /*
+ Keys are compressed the following way:
+
+ If the max length of first key segment <= 127 characters the prefix is
+ 1 byte else it's 2 byte
+
+ prefix The high bit is set if this is a prefix for the prev key
+ length Packed length if the previous was a prefix byte
+ [length] Length character of data
+ next-key-seg Next key segments
+ */
+
+ matched=0; /* how many char's from prefix were alredy matched */
+ len=0; /* length of previous key unpacked */
+
+ while (page < end)
+ {
+ uint packed= *page & 128;
+
+ vseg=page;
+ if (keyinfo->seg->length >= 127)
+ {
+ suffix_len=mi_uint2korr(vseg) & 32767;
+ vseg+=2;
+ }
+ else
+ suffix_len= *vseg++ & 127;
+
+ if (packed)
+ {
+ if (suffix_len == 0) /* Same key */
+ prefix_len=len;
+ else
+ {
+ prefix_len=suffix_len;
+ get_key_length(suffix_len,vseg);
+ }
+ }
+ else
+ prefix_len=0;
+
+ len=prefix_len+suffix_len;
+ seg_len_pack=get_pack_length(len);
+ t_buff=tt_buff+3-seg_len_pack;
+ store_key_length(t_buff,len);
+
+ if (prefix_len > saved_prefix_len)
+ memcpy(t_buff+seg_len_pack+saved_prefix_len,saved_vseg,
+ prefix_len-saved_prefix_len);
+ saved_vseg=vseg;
+ saved_prefix_len=prefix_len;
+
+ DBUG_PRINT("loop",("page: '%.*s%.*s'",prefix_len,t_buff+seg_len_pack,
+ suffix_len,vseg));
+ {
+ uchar *from=vseg+suffix_len;
+ HA_KEYSEG *keyseg;
+ uint l;
+
+ for (keyseg=keyinfo->seg+1 ; keyseg->type ; keyseg++ )
+ {
+
+ if (keyseg->flag & HA_NULL_PART)
+ {
+ if (!(*from++))
+ continue;
+ }
+ if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART | HA_SPACE_PACK))
+ {
+ get_key_length(l,from);
+ }
+ else
+ l=keyseg->length;
+
+ from+=l;
+ }
+ from+=keyseg->length;
+ page=from+nod_flag;
+ length=from-vseg;
+ }
+
+ if (page > end)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_PRINT("error",("Found wrong key: length: %u page: %p end: %p",
+ length, page, end));
+ DBUG_RETURN(MI_FOUND_WRONG_KEY);
+ }
+
+ if (matched >= prefix_len)
+ {
+ /* We have to compare. But we can still skip part of the key */
+ uint left;
+ uchar *k=kseg+prefix_len;
+
+ left=(len>cmplen) ? cmplen-prefix_len : suffix_len;
+
+ matched=prefix_len+left;
+
+ if (sort_order)
+ {
+ for (my_flag=0;left;left--)
+ if ((my_flag= (int) sort_order[*vseg++] - (int) sort_order[*k++]))
+ break;
+ }
+ else
+ {
+ for (my_flag=0;left;left--)
+ if ((my_flag= (int) *vseg++ - (int) *k++))
+ break;
+ }
+
+ if (my_flag>0) /* mismatch */
+ break;
+ if (my_flag==0) /* match */
+ {
+ /*
+ ** len cmplen seg_left_len more_segs
+ ** < matched=len; continue search
+ ** > = prefix ? found : (matched=len; continue search)
+ ** > < - ok, found
+ ** = < - ok, found
+ ** = = - ok, found
+ ** = = + next seg
+ */
+ if (len < cmplen)
+ {
+ if ((keyinfo->seg->type != HA_KEYTYPE_TEXT &&
+ keyinfo->seg->type != HA_KEYTYPE_VARTEXT1 &&
+ keyinfo->seg->type != HA_KEYTYPE_VARTEXT2))
+ my_flag= -1;
+ else
+ {
+ /* We have to compare k and vseg as if they where space extended */
+ uchar *end= k+ (cmplen - len);
+ for ( ; k < end && *k == ' '; k++) ;
+ if (k == end)
+ goto cmp_rest; /* should never happen */
+ if (*k < (uchar) ' ')
+ {
+ my_flag= 1; /* Compared string is smaller */
+ break;
+ }
+ my_flag= -1; /* Continue searching */
+ }
+ }
+ else if (len > cmplen)
+ {
+ uchar *end;
+ if ((nextflag & SEARCH_PREFIX) && key_len_left == 0)
+ goto fix_flag;
+
+ /* We have to compare k and vseg as if they where space extended */
+ for (end=vseg + (len-cmplen) ;
+ vseg < end && *vseg == (uchar) ' ';
+ vseg++, matched++) ;
+ DBUG_ASSERT(vseg < end);
+
+ if (*vseg > (uchar) ' ')
+ {
+ my_flag= 1; /* Compared string is smaller */
+ break;
+ }
+ my_flag= -1; /* Continue searching */
+ }
+ else
+ {
+ cmp_rest:
+ if (key_len_left>0)
+ {
+ uint not_used;
+ if ((flag = ha_key_cmp(keyinfo->seg+1,vseg,
+ k,key_len_left,nextflag,&not_used)) >= 0)
+ break;
+ }
+ else
+ {
+ /*
+ at this line flag==-1 if the following lines were already
+ visited and 0 otherwise, i.e. flag <=0 here always !!!
+ */
+ fix_flag:
+ DBUG_ASSERT(flag <= 0);
+ if (nextflag & (SEARCH_NO_FIND | SEARCH_LAST))
+ flag=(nextflag & (SEARCH_BIGGER | SEARCH_LAST)) ? -1 : 1;
+ if (flag>=0)
+ break;
+ }
+ }
+ }
+ matched-=left;
+ }
+ /* else (matched < prefix_len) ---> do nothing. */
+
+ memcpy(buff,t_buff,saved_length=seg_len_pack+prefix_len);
+ saved_to=buff+saved_length;
+ saved_from=saved_vseg;
+ saved_length=length;
+ *ret_pos=page;
+ }
+ if (my_flag)
+ flag=(keyinfo->seg->flag & HA_REVERSE_SORT) ? -my_flag : my_flag;
+ if (flag == 0)
+ {
+ memcpy(buff,t_buff,saved_length=seg_len_pack+prefix_len);
+ saved_to=buff+saved_length;
+ saved_from=saved_vseg;
+ saved_length=length;
+ }
+ if (saved_length)
+ memcpy(saved_to,saved_from,saved_length);
+
+ *last_key= page == end;
+
+ DBUG_PRINT("exit",("flag: %d ret_pos: %p", flag, *ret_pos));
+ DBUG_RETURN(flag);
+} /* _mi_prefix_search */
+
+
+ /* Get pos to a key_block */
+
+my_off_t _mi_kpos(uint nod_flag, uchar *after_key)
+{
+ after_key-=nod_flag;
+ switch (nod_flag) {
+#if SIZEOF_OFF_T > 4
+ case 7:
+ return mi_uint7korr(after_key)*MI_MIN_KEY_BLOCK_LENGTH;
+ case 6:
+ return mi_uint6korr(after_key)*MI_MIN_KEY_BLOCK_LENGTH;
+ case 5:
+ return mi_uint5korr(after_key)*MI_MIN_KEY_BLOCK_LENGTH;
+#else
+ case 7:
+ after_key++;
+ case 6:
+ after_key++;
+ case 5:
+ after_key++;
+#endif
+ case 4:
+ return ((my_off_t) mi_uint4korr(after_key))*MI_MIN_KEY_BLOCK_LENGTH;
+ case 3:
+ return ((my_off_t) mi_uint3korr(after_key))*MI_MIN_KEY_BLOCK_LENGTH;
+ case 2:
+ return (my_off_t) (mi_uint2korr(after_key)*MI_MIN_KEY_BLOCK_LENGTH);
+ case 1:
+ return (uint) (*after_key)*MI_MIN_KEY_BLOCK_LENGTH;
+ case 0: /* At leaf page */
+ default: /* Impossible */
+ return(HA_OFFSET_ERROR);
+ }
+} /* _kpos */
+
+
+ /* Save pos to a key_block */
+
+void _mi_kpointer(register MI_INFO *info, register uchar *buff, my_off_t pos)
+{
+ pos/=MI_MIN_KEY_BLOCK_LENGTH;
+ switch (info->s->base.key_reflength) {
+#if SIZEOF_OFF_T > 4
+ case 7: mi_int7store(buff,pos); break;
+ case 6: mi_int6store(buff,pos); break;
+ case 5: mi_int5store(buff,pos); break;
+#else
+ case 7: *buff++=0;
+ /* fall trough */
+ case 6: *buff++=0;
+ /* fall trough */
+ case 5: *buff++=0;
+ /* fall trough */
+#endif
+ case 4: mi_int4store(buff,pos); break;
+ case 3: mi_int3store(buff,pos); break;
+ case 2: mi_int2store(buff,(uint) pos); break;
+ case 1: buff[0]= (uchar) pos; break;
+ default: abort(); /* impossible */
+ }
+} /* _mi_kpointer */
+
+
+ /* Calc pos to a data-record from a key */
+
+
+my_off_t _mi_dpos(MI_INFO *info, uint nod_flag, uchar *after_key)
+{
+ my_off_t pos;
+ after_key-=(nod_flag + info->s->rec_reflength);
+ switch (info->s->rec_reflength) {
+#if SIZEOF_OFF_T > 4
+ case 8: pos= (my_off_t) mi_uint8korr(after_key); break;
+ case 7: pos= (my_off_t) mi_uint7korr(after_key); break;
+ case 6: pos= (my_off_t) mi_uint6korr(after_key); break;
+ case 5: pos= (my_off_t) mi_uint5korr(after_key); break;
+#else
+ case 8: pos= (my_off_t) mi_uint4korr(after_key+4); break;
+ case 7: pos= (my_off_t) mi_uint4korr(after_key+3); break;
+ case 6: pos= (my_off_t) mi_uint4korr(after_key+2); break;
+ case 5: pos= (my_off_t) mi_uint4korr(after_key+1); break;
+#endif
+ case 4: pos= (my_off_t) mi_uint4korr(after_key); break;
+ case 3: pos= (my_off_t) mi_uint3korr(after_key); break;
+ case 2: pos= (my_off_t) mi_uint2korr(after_key); break;
+ default:
+ pos=0L; /* Shut compiler up */
+ }
+ return (info->s->options &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ? pos :
+ pos*info->s->base.pack_reclength;
+}
+
+
+/* Calc position from a record pointer ( in delete link chain ) */
+
+my_off_t _mi_rec_pos(MYISAM_SHARE *s, uchar *ptr)
+{
+ my_off_t pos;
+ switch (s->rec_reflength) {
+#if SIZEOF_OFF_T > 4
+ case 8:
+ pos= (my_off_t) mi_uint8korr(ptr);
+ if (pos == HA_OFFSET_ERROR)
+ return HA_OFFSET_ERROR; /* end of list */
+ break;
+ case 7:
+ pos= (my_off_t) mi_uint7korr(ptr);
+ if (pos == (((my_off_t) 1) << 56) -1)
+ return HA_OFFSET_ERROR; /* end of list */
+ break;
+ case 6:
+ pos= (my_off_t) mi_uint6korr(ptr);
+ if (pos == (((my_off_t) 1) << 48) -1)
+ return HA_OFFSET_ERROR; /* end of list */
+ break;
+ case 5:
+ pos= (my_off_t) mi_uint5korr(ptr);
+ if (pos == (((my_off_t) 1) << 40) -1)
+ return HA_OFFSET_ERROR; /* end of list */
+ break;
+#else
+ case 8:
+ case 7:
+ case 6:
+ case 5:
+ ptr+= (s->rec_reflength-4);
+ /* fall through */
+#endif
+ case 4:
+ pos= (my_off_t) mi_uint4korr(ptr);
+ if (pos == (my_off_t) (uint32) ~0L)
+ return HA_OFFSET_ERROR;
+ break;
+ case 3:
+ pos= (my_off_t) mi_uint3korr(ptr);
+ if (pos == (my_off_t) (1 << 24) -1)
+ return HA_OFFSET_ERROR;
+ break;
+ case 2:
+ pos= (my_off_t) mi_uint2korr(ptr);
+ if (pos == (my_off_t) (1 << 16) -1)
+ return HA_OFFSET_ERROR;
+ break;
+ default: abort(); /* Impossible */
+ }
+ return ((s->options &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ? pos :
+ pos*s->base.pack_reclength);
+}
+
+
+ /* save position to record */
+
+void _mi_dpointer(MI_INFO *info, uchar *buff, my_off_t pos)
+{
+ if (!(info->s->options &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) &&
+ pos != HA_OFFSET_ERROR)
+ pos/=info->s->base.pack_reclength;
+
+ switch (info->s->rec_reflength) {
+#if SIZEOF_OFF_T > 4
+ case 8: mi_int8store(buff,pos); break;
+ case 7: mi_int7store(buff,pos); break;
+ case 6: mi_int6store(buff,pos); break;
+ case 5: mi_int5store(buff,pos); break;
+#else
+ case 8: *buff++=0;
+ /* fall trough */
+ case 7: *buff++=0;
+ /* fall trough */
+ case 6: *buff++=0;
+ /* fall trough */
+ case 5: *buff++=0;
+ /* fall trough */
+#endif
+ case 4: mi_int4store(buff,pos); break;
+ case 3: mi_int3store(buff,pos); break;
+ case 2: mi_int2store(buff,(uint) pos); break;
+ default: abort(); /* Impossible */
+ }
+} /* _mi_dpointer */
+
+
+ /* Get key from key-block */
+ /* page points at previous key; its advanced to point at next key */
+ /* key should contain previous key */
+ /* Returns length of found key + pointers */
+ /* nod_flag is a flag if we are on nod */
+
+ /* same as _mi_get_key but used with fixed length keys */
+
+uint _mi_get_static_key(register MI_KEYDEF *keyinfo, uint nod_flag,
+ register uchar **page, register uchar *key)
+{
+ memcpy((byte*) key,(byte*) *page,
+ (size_t) (keyinfo->keylength+nod_flag));
+ *page+=keyinfo->keylength+nod_flag;
+ return(keyinfo->keylength);
+} /* _mi_get_static_key */
+
+
+/* Key with is packed against previous key or key with a NULL column */
+
+uint _mi_get_pack_key(register MI_KEYDEF *keyinfo, uint nod_flag,
+ register uchar **page_pos, register uchar *key)
+{
+ reg1 HA_KEYSEG *keyseg;
+ uchar *start_key,*page=*page_pos;
+ uint length;
+
+ start_key=key;
+ for (keyseg=keyinfo->seg ; keyseg->type ;keyseg++)
+ {
+ if (keyseg->flag & HA_PACK_KEY)
+ {
+ /* key with length, packed to previous key */
+ uchar *start=key;
+ uint packed= *page & 128,tot_length,rest_length;
+ if (keyseg->length >= 127)
+ {
+ length=mi_uint2korr(page) & 32767;
+ page+=2;
+ }
+ else
+ length= *page++ & 127;
+
+ if (packed)
+ {
+ if (length > (uint) keyseg->length)
+ {
+ mi_print_error(keyinfo->share, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return 0; /* Error */
+ }
+ if (length == 0) /* Same key */
+ {
+ if (keyseg->flag & HA_NULL_PART)
+ *key++=1; /* Can't be NULL */
+ get_key_length(length,key);
+ key+= length; /* Same diff_key as prev */
+ if (length > keyseg->length)
+ {
+ DBUG_PRINT("error",
+ ("Found too long null packed key: %u of %u at %p",
+ length, keyseg->length, *page_pos));
+ DBUG_DUMP("key",(char*) *page_pos,16);
+ mi_print_error(keyinfo->share, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return 0;
+ }
+ continue;
+ }
+ if (keyseg->flag & HA_NULL_PART)
+ {
+ key++; /* Skip null marker*/
+ start++;
+ }
+
+ get_key_length(rest_length,page);
+ tot_length=rest_length+length;
+
+ /* If the stored length has changed, we must move the key */
+ if (tot_length >= 255 && *start != 255)
+ {
+ /* length prefix changed from a length of one to a length of 3 */
+ bmove_upp((char*) key+length+3,(char*) key+length+1,length);
+ *key=255;
+ mi_int2store(key+1,tot_length);
+ key+=3+length;
+ }
+ else if (tot_length < 255 && *start == 255)
+ {
+ bmove(key+1,key+3,length);
+ *key=tot_length;
+ key+=1+length;
+ }
+ else
+ {
+ store_key_length_inc(key,tot_length);
+ key+=length;
+ }
+ memcpy(key,page,rest_length);
+ page+=rest_length;
+ key+=rest_length;
+ continue;
+ }
+ else
+ {
+ if (keyseg->flag & HA_NULL_PART)
+ {
+ if (!length--) /* Null part */
+ {
+ *key++=0;
+ continue;
+ }
+ *key++=1; /* Not null */
+ }
+ }
+ if (length > (uint) keyseg->length)
+ {
+ DBUG_PRINT("error",("Found too long packed key: %u of %u at %p",
+ length, keyseg->length, *page_pos));
+ DBUG_DUMP("key",(char*) *page_pos,16);
+ mi_print_error(keyinfo->share, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return 0; /* Error */
+ }
+ store_key_length_inc(key,length);
+ }
+ else
+ {
+ if (keyseg->flag & HA_NULL_PART)
+ {
+ if (!(*key++ = *page++))
+ continue;
+ }
+ if (keyseg->flag &
+ (HA_VAR_LENGTH_PART | HA_BLOB_PART | HA_SPACE_PACK))
+ {
+ uchar *tmp=page;
+ get_key_length(length,tmp);
+ length+=(uint) (tmp-page);
+ }
+ else
+ length=keyseg->length;
+ }
+ memcpy((byte*) key,(byte*) page,(size_t) length);
+ key+=length;
+ page+=length;
+ }
+ length=keyseg->length+nod_flag;
+ bmove((byte*) key,(byte*) page,length);
+ *page_pos= page+length;
+ return ((uint) (key-start_key)+keyseg->length);
+} /* _mi_get_pack_key */
+
+
+
+/* key that is packed relatively to previous */
+
+uint _mi_get_binary_pack_key(register MI_KEYDEF *keyinfo, uint nod_flag,
+ register uchar **page_pos, register uchar *key)
+{
+ reg1 HA_KEYSEG *keyseg;
+ uchar *start_key,*page,*page_end,*from,*from_end;
+ uint length,tmp;
+
+ page= *page_pos;
+ page_end=page+MI_MAX_KEY_BUFF+1;
+ start_key=key;
+
+ get_key_length(length,page);
+ if (length)
+ {
+ if (length > keyinfo->maxlength)
+ {
+ DBUG_PRINT("error",("Found too long binary packed key: %u of %u at %p",
+ length, keyinfo->maxlength, *page_pos));
+ DBUG_DUMP("key",(char*) *page_pos,16);
+ mi_print_error(keyinfo->share, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return 0; /* Wrong key */
+ }
+ from=key; from_end=key+length;
+ }
+ else
+ {
+ from=page; from_end=page_end; /* Not packed key */
+ }
+
+ /*
+ The trouble is that key is split in two parts:
+ The first part is in from ...from_end-1.
+ The second part starts at page
+ */
+ for (keyseg=keyinfo->seg ; keyseg->type ;keyseg++)
+ {
+ if (keyseg->flag & HA_NULL_PART)
+ {
+ if (from == from_end) { from=page; from_end=page_end; }
+ if (!(*key++ = *from++))
+ continue; /* Null part */
+ }
+ if (keyseg->flag & (HA_VAR_LENGTH_PART | HA_BLOB_PART | HA_SPACE_PACK))
+ {
+ /* Get length of dynamic length key part */
+ if (from == from_end) { from=page; from_end=page_end; }
+ if ((length= (*key++ = *from++)) == 255)
+ {
+ if (from == from_end) { from=page; from_end=page_end; }
+ length= (uint) ((*key++ = *from++)) << 8;
+ if (from == from_end) { from=page; from_end=page_end; }
+ length+= (uint) ((*key++ = *from++));
+ }
+ }
+ else
+ length=keyseg->length;
+
+ if ((tmp=(uint) (from_end-from)) <= length)
+ {
+ key+=tmp; /* Use old key */
+ length-=tmp;
+ from=page; from_end=page_end;
+ }
+ DBUG_PRINT("info",("key: %p from: %p length: %u",
+ key, from, length));
+ memcpy_overlap((byte*) key, (byte*) from, (size_t) length);
+ key+=length;
+ from+=length;
+ }
+ length=keyseg->length+nod_flag;
+ if ((tmp=(uint) (from_end-from)) <= length)
+ {
+ memcpy(key+tmp,page,length-tmp); /* Get last part of key */
+ *page_pos= page+length-tmp;
+ }
+ else
+ {
+ if (from_end != page_end)
+ {
+ DBUG_PRINT("error",("Error when unpacking key"));
+ mi_print_error(keyinfo->share, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ return 0; /* Error */
+ }
+ memcpy((byte*) key,(byte*) from,(size_t) length);
+ *page_pos= from+length;
+ }
+ return((uint) (key-start_key)+keyseg->length);
+}
+
+
+ /* Get key at position without knowledge of previous key */
+ /* Returns pointer to next key */
+
+uchar *_mi_get_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uchar *keypos, uint *return_key_length)
+{
+ uint nod_flag;
+ DBUG_ENTER("_mi_get_key");
+
+ nod_flag=mi_test_if_nod(page);
+ if (! (keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)))
+ {
+ bmove((byte*) key,(byte*) keypos,keyinfo->keylength+nod_flag);
+ DBUG_RETURN(keypos+keyinfo->keylength+nod_flag);
+ }
+ else
+ {
+ page+=2+nod_flag;
+ key[0]=0; /* safety */
+ while (page <= keypos)
+ {
+ *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key);
+ if (*return_key_length == 0)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_RETURN(0);
+ }
+ }
+ }
+ DBUG_PRINT("exit",("page: %p length: %u", page, *return_key_length));
+ DBUG_RETURN(page);
+} /* _mi_get_key */
+
+
+ /* Get key at position without knowledge of previous key */
+ /* Returns 0 if ok */
+
+static my_bool _mi_get_prev_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uchar *keypos,
+ uint *return_key_length)
+{
+ uint nod_flag;
+ DBUG_ENTER("_mi_get_prev_key");
+
+ nod_flag=mi_test_if_nod(page);
+ if (! (keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)))
+ {
+ *return_key_length=keyinfo->keylength;
+ bmove((byte*) key,(byte*) keypos- *return_key_length-nod_flag,
+ *return_key_length);
+ DBUG_RETURN(0);
+ }
+ else
+ {
+ page+=2+nod_flag;
+ key[0]=0; /* safety */
+ while (page < keypos)
+ {
+ *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key);
+ if (*return_key_length == 0)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+} /* _mi_get_key */
+
+
+
+ /* Get last key from key-page */
+ /* Return pointer to where key starts */
+
+uchar *_mi_get_last_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page,
+ uchar *lastkey, uchar *endpos, uint *return_key_length)
+{
+ uint nod_flag;
+ uchar *lastpos;
+ DBUG_ENTER("_mi_get_last_key");
+ DBUG_PRINT("enter",("page: %p endpos: %p", page, endpos));
+
+ nod_flag=mi_test_if_nod(page);
+ if (! (keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)))
+ {
+ lastpos=endpos-keyinfo->keylength-nod_flag;
+ *return_key_length=keyinfo->keylength;
+ if (lastpos > page)
+ bmove((byte*) lastkey,(byte*) lastpos,keyinfo->keylength+nod_flag);
+ }
+ else
+ {
+ lastpos=(page+=2+nod_flag);
+ lastkey[0]=0;
+ while (page < endpos)
+ {
+ lastpos=page;
+ *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,lastkey);
+ if (*return_key_length == 0)
+ {
+ DBUG_PRINT("error",("Couldn't find last key: page: %p", page));
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_RETURN(0);
+ }
+ }
+ }
+ DBUG_PRINT("exit",("lastpos: %p length: %u", lastpos, *return_key_length));
+ DBUG_RETURN(lastpos);
+} /* _mi_get_last_key */
+
+
+ /* Calculate length of key */
+
+uint _mi_keylength(MI_KEYDEF *keyinfo, register uchar *key)
+{
+ reg1 HA_KEYSEG *keyseg;
+ uchar *start;
+
+ if (! (keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)))
+ return (keyinfo->keylength);
+
+ start=key;
+ for (keyseg=keyinfo->seg ; keyseg->type ; keyseg++)
+ {
+ if (keyseg->flag & HA_NULL_PART)
+ if (!*key++)
+ continue;
+ if (keyseg->flag & (HA_SPACE_PACK | HA_BLOB_PART | HA_VAR_LENGTH_PART))
+ {
+ uint length;
+ get_key_length(length,key);
+ key+=length;
+ }
+ else
+ key+= keyseg->length;
+ }
+ return((uint) (key-start)+keyseg->length);
+} /* _mi_keylength */
+
+
+/*
+ Calculate length of part key.
+
+ Used in mi_rkey() to find the key found for the key-part that was used.
+ This is needed in case of multi-byte character sets where we may search
+ after '0xDF' but find 'ss'
+*/
+
+uint _mi_keylength_part(MI_KEYDEF *keyinfo, register uchar *key,
+ HA_KEYSEG *end)
+{
+ reg1 HA_KEYSEG *keyseg;
+ uchar *start= key;
+
+ for (keyseg=keyinfo->seg ; keyseg != end ; keyseg++)
+ {
+ if (keyseg->flag & HA_NULL_PART)
+ if (!*key++)
+ continue;
+ if (keyseg->flag & (HA_SPACE_PACK | HA_BLOB_PART | HA_VAR_LENGTH_PART))
+ {
+ uint length;
+ get_key_length(length,key);
+ key+=length;
+ }
+ else
+ key+= keyseg->length;
+ }
+ return (uint) (key-start);
+}
+
+ /* Move a key */
+
+uchar *_mi_move_key(MI_KEYDEF *keyinfo, uchar *to, uchar *from)
+{
+ reg1 uint length;
+ memcpy((byte*) to, (byte*) from,
+ (size_t) (length=_mi_keylength(keyinfo,from)));
+ return to+length;
+}
+
+ /* Find next/previous record with same key */
+ /* This can't be used when database is touched after last read */
+
+int _mi_search_next(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ uchar *key, uint key_length, uint nextflag, my_off_t pos)
+{
+ int error;
+ uint nod_flag;
+ uchar lastkey[MI_MAX_KEY_BUFF];
+ DBUG_ENTER("_mi_search_next");
+ DBUG_PRINT("enter",("nextflag: %u lastpos: %lu int_keypos: %lu",
+ nextflag, (ulong) info->lastpos,
+ (ulong) info->int_keypos));
+ DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE,keyinfo->seg,key,key_length););
+
+ /* Force full read if we are at last key or if we are not on a leaf
+ and the key tree has changed since we used it last time
+ Note that even if the key tree has changed since last read, we can use
+ the last read data from the leaf if we haven't used the buffer for
+ something else.
+ */
+
+ if (((nextflag & SEARCH_BIGGER) && info->int_keypos >= info->int_maxpos) ||
+ info->page_changed ||
+ (info->int_keytree_version != keyinfo->version &&
+ (info->int_nod_flag || info->buff_used)))
+ DBUG_RETURN(_mi_search(info,keyinfo,key, USE_WHOLE_KEY,
+ nextflag | SEARCH_SAVE_BUFF, pos));
+
+ if (info->buff_used)
+ {
+ if (!_mi_fetch_keypage(info,keyinfo,info->last_search_keypage,
+ DFLT_INIT_HITS,info->buff,0))
+ DBUG_RETURN(-1);
+ info->buff_used=0;
+ }
+
+ /* Last used buffer is in info->buff */
+ nod_flag=mi_test_if_nod(info->buff);
+
+ if (nextflag & SEARCH_BIGGER) /* Next key */
+ {
+ my_off_t tmp_pos=_mi_kpos(nod_flag,info->int_keypos);
+ if (tmp_pos != HA_OFFSET_ERROR)
+ {
+ if ((error=_mi_search(info,keyinfo,key, USE_WHOLE_KEY,
+ nextflag | SEARCH_SAVE_BUFF, tmp_pos)) <=0)
+ DBUG_RETURN(error);
+ }
+ memcpy(lastkey,key,key_length);
+ if (!(info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag,
+ &info->int_keypos,lastkey)))
+ DBUG_RETURN(-1);
+ }
+ else /* Previous key */
+ {
+ uint length;
+ /* Find start of previous key */
+ info->int_keypos=_mi_get_last_key(info,keyinfo,info->buff,lastkey,
+ info->int_keypos, &length);
+ if (!info->int_keypos)
+ DBUG_RETURN(-1);
+ if (info->int_keypos == info->buff+2)
+ DBUG_RETURN(_mi_search(info,keyinfo,key, USE_WHOLE_KEY,
+ nextflag | SEARCH_SAVE_BUFF, pos));
+ if ((error=_mi_search(info,keyinfo,key, USE_WHOLE_KEY,
+ nextflag | SEARCH_SAVE_BUFF,
+ _mi_kpos(nod_flag,info->int_keypos))) <= 0)
+ DBUG_RETURN(error);
+
+ /* QQ: We should be able to optimize away the following call */
+ if (! _mi_get_last_key(info,keyinfo,info->buff,lastkey,
+ info->int_keypos,&info->lastkey_length))
+ DBUG_RETURN(-1);
+ }
+ memcpy(info->lastkey,lastkey,info->lastkey_length);
+ info->lastpos=_mi_dpos(info,0,info->lastkey+info->lastkey_length);
+ DBUG_PRINT("exit",("found key at %lu",(ulong) info->lastpos));
+ DBUG_RETURN(0);
+} /* _mi_search_next */
+
+
+ /* Search after position for the first row in an index */
+ /* This is stored in info->lastpos */
+
+int _mi_search_first(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ register my_off_t pos)
+{
+ uint nod_flag;
+ uchar *page;
+ DBUG_ENTER("_mi_search_first");
+
+ if (pos == HA_OFFSET_ERROR)
+ {
+ my_errno=HA_ERR_KEY_NOT_FOUND;
+ info->lastpos= HA_OFFSET_ERROR;
+ DBUG_RETURN(-1);
+ }
+
+ do
+ {
+ if (!_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,0))
+ {
+ info->lastpos= HA_OFFSET_ERROR;
+ DBUG_RETURN(-1);
+ }
+ nod_flag=mi_test_if_nod(info->buff);
+ page=info->buff+2+nod_flag;
+ } while ((pos=_mi_kpos(nod_flag,page)) != HA_OFFSET_ERROR);
+
+ if (!(info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,
+ info->lastkey)))
+ DBUG_RETURN(-1); /* Crashed */
+
+ info->int_keypos=page; info->int_maxpos=info->buff+mi_getint(info->buff)-1;
+ info->int_nod_flag=nod_flag;
+ info->int_keytree_version=keyinfo->version;
+ info->last_search_keypage=info->last_keypage;
+ info->page_changed=info->buff_used=0;
+ info->lastpos=_mi_dpos(info,0,info->lastkey+info->lastkey_length);
+
+ DBUG_PRINT("exit",("found key at %lu", (ulong) info->lastpos));
+ DBUG_RETURN(0);
+} /* _mi_search_first */
+
+
+ /* Search after position for the last row in an index */
+ /* This is stored in info->lastpos */
+
+int _mi_search_last(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ register my_off_t pos)
+{
+ uint nod_flag;
+ uchar *buff,*page;
+ DBUG_ENTER("_mi_search_last");
+
+ if (pos == HA_OFFSET_ERROR)
+ {
+ my_errno=HA_ERR_KEY_NOT_FOUND; /* Didn't find key */
+ info->lastpos= HA_OFFSET_ERROR;
+ DBUG_RETURN(-1);
+ }
+
+ buff=info->buff;
+ do
+ {
+ if (!_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,buff,0))
+ {
+ info->lastpos= HA_OFFSET_ERROR;
+ DBUG_RETURN(-1);
+ }
+ page= buff+mi_getint(buff);
+ nod_flag=mi_test_if_nod(buff);
+ } while ((pos=_mi_kpos(nod_flag,page)) != HA_OFFSET_ERROR);
+
+ if (!_mi_get_last_key(info,keyinfo,buff,info->lastkey,page,
+ &info->lastkey_length))
+ DBUG_RETURN(-1);
+ info->lastpos=_mi_dpos(info,0,info->lastkey+info->lastkey_length);
+ info->int_keypos=info->int_maxpos=page;
+ info->int_nod_flag=nod_flag;
+ info->int_keytree_version=keyinfo->version;
+ info->last_search_keypage=info->last_keypage;
+ info->page_changed=info->buff_used=0;
+
+ DBUG_PRINT("exit",("found key at %lu",(ulong) info->lastpos));
+ DBUG_RETURN(0);
+} /* _mi_search_last */
+
+
+
+/****************************************************************************
+**
+** Functions to store and pack a key in a page
+**
+** mi_calc_xx_key_length takes the following arguments:
+** nod_flag If nod: Length of nod-pointer
+** next_key Position to pos after the new key in buffer
+** org_key Key that was before the next key in buffer
+** prev_key Last key before current key
+** key Key that will be stored
+** s_temp Information how next key will be packed
+****************************************************************************/
+
+/* Static length key */
+
+int
+_mi_calc_static_key_length(MI_KEYDEF *keyinfo,uint nod_flag,
+ uchar *next_pos __attribute__((unused)),
+ uchar *org_key __attribute__((unused)),
+ uchar *prev_key __attribute__((unused)),
+ uchar *key, MI_KEY_PARAM *s_temp)
+{
+ s_temp->key=key;
+ return (int) (s_temp->totlength=keyinfo->keylength+nod_flag);
+}
+
+/* Variable length key */
+
+int
+_mi_calc_var_key_length(MI_KEYDEF *keyinfo,uint nod_flag,
+ uchar *next_pos __attribute__((unused)),
+ uchar *org_key __attribute__((unused)),
+ uchar *prev_key __attribute__((unused)),
+ uchar *key, MI_KEY_PARAM *s_temp)
+{
+ s_temp->key=key;
+ return (int) (s_temp->totlength=_mi_keylength(keyinfo,key)+nod_flag);
+}
+
+/*
+ length of key with a variable length first segment which is prefix
+ compressed (myisamchk reports 'packed + stripped')
+
+ Keys are compressed the following way:
+
+ If the max length of first key segment <= 127 characters the prefix is
+ 1 byte else it's 2 byte
+
+ prefix byte The high bit is set if this is a prefix for the prev key
+ length Packed length if the previous was a prefix byte
+ [length] Length character of data
+ next-key-seg Next key segments
+
+ If the first segment can have NULL:
+ The length is 0 for NULLS and 1+length for not null columns.
+
+*/
+
+int
+_mi_calc_var_pack_key_length(MI_KEYDEF *keyinfo,uint nod_flag,uchar *next_key,
+ uchar *org_key, uchar *prev_key, uchar *key,
+ MI_KEY_PARAM *s_temp)
+{
+ reg1 HA_KEYSEG *keyseg;
+ int length;
+ uint key_length,ref_length,org_key_length=0,
+ length_pack,new_key_length,diff_flag,pack_marker;
+ uchar *start,*end,*key_end,*sort_order;
+ bool same_length;
+
+ length_pack=s_temp->ref_length=s_temp->n_ref_length=s_temp->n_length=0;
+ same_length=0; keyseg=keyinfo->seg;
+ key_length=_mi_keylength(keyinfo,key)+nod_flag;
+
+ sort_order=0;
+ if ((keyinfo->flag & HA_FULLTEXT) &&
+ ((keyseg->type == HA_KEYTYPE_TEXT) ||
+ (keyseg->type == HA_KEYTYPE_VARTEXT1) ||
+ (keyseg->type == HA_KEYTYPE_VARTEXT2)) &&
+ !use_strnxfrm(keyseg->charset))
+ sort_order=keyseg->charset->sort_order;
+
+ /* diff flag contains how many bytes is needed to pack key */
+ if (keyseg->length >= 127)
+ {
+ diff_flag=2;
+ pack_marker=32768;
+ }
+ else
+ {
+ diff_flag= 1;
+ pack_marker=128;
+ }
+ s_temp->pack_marker=pack_marker;
+
+ /* Handle the case that the first part have NULL values */
+ if (keyseg->flag & HA_NULL_PART)
+ {
+ if (!*key++)
+ {
+ s_temp->key=key;
+ s_temp->ref_length=s_temp->key_length=0;
+ s_temp->totlength=key_length-1+diff_flag;
+ s_temp->next_key_pos=0; /* No next key */
+ return (s_temp->totlength);
+ }
+ s_temp->store_not_null=1;
+ key_length--; /* We don't store NULL */
+ if (prev_key && !*prev_key++)
+ org_key=prev_key=0; /* Can't pack against prev */
+ else if (org_key)
+ org_key++; /* Skip NULL */
+ }
+ else
+ s_temp->store_not_null=0;
+ s_temp->prev_key=org_key;
+
+ /* The key part will start with a packed length */
+
+ get_key_pack_length(new_key_length,length_pack,key);
+ end=key_end= key+ new_key_length;
+ start=key;
+
+ /* Calc how many characters are identical between this and the prev. key */
+ if (prev_key)
+ {
+ get_key_length(org_key_length,prev_key);
+ s_temp->prev_key=prev_key; /* Pointer at data */
+ /* Don't use key-pack if length == 0 */
+ if (new_key_length && new_key_length == org_key_length)
+ same_length=1;
+ else if (new_key_length > org_key_length)
+ end=key + org_key_length;
+
+ if (sort_order) /* SerG */
+ {
+ while (key < end && sort_order[*key] == sort_order[*prev_key])
+ {
+ key++; prev_key++;
+ }
+ }
+ else
+ {
+ while (key < end && *key == *prev_key)
+ {
+ key++; prev_key++;
+ }
+ }
+ }
+
+ s_temp->key=key;
+ s_temp->key_length= (uint) (key_end-key);
+
+ if (same_length && key == key_end)
+ {
+ /* identical variable length key */
+ s_temp->ref_length= pack_marker;
+ length=(int) key_length-(int) (key_end-start)-length_pack;
+ length+= diff_flag;
+ if (next_key)
+ { /* Can't combine with next */
+ s_temp->n_length= *next_key; /* Needed by _mi_store_key */
+ next_key=0;
+ }
+ }
+ else
+ {
+ if (start != key)
+ { /* Starts as prev key */
+ ref_length= (uint) (key-start);
+ s_temp->ref_length= ref_length + pack_marker;
+ length= (int) (key_length - ref_length);
+
+ length-= length_pack;
+ length+= diff_flag;
+ length+= ((new_key_length-ref_length) >= 255) ? 3 : 1;/* Rest_of_key */
+ }
+ else
+ {
+ s_temp->key_length+=s_temp->store_not_null; /* If null */
+ length= key_length - length_pack+ diff_flag;
+ }
+ }
+ s_temp->totlength=(uint) length;
+ s_temp->prev_length=0;
+ DBUG_PRINT("test",("tot_length: %u length: %d uniq_key_length: %u",
+ key_length, length, s_temp->key_length));
+
+ /* If something after that hasn't length=0, test if we can combine */
+ if ((s_temp->next_key_pos=next_key))
+ {
+ uint packed,n_length;
+
+ packed = *next_key & 128;
+ if (diff_flag == 2)
+ {
+ n_length= mi_uint2korr(next_key) & 32767; /* Length of next key */
+ next_key+=2;
+ }
+ else
+ n_length= *next_key++ & 127;
+ if (!packed)
+ n_length-= s_temp->store_not_null;
+
+ if (n_length || packed) /* Don't pack 0 length keys */
+ {
+ uint next_length_pack, new_ref_length=s_temp->ref_length;
+
+ if (packed)
+ {
+ /* If first key and next key is packed (only on delete) */
+ if (!prev_key && org_key)
+ {
+ get_key_length(org_key_length,org_key);
+ key=start;
+ if (sort_order) /* SerG */
+ {
+ while (key < end && sort_order[*key] == sort_order[*org_key])
+ {
+ key++; org_key++;
+ }
+ }
+ else
+ {
+ while (key < end && *key == *org_key)
+ {
+ key++; org_key++;
+ }
+ }
+ if ((new_ref_length= (uint) (key - start)))
+ new_ref_length+=pack_marker;
+ }
+
+ if (!n_length)
+ {
+ /*
+ We put a different key between two identical variable length keys
+ Extend next key to have same prefix as this key
+ */
+ if (new_ref_length) /* prefix of previus key */
+ { /* make next key longer */
+ s_temp->part_of_prev_key= new_ref_length;
+ s_temp->prev_length= org_key_length -
+ (new_ref_length-pack_marker);
+ s_temp->n_ref_length= s_temp->n_length= s_temp->prev_length;
+ n_length= get_pack_length(s_temp->prev_length);
+ s_temp->prev_key+= (new_ref_length - pack_marker);
+ length+= s_temp->prev_length + n_length;
+ }
+ else
+ { /* Can't use prev key */
+ s_temp->part_of_prev_key=0;
+ s_temp->prev_length= org_key_length;
+ s_temp->n_ref_length=s_temp->n_length= org_key_length;
+ length+= org_key_length;
+ /* +get_pack_length(org_key_length); */
+ }
+ return (int) length;
+ }
+
+ ref_length=n_length;
+ get_key_pack_length(n_length,next_length_pack,next_key);
+
+ /* Test if new keys has fewer characters that match the previous key */
+ if (!new_ref_length)
+ { /* Can't use prev key */
+ s_temp->part_of_prev_key= 0;
+ s_temp->prev_length= ref_length;
+ s_temp->n_ref_length= s_temp->n_length= n_length+ref_length;
+ /* s_temp->prev_key+= get_pack_length(org_key_length); */
+ return (int) length+ref_length-next_length_pack;
+ }
+ if (ref_length+pack_marker > new_ref_length)
+ {
+ uint new_pack_length=new_ref_length-pack_marker;
+ /* We must copy characters from the original key to the next key */
+ s_temp->part_of_prev_key= new_ref_length;
+ s_temp->prev_length= ref_length - new_pack_length;
+ s_temp->n_ref_length=s_temp->n_length=n_length + s_temp->prev_length;
+ s_temp->prev_key+= new_pack_length;
+/* +get_pack_length(org_key_length); */
+ length= length-get_pack_length(ref_length)+
+ get_pack_length(new_pack_length);
+ return (int) length + s_temp->prev_length;
+ }
+ }
+ else
+ {
+ /* Next key wasn't a prefix of previous key */
+ ref_length=0;
+ next_length_pack=0;
+ }
+ DBUG_PRINT("test",("length: %d next_key: %p", length, next_key));
+
+ {
+ uint tmp_length;
+ key=(start+=ref_length);
+ if (key+n_length < key_end) /* Normalize length based */
+ key_end=key+n_length;
+ if (sort_order) /* SerG */
+ {
+ while (key < key_end && sort_order[*key] ==
+ sort_order[*next_key])
+ {
+ key++; next_key++;
+ }
+ }
+ else
+ {
+ while (key < key_end && *key == *next_key)
+ {
+ key++; next_key++;
+ }
+ }
+ if (!(tmp_length=(uint) (key-start)))
+ { /* Key can't be re-packed */
+ s_temp->next_key_pos=0;
+ return length;
+ }
+ ref_length+=tmp_length;
+ n_length-=tmp_length;
+ length-=tmp_length+next_length_pack; /* We gained these chars */
+ }
+ if (n_length == 0 && ref_length == new_key_length)
+ {
+ s_temp->n_ref_length=pack_marker; /* Same as prev key */
+ }
+ else
+ {
+ s_temp->n_ref_length=ref_length | pack_marker;
+ length+= get_pack_length(n_length);
+ s_temp->n_length=n_length;
+ }
+ }
+ }
+ return length;
+}
+
+
+/* Length of key which is prefix compressed */
+
+int
+_mi_calc_bin_pack_key_length(MI_KEYDEF *keyinfo,uint nod_flag,uchar *next_key,
+ uchar *org_key, uchar *prev_key, uchar *key,
+ MI_KEY_PARAM *s_temp)
+{
+ uint length,key_length,ref_length;
+
+ s_temp->totlength=key_length=_mi_keylength(keyinfo,key)+nod_flag;
+#ifdef HAVE_purify
+ s_temp->n_length= s_temp->n_ref_length=0; /* For valgrind */
+#endif
+ s_temp->key=key;
+ s_temp->prev_key=org_key;
+ if (prev_key) /* If not first key in block */
+ {
+ /* pack key against previous key */
+ /*
+ As keys may be identical when running a sort in myisamchk, we
+ have to guard against the case where keys may be identical
+ */
+ uchar *end;
+ end=key+key_length;
+ for ( ; *key == *prev_key && key < end; key++,prev_key++) ;
+ s_temp->ref_length= ref_length=(uint) (key-s_temp->key);
+ length=key_length - ref_length + get_pack_length(ref_length);
+ }
+ else
+ {
+ /* No previous key */
+ s_temp->ref_length=ref_length=0;
+ length=key_length+1;
+ }
+ if ((s_temp->next_key_pos=next_key)) /* If another key after */
+ {
+ /* pack key against next key */
+ uint next_length,next_length_pack;
+ get_key_pack_length(next_length,next_length_pack,next_key);
+
+ /* If first key and next key is packed (only on delete) */
+ if (!prev_key && org_key && next_length)
+ {
+ uchar *end;
+ for (key= s_temp->key, end=key+next_length ;
+ *key == *org_key && key < end;
+ key++,org_key++) ;
+ ref_length= (uint) (key - s_temp->key);
+ }
+
+ if (next_length > ref_length)
+ {
+ /* We put a key with different case between two keys with the same prefix
+ Extend next key to have same prefix as
+ this key */
+ s_temp->n_ref_length= ref_length;
+ s_temp->prev_length= next_length-ref_length;
+ s_temp->prev_key+= ref_length;
+ return (int) (length+ s_temp->prev_length - next_length_pack +
+ get_pack_length(ref_length));
+ }
+ /* Check how many characters are identical to next key */
+ key= s_temp->key+next_length;
+ while (*key++ == *next_key++) ;
+ if ((ref_length= (uint) (key - s_temp->key)-1) == next_length)
+ {
+ s_temp->next_key_pos=0;
+ return length; /* can't pack next key */
+ }
+ s_temp->prev_length=0;
+ s_temp->n_ref_length=ref_length;
+ return (int) (length-(ref_length - next_length) - next_length_pack +
+ get_pack_length(ref_length));
+ }
+ return (int) length;
+}
+
+
+/*
+** store a key packed with _mi_calc_xxx_key_length in page-buffert
+*/
+
+/* store key without compression */
+
+void _mi_store_static_key(MI_KEYDEF *keyinfo __attribute__((unused)),
+ register uchar *key_pos,
+ register MI_KEY_PARAM *s_temp)
+{
+ memcpy((byte*) key_pos,(byte*) s_temp->key,(size_t) s_temp->totlength);
+}
+
+
+/* store variable length key with prefix compression */
+
+#define store_pack_length(test,pos,length) { \
+ if (test) { *((pos)++) = (uchar) (length); } else \
+ { *((pos)++) = (uchar) ((length) >> 8); *((pos)++) = (uchar) (length); } }
+
+
+void _mi_store_var_pack_key(MI_KEYDEF *keyinfo __attribute__((unused)),
+ register uchar *key_pos,
+ register MI_KEY_PARAM *s_temp)
+{
+ uint length;
+ uchar *start;
+
+ start=key_pos;
+
+ if (s_temp->ref_length)
+ {
+ /* Packed against previous key */
+ store_pack_length(s_temp->pack_marker == 128,key_pos,s_temp->ref_length);
+ /* If not same key after */
+ if (s_temp->ref_length != s_temp->pack_marker)
+ store_key_length_inc(key_pos,s_temp->key_length);
+ }
+ else
+ {
+ /* Not packed against previous key */
+ store_pack_length(s_temp->pack_marker == 128,key_pos,s_temp->key_length);
+ }
+ bmove((byte*) key_pos,(byte*) s_temp->key,
+ (length=s_temp->totlength-(uint) (key_pos-start)));
+
+ if (!s_temp->next_key_pos) /* No following key */
+ return;
+ key_pos+=length;
+
+ if (s_temp->prev_length)
+ {
+ /* Extend next key because new key didn't have same prefix as prev key */
+ if (s_temp->part_of_prev_key)
+ {
+ store_pack_length(s_temp->pack_marker == 128,key_pos,
+ s_temp->part_of_prev_key);
+ store_key_length_inc(key_pos,s_temp->n_length);
+ }
+ else
+ {
+ s_temp->n_length+= s_temp->store_not_null;
+ store_pack_length(s_temp->pack_marker == 128,key_pos,
+ s_temp->n_length);
+ }
+ memcpy(key_pos, s_temp->prev_key, s_temp->prev_length);
+ }
+ else if (s_temp->n_ref_length)
+ {
+ store_pack_length(s_temp->pack_marker == 128,key_pos,s_temp->n_ref_length);
+ if (s_temp->n_ref_length == s_temp->pack_marker)
+ return; /* Identical key */
+ store_key_length(key_pos,s_temp->n_length);
+ }
+ else
+ {
+ s_temp->n_length+= s_temp->store_not_null;
+ store_pack_length(s_temp->pack_marker == 128,key_pos,s_temp->n_length);
+ }
+}
+
+
+/* variable length key with prefix compression */
+
+void _mi_store_bin_pack_key(MI_KEYDEF *keyinfo __attribute__((unused)),
+ register uchar *key_pos,
+ register MI_KEY_PARAM *s_temp)
+{
+ store_key_length_inc(key_pos,s_temp->ref_length);
+ memcpy((char*) key_pos,(char*) s_temp->key+s_temp->ref_length,
+ (size_t) s_temp->totlength-s_temp->ref_length);
+
+ if (s_temp->next_key_pos)
+ {
+ key_pos+=(uint) (s_temp->totlength-s_temp->ref_length);
+ store_key_length_inc(key_pos,s_temp->n_ref_length);
+ if (s_temp->prev_length) /* If we must extend key */
+ {
+ memcpy(key_pos,s_temp->prev_key,s_temp->prev_length);
+ }
+ }
+}
diff --git a/storage/myisam/mi_static.c b/storage/myisam/mi_static.c
new file mode 100644
index 00000000000..f41aeff8453
--- /dev/null
+++ b/storage/myisam/mi_static.c
@@ -0,0 +1,63 @@
+/* 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 */
+
+/*
+ Static variables for MyISAM library. All definied here for easy making of
+ a shared library
+*/
+
+#ifndef _global_h
+#include "myisamdef.h"
+#endif
+
+LIST *myisam_open_list=0;
+uchar NEAR myisam_file_magic[]=
+{ (uchar) 254, (uchar) 254,'\007', '\001', };
+uchar NEAR myisam_pack_file_magic[]=
+{ (uchar) 254, (uchar) 254,'\010', '\001', };
+my_string myisam_log_filename=(char*) "myisam.log";
+File myisam_log_file= -1;
+uint myisam_quick_table_bits=9;
+uint myisam_block_size=MI_KEY_BLOCK_LENGTH; /* Best by test */
+my_bool myisam_flush=0, myisam_delay_key_write=0, myisam_single_user=0;
+#if defined(THREAD) && !defined(DONT_USE_RW_LOCKS)
+my_bool myisam_concurrent_insert=1;
+#else
+my_bool myisam_concurrent_insert=0;
+#endif
+my_off_t myisam_max_extra_temp_length= (my_off_t)MI_MAX_TEMP_LENGTH;
+my_off_t myisam_max_temp_length= MAX_FILE_SIZE;
+ulong myisam_bulk_insert_tree_size=8192*1024;
+ulong myisam_data_pointer_size=4;
+
+/*
+ read_vec[] is used for converting between P_READ_KEY.. and SEARCH_
+ Position is , == , >= , <= , > , <
+*/
+
+uint NEAR myisam_read_vec[]=
+{
+ SEARCH_FIND, SEARCH_FIND | SEARCH_BIGGER, SEARCH_FIND | SEARCH_SMALLER,
+ SEARCH_NO_FIND | SEARCH_BIGGER, SEARCH_NO_FIND | SEARCH_SMALLER,
+ SEARCH_FIND | SEARCH_PREFIX, SEARCH_LAST, SEARCH_LAST | SEARCH_SMALLER,
+ MBR_CONTAIN, MBR_INTERSECT, MBR_WITHIN, MBR_DISJOINT, MBR_EQUAL
+};
+
+uint NEAR myisam_readnext_vec[]=
+{
+ SEARCH_BIGGER, SEARCH_BIGGER, SEARCH_SMALLER, SEARCH_BIGGER, SEARCH_SMALLER,
+ SEARCH_BIGGER, SEARCH_SMALLER, SEARCH_SMALLER
+};
diff --git a/storage/myisam/mi_statrec.c b/storage/myisam/mi_statrec.c
new file mode 100644
index 00000000000..8f5cde45e24
--- /dev/null
+++ b/storage/myisam/mi_statrec.c
@@ -0,0 +1,301 @@
+/* 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 */
+
+ /* Functions to handle fixed-length-records */
+
+#include "myisamdef.h"
+
+
+int _mi_write_static_record(MI_INFO *info, const byte *record)
+{
+ uchar temp[8]; /* max pointer length */
+
+ if (info->s->state.dellink != HA_OFFSET_ERROR)
+ {
+ my_off_t filepos=info->s->state.dellink;
+ info->rec_cache.seek_not_done=1; /* We have done a seek */
+ if (my_pread(info->dfile,(char*) &temp[0],info->s->base.rec_reflength,
+ info->s->state.dellink+1,
+ MYF(MY_NABP)))
+ goto err;
+ info->s->state.dellink= _mi_rec_pos(info->s,temp);
+ info->state->del--;
+ info->state->empty-=info->s->base.pack_reclength;
+ if (my_pwrite(info->dfile, (char*) record, info->s->base.reclength,
+ filepos,
+ MYF(MY_NABP)))
+ goto err;
+ }
+ else
+ {
+ if (info->state->data_file_length > info->s->base.max_data_file_length-
+ info->s->base.pack_reclength)
+ {
+ my_errno=HA_ERR_RECORD_FILE_FULL;
+ return(2);
+ }
+ if (info->opt_flag & WRITE_CACHE_USED)
+ { /* Cash in use */
+ if (my_b_write(&info->rec_cache, (byte*) record,
+ info->s->base.reclength))
+ goto err;
+ if (info->s->base.pack_reclength != info->s->base.reclength)
+ {
+ uint length=info->s->base.pack_reclength - info->s->base.reclength;
+ bzero((char*) temp,length);
+ if (my_b_write(&info->rec_cache, (byte*) temp,length))
+ goto err;
+ }
+ }
+ else
+ {
+ info->rec_cache.seek_not_done=1; /* We have done a seek */
+ if (my_pwrite(info->dfile,(char*) record,info->s->base.reclength,
+ info->state->data_file_length,
+ info->s->write_flag))
+ goto err;
+ if (info->s->base.pack_reclength != info->s->base.reclength)
+ {
+ uint length=info->s->base.pack_reclength - info->s->base.reclength;
+ bzero((char*) temp,length);
+ if (my_pwrite(info->dfile, (byte*) temp,length,
+ info->state->data_file_length+
+ info->s->base.reclength,
+ info->s->write_flag))
+ goto err;
+ }
+ }
+ info->state->data_file_length+=info->s->base.pack_reclength;
+ info->s->state.split++;
+ }
+ return 0;
+ err:
+ return 1;
+}
+
+int _mi_update_static_record(MI_INFO *info, my_off_t pos, const byte *record)
+{
+ info->rec_cache.seek_not_done=1; /* We have done a seek */
+ return (my_pwrite(info->dfile,
+ (char*) record,info->s->base.reclength,
+ pos,
+ MYF(MY_NABP)) != 0);
+}
+
+
+int _mi_delete_static_record(MI_INFO *info)
+{
+ uchar temp[9]; /* 1+sizeof(uint32) */
+
+ info->state->del++;
+ info->state->empty+=info->s->base.pack_reclength;
+ temp[0]= '\0'; /* Mark that record is deleted */
+ _mi_dpointer(info,temp+1,info->s->state.dellink);
+ info->s->state.dellink = info->lastpos;
+ info->rec_cache.seek_not_done=1;
+ return (my_pwrite(info->dfile,(byte*) temp, 1+info->s->rec_reflength,
+ info->lastpos, MYF(MY_NABP)) != 0);
+}
+
+
+int _mi_cmp_static_record(register MI_INFO *info, register const byte *old)
+{
+ DBUG_ENTER("_mi_cmp_static_record");
+
+ /* We are going to do changes; dont let anybody disturb */
+ dont_break(); /* Dont allow SIGHUP or SIGINT */
+
+ if (info->opt_flag & WRITE_CACHE_USED)
+ {
+ if (flush_io_cache(&info->rec_cache))
+ {
+ DBUG_RETURN(-1);
+ }
+ info->rec_cache.seek_not_done=1; /* We have done a seek */
+ }
+
+ if ((info->opt_flag & READ_CHECK_USED))
+ { /* If check isn't disabled */
+ info->rec_cache.seek_not_done=1; /* We have done a seek */
+ if (my_pread(info->dfile, (char*) info->rec_buff, info->s->base.reclength,
+ info->lastpos,
+ MYF(MY_NABP)))
+ DBUG_RETURN(-1);
+ if (memcmp((byte*) info->rec_buff, (byte*) old,
+ (uint) info->s->base.reclength))
+ {
+ DBUG_DUMP("read",old,info->s->base.reclength);
+ DBUG_DUMP("disk",info->rec_buff,info->s->base.reclength);
+ my_errno=HA_ERR_RECORD_CHANGED; /* Record have changed */
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+int _mi_cmp_static_unique(MI_INFO *info, MI_UNIQUEDEF *def,
+ const byte *record, my_off_t pos)
+{
+ DBUG_ENTER("_mi_cmp_static_unique");
+
+ info->rec_cache.seek_not_done=1; /* We have done a seek */
+ if (my_pread(info->dfile, (char*) info->rec_buff, info->s->base.reclength,
+ pos, MYF(MY_NABP)))
+ DBUG_RETURN(-1);
+ DBUG_RETURN(mi_unique_comp(def, record, info->rec_buff,
+ def->null_are_equal));
+}
+
+
+ /* Read a fixed-length-record */
+ /* Returns 0 if Ok. */
+ /* 1 if record is deleted */
+ /* MY_FILE_ERROR on read-error or locking-error */
+
+int _mi_read_static_record(register MI_INFO *info, register my_off_t pos,
+ register byte *record)
+{
+ int error;
+
+ if (pos != HA_OFFSET_ERROR)
+ {
+ if (info->opt_flag & WRITE_CACHE_USED &&
+ info->rec_cache.pos_in_file <= pos &&
+ flush_io_cache(&info->rec_cache))
+ return(-1);
+ info->rec_cache.seek_not_done=1; /* We have done a seek */
+
+ error=my_pread(info->dfile,(char*) record,info->s->base.reclength,
+ pos,MYF(MY_NABP)) != 0;
+ fast_mi_writeinfo(info);
+ if (! error)
+ {
+ if (!*record)
+ {
+ my_errno=HA_ERR_RECORD_DELETED;
+ return(1); /* Record is deleted */
+ }
+ info->update|= HA_STATE_AKTIV; /* Record is read */
+ return(0);
+ }
+ return(-1); /* Error on read */
+ }
+ fast_mi_writeinfo(info); /* No such record */
+ return(-1);
+}
+
+
+
+int _mi_read_rnd_static_record(MI_INFO *info, byte *buf,
+ register my_off_t filepos,
+ my_bool skip_deleted_blocks)
+{
+ int locked,error,cache_read;
+ uint cache_length;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_read_rnd_static_record");
+
+ cache_read=0;
+ cache_length=0;
+ if (info->opt_flag & WRITE_CACHE_USED &&
+ (info->rec_cache.pos_in_file <= filepos || skip_deleted_blocks) &&
+ flush_io_cache(&info->rec_cache))
+ DBUG_RETURN(my_errno);
+ if (info->opt_flag & READ_CACHE_USED)
+ { /* Cache in use */
+ if (filepos == my_b_tell(&info->rec_cache) &&
+ (skip_deleted_blocks || !filepos))
+ {
+ cache_read=1; /* Read record using cache */
+ cache_length=(uint) (info->rec_cache.read_end - info->rec_cache.read_pos);
+ }
+ else
+ info->rec_cache.seek_not_done=1; /* Filepos is changed */
+ }
+ locked=0;
+ if (info->lock_type == F_UNLCK)
+ {
+ if (filepos >= info->state->data_file_length)
+ { /* Test if new records */
+ if (_mi_readinfo(info,F_RDLCK,0))
+ DBUG_RETURN(my_errno);
+ locked=1;
+ }
+ else
+ { /* We don't nead new info */
+#ifndef UNSAFE_LOCKING
+ if ((! cache_read || share->base.reclength > cache_length) &&
+ share->tot_locks == 0)
+ { /* record not in cache */
+ if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE) | info->lock_wait))
+ DBUG_RETURN(my_errno);
+ locked=1;
+ }
+#else
+ info->tmp_lock_type=F_RDLCK;
+#endif
+ }
+ }
+ if (filepos >= info->state->data_file_length)
+ {
+ DBUG_PRINT("test",("filepos: %ld (%ld) records: %ld del: %ld",
+ filepos/share->base.reclength,filepos,
+ info->state->records, info->state->del));
+ fast_mi_writeinfo(info);
+ DBUG_RETURN(my_errno=HA_ERR_END_OF_FILE);
+ }
+ info->lastpos= filepos;
+ info->nextpos= filepos+share->base.pack_reclength;
+
+ if (! cache_read) /* No cacheing */
+ {
+ if ((error=_mi_read_static_record(info,filepos,buf)))
+ {
+ if (error > 0)
+ error=my_errno=HA_ERR_RECORD_DELETED;
+ else
+ error=my_errno;
+ }
+ DBUG_RETURN(error);
+ }
+
+ /* Read record with cacheing */
+ error=my_b_read(&info->rec_cache,(byte*) buf,share->base.reclength);
+ if (info->s->base.pack_reclength != info->s->base.reclength && !error)
+ {
+ char tmp[8]; /* Skill fill bytes */
+ error=my_b_read(&info->rec_cache,(byte*) tmp,
+ info->s->base.pack_reclength - info->s->base.reclength);
+ }
+ if (locked)
+ VOID(_mi_writeinfo(info,0)); /* Unlock keyfile */
+ if (!error)
+ {
+ if (!buf[0])
+ { /* Record is removed */
+ DBUG_RETURN(my_errno=HA_ERR_RECORD_DELETED);
+ }
+ /* Found and may be updated */
+ info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
+ DBUG_RETURN(0);
+ }
+ /* my_errno should be set if rec_cache.error == -1 */
+ if (info->rec_cache.error != -1 || my_errno == 0)
+ my_errno=HA_ERR_WRONG_IN_RECORD;
+ DBUG_RETURN(my_errno); /* Something wrong (EOF?) */
+}
diff --git a/storage/myisam/mi_test1.c b/storage/myisam/mi_test1.c
new file mode 100644
index 00000000000..aa6cd98ac8e
--- /dev/null
+++ b/storage/myisam/mi_test1.c
@@ -0,0 +1,674 @@
+/* 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 */
+
+/* Testing of the basic functions of a MyISAM table */
+
+#include "myisam.h"
+#include <my_getopt.h>
+#include <m_string.h>
+
+#define MAX_REC_LENGTH 1024
+
+static void usage();
+
+static int rec_pointer_size=0, flags[50];
+static int key_field=FIELD_SKIP_PRESPACE,extra_field=FIELD_SKIP_ENDSPACE;
+static int key_type=HA_KEYTYPE_NUM;
+static int create_flag=0;
+
+static uint insert_count, update_count, remove_count;
+static uint pack_keys=0, pack_seg=0, key_length;
+static uint unique_key=HA_NOSAME;
+static my_bool key_cacheing, null_fields, silent, skip_update, opt_unique,
+ verbose;
+static MI_COLUMNDEF recinfo[4];
+static MI_KEYDEF keyinfo[10];
+static HA_KEYSEG keyseg[10];
+static HA_KEYSEG uniqueseg[10];
+
+static int run_test(const char *filename);
+static void get_options(int argc, char *argv[]);
+static void create_key(char *key,uint rownr);
+static void create_record(char *record,uint rownr);
+static void update_record(char *record);
+
+int main(int argc,char *argv[])
+{
+ MY_INIT(argv[0]);
+ my_init();
+ if (key_cacheing)
+ init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,IO_SIZE*16,0,0);
+ get_options(argc,argv);
+
+ exit(run_test("test1"));
+}
+
+
+static int run_test(const char *filename)
+{
+ MI_INFO *file;
+ int i,j,error,deleted,rec_length,uniques=0;
+ ha_rows found,row_count;
+ my_off_t pos;
+ char record[MAX_REC_LENGTH],key[MAX_REC_LENGTH],read_record[MAX_REC_LENGTH];
+ MI_UNIQUEDEF uniquedef;
+ MI_CREATE_INFO create_info;
+
+ bzero((char*) recinfo,sizeof(recinfo));
+
+ /* First define 2 columns */
+ recinfo[0].type=FIELD_NORMAL; recinfo[0].length=1; /* For NULL bits */
+ recinfo[1].type=key_field;
+ recinfo[1].length= (key_field == FIELD_BLOB ? 4+mi_portable_sizeof_char_ptr :
+ key_length);
+ if (key_field == FIELD_VARCHAR)
+ recinfo[1].length+= HA_VARCHAR_PACKLENGTH(key_length);;
+ recinfo[2].type=extra_field;
+ recinfo[2].length= (extra_field == FIELD_BLOB ? 4 + mi_portable_sizeof_char_ptr : 24);
+ if (extra_field == FIELD_VARCHAR)
+ recinfo[2].length+= HA_VARCHAR_PACKLENGTH(recinfo[2].length);
+ if (opt_unique)
+ {
+ recinfo[3].type=FIELD_CHECK;
+ recinfo[3].length=MI_UNIQUE_HASH_LENGTH;
+ }
+ rec_length=recinfo[0].length+recinfo[1].length+recinfo[2].length+
+ recinfo[3].length;
+
+ if (key_type == HA_KEYTYPE_VARTEXT1 &&
+ key_length > 255)
+ key_type= HA_KEYTYPE_VARTEXT2;
+
+ /* Define a key over the first column */
+ keyinfo[0].seg=keyseg;
+ keyinfo[0].keysegs=1;
+ keyinfo[0].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[0].seg[0].type= key_type;
+ keyinfo[0].seg[0].flag= pack_seg;
+ keyinfo[0].seg[0].start=1;
+ keyinfo[0].seg[0].length=key_length;
+ keyinfo[0].seg[0].null_bit= null_fields ? 2 : 0;
+ keyinfo[0].seg[0].null_pos=0;
+ keyinfo[0].seg[0].language= default_charset_info->number;
+ if (pack_seg & HA_BLOB_PART)
+ {
+ keyinfo[0].seg[0].bit_start=4; /* Length of blob length */
+ }
+ keyinfo[0].flag = (uint8) (pack_keys | unique_key);
+
+ bzero((byte*) flags,sizeof(flags));
+ if (opt_unique)
+ {
+ uint start;
+ uniques=1;
+ bzero((char*) &uniquedef,sizeof(uniquedef));
+ bzero((char*) uniqueseg,sizeof(uniqueseg));
+ uniquedef.seg=uniqueseg;
+ uniquedef.keysegs=2;
+
+ /* Make a unique over all columns (except first NULL fields) */
+ for (i=0, start=1 ; i < 2 ; i++)
+ {
+ uniqueseg[i].start=start;
+ start+=recinfo[i+1].length;
+ uniqueseg[i].length=recinfo[i+1].length;
+ uniqueseg[i].language= default_charset_info->number;
+ }
+ uniqueseg[0].type= key_type;
+ uniqueseg[0].null_bit= null_fields ? 2 : 0;
+ uniqueseg[1].type= HA_KEYTYPE_TEXT;
+ if (extra_field == FIELD_BLOB)
+ {
+ uniqueseg[1].length=0; /* The whole blob */
+ uniqueseg[1].bit_start=4; /* long blob */
+ uniqueseg[1].flag|= HA_BLOB_PART;
+ }
+ else if (extra_field == FIELD_VARCHAR)
+ uniqueseg[1].flag|= HA_VAR_LENGTH_PART;
+ }
+ else
+ uniques=0;
+
+ if (!silent)
+ printf("- Creating isam-file\n");
+ bzero((char*) &create_info,sizeof(create_info));
+ create_info.max_rows=(ulong) (rec_pointer_size ?
+ (1L << (rec_pointer_size*8))/40 :
+ 0);
+ if (mi_create(filename,1,keyinfo,3+opt_unique,recinfo,
+ uniques, &uniquedef, &create_info,
+ create_flag))
+ goto err;
+ if (!(file=mi_open(filename,2,HA_OPEN_ABORT_IF_LOCKED)))
+ goto err;
+ if (!silent)
+ printf("- Writing key:s\n");
+
+ my_errno=0;
+ row_count=deleted=0;
+ for (i=49 ; i>=1 ; i-=2 )
+ {
+ if (insert_count-- == 0) { VOID(mi_close(file)) ; exit(0) ; }
+ j=i%25 +1;
+ create_record(record,j);
+ error=mi_write(file,record);
+ if (!error)
+ row_count++;
+ flags[j]=1;
+ if (verbose || error)
+ printf("J= %2d mi_write: %d errno: %d\n", j,error,my_errno);
+ }
+
+ /* Insert 2 rows with null values */
+ if (null_fields)
+ {
+ create_record(record,0);
+ error=mi_write(file,record);
+ if (!error)
+ row_count++;
+ if (verbose || error)
+ printf("J= NULL mi_write: %d errno: %d\n", error,my_errno);
+ error=mi_write(file,record);
+ if (!error)
+ row_count++;
+ if (verbose || error)
+ printf("J= NULL mi_write: %d errno: %d\n", error,my_errno);
+ flags[0]=2;
+ }
+
+ if (!skip_update)
+ {
+ if (opt_unique)
+ {
+ if (!silent)
+ printf("- Checking unique constraint\n");
+ create_record(record,j);
+ if (!mi_write(file,record) || my_errno != HA_ERR_FOUND_DUPP_UNIQUE)
+ {
+ printf("unique check failed\n");
+ }
+ }
+ if (!silent)
+ printf("- Updating rows\n");
+
+ /* Update first last row to force extend of file */
+ if (mi_rsame(file,read_record,-1))
+ {
+ printf("Can't find last row with mi_rsame\n");
+ }
+ else
+ {
+ memcpy(record,read_record,rec_length);
+ update_record(record);
+ if (mi_update(file,read_record,record))
+ {
+ printf("Can't update last row: %.*s\n",
+ keyinfo[0].seg[0].length,read_record+1);
+ }
+ }
+
+ /* Read through all rows and update them */
+ pos=(my_off_t) 0;
+ found=0;
+ while ((error=mi_rrnd(file,read_record,pos)) == 0)
+ {
+ if (update_count-- == 0) { VOID(mi_close(file)) ; exit(0) ; }
+ memcpy(record,read_record,rec_length);
+ update_record(record);
+ if (mi_update(file,read_record,record))
+ {
+ printf("Can't update row: %.*s, error: %d\n",
+ keyinfo[0].seg[0].length,record+1,my_errno);
+ }
+ found++;
+ pos=HA_OFFSET_ERROR;
+ }
+ if (found != row_count)
+ printf("Found %ld of %ld rows\n", found,row_count);
+ }
+
+ if (!silent)
+ printf("- Reopening file\n");
+ if (mi_close(file)) goto err;
+ if (!(file=mi_open(filename,2,HA_OPEN_ABORT_IF_LOCKED))) goto err;
+ if (!skip_update)
+ {
+ if (!silent)
+ printf("- Removing keys\n");
+
+ for (i=0 ; i <= 10 ; i++)
+ {
+ /* testing */
+ if (remove_count-- == 0) { VOID(mi_close(file)) ; exit(0) ; }
+ j=i*2;
+ if (!flags[j])
+ continue;
+ create_key(key,j);
+ my_errno=0;
+ if ((error = mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT)))
+ {
+ if (verbose || (flags[j] >= 1 ||
+ (error && my_errno != HA_ERR_KEY_NOT_FOUND)))
+ printf("key: '%.*s' mi_rkey: %3d errno: %3d\n",
+ (int) key_length,key+test(null_fields),error,my_errno);
+ }
+ else
+ {
+ error=mi_delete(file,read_record);
+ if (verbose || error)
+ printf("key: '%.*s' mi_delete: %3d errno: %3d\n",
+ (int) key_length, key+test(null_fields), error, my_errno);
+ if (! error)
+ {
+ deleted++;
+ flags[j]--;
+ }
+ }
+ }
+ }
+ if (!silent)
+ printf("- Reading rows with key\n");
+ for (i=0 ; i <= 25 ; i++)
+ {
+ create_key(key,i);
+ my_errno=0;
+ error=mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT);
+ if (verbose ||
+ (error == 0 && flags[i] == 0 && unique_key) ||
+ (error && (flags[i] != 0 || my_errno != HA_ERR_KEY_NOT_FOUND)))
+ {
+ printf("key: '%.*s' mi_rkey: %3d errno: %3d record: %s\n",
+ (int) key_length,key+test(null_fields),error,my_errno,record+1);
+ }
+ }
+
+ if (!silent)
+ printf("- Reading rows with position\n");
+ for (i=1,found=0 ; i <= 30 ; i++)
+ {
+ my_errno=0;
+ if ((error=mi_rrnd(file,read_record,i == 1 ? 0L : HA_OFFSET_ERROR)) == -1)
+ {
+ if (found != row_count-deleted)
+ printf("Found only %ld of %ld rows\n",found,row_count-deleted);
+ break;
+ }
+ if (!error)
+ found++;
+ if (verbose || (error != 0 && error != HA_ERR_RECORD_DELETED &&
+ error != HA_ERR_END_OF_FILE))
+ {
+ printf("pos: %2d mi_rrnd: %3d errno: %3d record: %s\n",
+ i-1,error,my_errno,read_record+1);
+ }
+ }
+ if (mi_close(file)) goto err;
+ my_end(MY_CHECK_ERROR);
+
+ return (0);
+err:
+ printf("got error: %3d when using myisam-database\n",my_errno);
+ return 1; /* skip warning */
+}
+
+
+static void create_key_part(char *key,uint rownr)
+{
+ if (!unique_key)
+ rownr&=7; /* Some identical keys */
+ if (keyinfo[0].seg[0].type == HA_KEYTYPE_NUM)
+ {
+ sprintf(key,"%*d",keyinfo[0].seg[0].length,rownr);
+ }
+ else if (keyinfo[0].seg[0].type == HA_KEYTYPE_VARTEXT1 ||
+ keyinfo[0].seg[0].type == HA_KEYTYPE_VARTEXT2)
+ { /* Alpha record */
+ /* Create a key that may be easily packed */
+ bfill(key,keyinfo[0].seg[0].length,rownr < 10 ? 'A' : 'B');
+ sprintf(key+keyinfo[0].seg[0].length-2,"%-2d",rownr);
+ if ((rownr & 7) == 0)
+ {
+ /* Change the key to force a unpack of the next key */
+ bfill(key+3,keyinfo[0].seg[0].length-4,rownr < 10 ? 'a' : 'b');
+ }
+ }
+ else
+ { /* Alpha record */
+ if (keyinfo[0].seg[0].flag & HA_SPACE_PACK)
+ sprintf(key,"%-*d",keyinfo[0].seg[0].length,rownr);
+ else
+ {
+ /* Create a key that may be easily packed */
+ bfill(key,keyinfo[0].seg[0].length,rownr < 10 ? 'A' : 'B');
+ sprintf(key+keyinfo[0].seg[0].length-2,"%-2d",rownr);
+ if ((rownr & 7) == 0)
+ {
+ /* Change the key to force a unpack of the next key */
+ key[1]= (rownr < 10 ? 'a' : 'b');
+ }
+ }
+ }
+}
+
+
+static void create_key(char *key,uint rownr)
+{
+ if (keyinfo[0].seg[0].null_bit)
+ {
+ if (rownr == 0)
+ {
+ key[0]=1; /* null key */
+ key[1]=0; /* Fore easy print of key */
+ return;
+ }
+ *key++=0;
+ }
+ if (keyinfo[0].seg[0].flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART))
+ {
+ uint tmp;
+ create_key_part(key+2,rownr);
+ tmp=strlen(key+2);
+ int2store(key,tmp);
+ }
+ else
+ create_key_part(key,rownr);
+}
+
+
+static char blob_key[MAX_REC_LENGTH];
+static char blob_record[MAX_REC_LENGTH+20*20];
+
+
+static void create_record(char *record,uint rownr)
+{
+ char *pos;
+ bzero((char*) record,MAX_REC_LENGTH);
+ record[0]=1; /* delete marker */
+ if (rownr == 0 && keyinfo[0].seg[0].null_bit)
+ record[0]|=keyinfo[0].seg[0].null_bit; /* Null key */
+
+ pos=record+1;
+ if (recinfo[1].type == FIELD_BLOB)
+ {
+ uint tmp;
+ char *ptr;
+ create_key_part(blob_key,rownr);
+ 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, pack_length= HA_VARCHAR_PACKLENGTH(recinfo[1].length-1);
+ create_key_part(pos+pack_length,rownr);
+ tmp= strlen(pos+pack_length);
+ if (pack_length == 1)
+ *(uchar*) pos= (uchar) tmp;
+ else
+ int2store(pos,tmp);
+ pos+= recinfo[1].length;
+ }
+ else
+ {
+ create_key_part(pos,rownr);
+ pos+=recinfo[1].length;
+ }
+ if (recinfo[2].type == FIELD_BLOB)
+ {
+ uint tmp;
+ char *ptr;;
+ sprintf(blob_record,"... row: %d", rownr);
+ strappend(blob_record,max(MAX_REC_LENGTH-rownr,10),' ');
+ tmp=strlen(blob_record);
+ int4store(pos,tmp);
+ ptr=blob_record;
+ memcpy_fixed(pos+4,&ptr,sizeof(char*));
+ }
+ else if (recinfo[2].type == FIELD_VARCHAR)
+ {
+ uint tmp, pack_length= HA_VARCHAR_PACKLENGTH(recinfo[1].length-1);
+ sprintf(pos+pack_length, "... row: %d", rownr);
+ tmp= strlen(pos+pack_length);
+ if (pack_length == 1)
+ *(uchar*) pos= (uchar) tmp;
+ else
+ int2store(pos,tmp);
+ }
+ else
+ {
+ sprintf(pos,"... row: %d", rownr);
+ strappend(pos,recinfo[2].length,' ');
+ }
+}
+
+/* change row to test re-packing of rows and reallocation of keys */
+
+static void update_record(char *record)
+{
+ char *pos=record+1;
+ if (recinfo[1].type == FIELD_BLOB)
+ {
+ char *column,*ptr;
+ int length;
+ length=uint4korr(pos); /* Long blob */
+ memcpy_fixed(&column,pos+4,sizeof(char*));
+ memcpy(blob_key,column,length); /* Move old key */
+ ptr=blob_key;
+ memcpy_fixed(pos+4,&ptr,sizeof(char*)); /* Store pointer to new key */
+ if (keyinfo[0].seg[0].type != HA_KEYTYPE_NUM)
+ my_casedn(default_charset_info,blob_key,length);
+ pos+=recinfo[1].length;
+ }
+ else if (recinfo[1].type == FIELD_VARCHAR)
+ {
+ uint pack_length= HA_VARCHAR_PACKLENGTH(recinfo[1].length-1);
+ uint length= pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos);
+ my_casedn(default_charset_info,pos+pack_length,length);
+ pos+=recinfo[1].length;
+ }
+ else
+ {
+ if (keyinfo[0].seg[0].type != HA_KEYTYPE_NUM)
+ my_casedn(default_charset_info,pos,keyinfo[0].seg[0].length);
+ pos+=recinfo[1].length;
+ }
+
+ if (recinfo[2].type == FIELD_BLOB)
+ {
+ char *column;
+ int length;
+ length=uint4korr(pos);
+ memcpy_fixed(&column,pos+4,sizeof(char*));
+ memcpy(blob_record,column,length);
+ bfill(blob_record+length,20,'.'); /* Make it larger */
+ length+=20;
+ int4store(pos,length);
+ column=blob_record;
+ memcpy_fixed(pos+4,&column,sizeof(char*));
+ }
+ else if (recinfo[2].type == FIELD_VARCHAR)
+ {
+ /* Second field is longer than 10 characters */
+ uint pack_length= HA_VARCHAR_PACKLENGTH(recinfo[1].length-1);
+ uint length= pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos);
+ bfill(pos+pack_length+length,recinfo[2].length-length-pack_length,'.');
+ length=recinfo[2].length-pack_length;
+ if (pack_length == 1)
+ *(uchar*) pos= (uchar) length;
+ else
+ int2store(pos,length);
+ }
+ else
+ {
+ bfill(pos+recinfo[2].length-10,10,'.');
+ }
+}
+
+
+static struct my_option my_long_options[] =
+{
+ {"checksum", 'c', "Undocumented",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifndef DBUG_OFF
+ {"debug", '#', "Undocumented",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"delete_rows", 'd', "Undocumented", (gptr*) &remove_count,
+ (gptr*) &remove_count, 0, GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0},
+ {"help", '?', "Display help and exit",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"insert_rows", 'i', "Undocumented", (gptr*) &insert_count,
+ (gptr*) &insert_count, 0, GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0},
+ {"key_alpha", 'a', "Use a key of type HA_KEYTYPE_TEXT",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"key_binary_pack", 'B', "Undocumented",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"key_blob", 'b', "Undocumented",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"key_cache", 'K', "Undocumented", (gptr*) &key_cacheing,
+ (gptr*) &key_cacheing, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"key_length", 'k', "Undocumented", (gptr*) &key_length, (gptr*) &key_length,
+ 0, GET_UINT, REQUIRED_ARG, 6, 0, 0, 0, 0, 0},
+ {"key_multiple", 'm', "Undocumented",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"key_prefix_pack", 'P', "Undocumented",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"key_space_pack", 'p', "Undocumented",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"key_varchar", 'w', "Test VARCHAR keys",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"null_fields", 'N', "Define fields with NULL",
+ (gptr*) &null_fields, (gptr*) &null_fields, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"row_fixed_size", 'S', "Undocumented",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"row_pointer_size", 'R', "Undocumented", (gptr*) &rec_pointer_size,
+ (gptr*) &rec_pointer_size, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"silent", 's', "Undocumented",
+ (gptr*) &silent, (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"skip_update", 'U', "Undocumented", (gptr*) &skip_update,
+ (gptr*) &skip_update, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"unique", 'C', "Undocumented", (gptr*) &opt_unique, (gptr*) &opt_unique, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"update_rows", 'u', "Undocumented", (gptr*) &update_count,
+ (gptr*) &update_count, 0, GET_UINT, REQUIRED_ARG, 1000, 0, 0, 0, 0, 0},
+ {"verbose", 'v', "Be more verbose", (gptr*) &verbose, (gptr*) &verbose, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V', "Print version number and exit",
+ 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}
+};
+
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ switch(optid) {
+ case 'a':
+ key_type= HA_KEYTYPE_TEXT;
+ break;
+ case 'c':
+ create_flag|= HA_CREATE_CHECKSUM;
+ break;
+ case 'R': /* Length of record pointer */
+ if (rec_pointer_size > 3)
+ rec_pointer_size=0;
+ break;
+ case 'P':
+ pack_keys= HA_PACK_KEY; /* Use prefix compression */
+ break;
+ case 'B':
+ pack_keys= HA_BINARY_PACK_KEY; /* Use binary compression */
+ break;
+ case 'S':
+ if (key_field == FIELD_VARCHAR)
+ {
+ create_flag=0; /* Static sized varchar */
+ }
+ else if (key_field != FIELD_BLOB)
+ {
+ key_field=FIELD_NORMAL; /* static-size record */
+ extra_field=FIELD_NORMAL;
+ }
+ break;
+ case 'p':
+ pack_keys=HA_PACK_KEY; /* Use prefix + space packing */
+ pack_seg=HA_SPACE_PACK;
+ key_type=HA_KEYTYPE_TEXT;
+ break;
+ case 'm':
+ unique_key=0;
+ break;
+ case 'b':
+ key_field=FIELD_BLOB; /* blob key */
+ extra_field= FIELD_BLOB;
+ pack_seg|= HA_BLOB_PART;
+ key_type= HA_KEYTYPE_VARTEXT1;
+ break;
+ case 'k':
+ if (key_length < 4 || key_length > MI_MAX_KEY_LENGTH)
+ {
+ fprintf(stderr,"Wrong key length\n");
+ exit(1);
+ }
+ break;
+ case 'w':
+ key_field=FIELD_VARCHAR; /* varchar keys */
+ extra_field= FIELD_VARCHAR;
+ key_type= HA_KEYTYPE_VARTEXT1;
+ pack_seg|= HA_VAR_LENGTH_PART;
+ create_flag|= HA_PACK_RECORD;
+ break;
+ case 'K': /* Use key cacheing */
+ key_cacheing=1;
+ break;
+ case 'V':
+ printf("test1 Ver 1.2 \n");
+ exit(0);
+ case '#':
+ DEBUGGER_ON;
+ DBUG_PUSH (argument);
+ break;
+ case '?':
+ 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("Usage: %s [options]\n\n", my_progname);
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
diff --git a/storage/myisam/mi_test2.c b/storage/myisam/mi_test2.c
new file mode 100644
index 00000000000..95c8ce56a13
--- /dev/null
+++ b/storage/myisam/mi_test2.c
@@ -0,0 +1,1048 @@
+/* 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 */
+
+/* Test av isam-databas: stor test */
+
+#ifndef USE_MY_FUNC /* We want to be able to dbug this !! */
+#define USE_MY_FUNC
+#endif
+#ifdef DBUG_OFF
+#undef DBUG_OFF
+#endif
+#ifndef SAFEMALLOC
+#define SAFEMALLOC
+#endif
+#include "myisamdef.h"
+#include <m_ctype.h>
+
+#define STANDARD_LENGTH 37
+#define MYISAM_KEYS 6
+#define MAX_PARTS 4
+#if !defined(MSDOS) && !defined(labs)
+#define labs(a) abs(a)
+#endif
+
+static void get_options(int argc, char *argv[]);
+static uint rnd(uint max_value);
+static void fix_length(byte *record,uint length);
+static void put_blob_in_record(char *blob_pos,char **blob_buffer);
+static void copy_key(struct st_myisam_info *info,uint inx,
+ uchar *record,uchar *key);
+
+static int verbose=0,testflag=0,
+ first_key=0,async_io=0,key_cacheing=0,write_cacheing=0,locking=0,
+ rec_pointer_size=0,pack_fields=1,use_log=0,silent=0,
+ opt_quick_mode=0;
+static int pack_seg=HA_SPACE_PACK,pack_type=HA_PACK_KEY,remove_count=-1,
+ create_flag=0;
+static ulong key_cache_size=IO_SIZE*16;
+static uint key_cache_block_size= KEY_CACHE_BLOCK_SIZE;
+
+static uint keys=MYISAM_KEYS,recant=1000;
+static uint use_blob=0;
+static uint16 key1[1001],key3[5000];
+static char record[300],record2[300],key[100],key2[100],
+ read_record[300],read_record2[300],read_record3[300];
+static HA_KEYSEG glob_keyseg[MYISAM_KEYS][MAX_PARTS];
+
+ /* Test program */
+
+int main(int argc, char *argv[])
+{
+ uint i;
+ int j,n1,n2,n3,error,k;
+ uint write_count,update,dupp_keys,opt_delete,start,length,blob_pos,
+ reclength,ant,found_parts;
+ my_off_t lastpos;
+ ha_rows range_records,records;
+ MI_INFO *file;
+ MI_KEYDEF keyinfo[10];
+ MI_COLUMNDEF recinfo[10];
+ MI_ISAMINFO info;
+ const char *filename;
+ char *blob_buffer;
+ MI_CREATE_INFO create_info;
+ MY_INIT(argv[0]);
+
+ filename= "test2";
+ get_options(argc,argv);
+ if (! async_io)
+ my_disable_async_io=1;
+
+ reclength=STANDARD_LENGTH+60+(use_blob ? 8 : 0);
+ blob_pos=STANDARD_LENGTH+60;
+ keyinfo[0].seg= &glob_keyseg[0][0];
+ keyinfo[0].seg[0].start=0;
+ keyinfo[0].seg[0].length=6;
+ keyinfo[0].seg[0].type=HA_KEYTYPE_TEXT;
+ keyinfo[0].seg[0].language= default_charset_info->number;
+ keyinfo[0].seg[0].flag=(uint8) pack_seg;
+ keyinfo[0].seg[0].null_bit=0;
+ keyinfo[0].seg[0].null_pos=0;
+ keyinfo[0].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[0].keysegs=1;
+ keyinfo[0].flag = pack_type;
+ keyinfo[1].seg= &glob_keyseg[1][0];
+ keyinfo[1].seg[0].start=7;
+ keyinfo[1].seg[0].length=6;
+ keyinfo[1].seg[0].type=HA_KEYTYPE_BINARY;
+ keyinfo[1].seg[0].flag=0;
+ keyinfo[1].seg[0].null_bit=0;
+ keyinfo[1].seg[0].null_pos=0;
+ keyinfo[1].seg[1].start=0; /* two part key */
+ keyinfo[1].seg[1].length=6;
+ keyinfo[1].seg[1].type=HA_KEYTYPE_NUM;
+ keyinfo[1].seg[1].flag=HA_REVERSE_SORT;
+ keyinfo[1].seg[1].null_bit=0;
+ keyinfo[1].seg[1].null_pos=0;
+ keyinfo[1].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[1].keysegs=2;
+ keyinfo[1].flag =0;
+ keyinfo[2].seg= &glob_keyseg[2][0];
+ keyinfo[2].seg[0].start=12;
+ keyinfo[2].seg[0].length=8;
+ keyinfo[2].seg[0].type=HA_KEYTYPE_BINARY;
+ keyinfo[2].seg[0].flag=HA_REVERSE_SORT;
+ keyinfo[2].seg[0].null_bit=0;
+ keyinfo[2].seg[0].null_pos=0;
+ keyinfo[2].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[2].keysegs=1;
+ keyinfo[2].flag =HA_NOSAME;
+ keyinfo[3].seg= &glob_keyseg[3][0];
+ keyinfo[3].seg[0].start=0;
+ keyinfo[3].seg[0].length=reclength-(use_blob ? 8 : 0);
+ keyinfo[3].seg[0].type=HA_KEYTYPE_TEXT;
+ keyinfo[3].seg[0].language=default_charset_info->number;
+ keyinfo[3].seg[0].flag=(uint8) pack_seg;
+ keyinfo[3].seg[0].null_bit=0;
+ keyinfo[3].seg[0].null_pos=0;
+ keyinfo[3].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[3].keysegs=1;
+ keyinfo[3].flag = pack_type;
+ keyinfo[4].seg= &glob_keyseg[4][0];
+ keyinfo[4].seg[0].start=0;
+ keyinfo[4].seg[0].length=5;
+ keyinfo[4].seg[0].type=HA_KEYTYPE_TEXT;
+ keyinfo[4].seg[0].language=default_charset_info->number;
+ keyinfo[4].seg[0].flag=0;
+ keyinfo[4].seg[0].null_bit=0;
+ keyinfo[4].seg[0].null_pos=0;
+ keyinfo[4].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[4].keysegs=1;
+ keyinfo[4].flag = pack_type;
+ keyinfo[5].seg= &glob_keyseg[5][0];
+ keyinfo[5].seg[0].start=0;
+ keyinfo[5].seg[0].length=4;
+ keyinfo[5].seg[0].type=HA_KEYTYPE_TEXT;
+ keyinfo[5].seg[0].language=default_charset_info->number;
+ keyinfo[5].seg[0].flag=pack_seg;
+ keyinfo[5].seg[0].null_bit=0;
+ keyinfo[5].seg[0].null_pos=0;
+ keyinfo[5].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[5].keysegs=1;
+ keyinfo[5].flag = pack_type;
+
+ recinfo[0].type=pack_fields ? FIELD_SKIP_PRESPACE : 0;
+ recinfo[0].length=7;
+ recinfo[0].null_bit=0;
+ recinfo[0].null_pos=0;
+ recinfo[1].type=pack_fields ? FIELD_SKIP_PRESPACE : 0;
+ recinfo[1].length=5;
+ recinfo[1].null_bit=0;
+ recinfo[1].null_pos=0;
+ recinfo[2].type=pack_fields ? FIELD_SKIP_PRESPACE : 0;
+ recinfo[2].length=9;
+ recinfo[2].null_bit=0;
+ recinfo[2].null_pos=0;
+ recinfo[3].type=FIELD_NORMAL;
+ recinfo[3].length=STANDARD_LENGTH-7-5-9-4;
+ recinfo[3].null_bit=0;
+ recinfo[3].null_pos=0;
+ recinfo[4].type=pack_fields ? FIELD_SKIP_ZERO : 0;
+ recinfo[4].length=4;
+ recinfo[4].null_bit=0;
+ recinfo[4].null_pos=0;
+ recinfo[5].type=pack_fields ? FIELD_SKIP_ENDSPACE : 0;
+ recinfo[5].length=60;
+ recinfo[5].null_bit=0;
+ recinfo[5].null_pos=0;
+ if (use_blob)
+ {
+ recinfo[6].type=FIELD_BLOB;
+ recinfo[6].length=4+mi_portable_sizeof_char_ptr;
+ recinfo[6].null_bit=0;
+ recinfo[6].null_pos=0;
+ }
+
+ write_count=update=dupp_keys=opt_delete=0;
+ blob_buffer=0;
+
+ for (i=1000 ; i>0 ; i--) key1[i]=0;
+ for (i=4999 ; i>0 ; i--) key3[i]=0;
+
+ if (!silent)
+ printf("- Creating isam-file\n");
+ /* DBUG_PUSH(""); */
+ /* my_delete(filename,MYF(0)); */ /* Remove old locks under gdb */
+ file= 0;
+ bzero((char*) &create_info,sizeof(create_info));
+ create_info.max_rows=(ha_rows) (rec_pointer_size ?
+ (1L << (rec_pointer_size*8))/
+ reclength : 0);
+ create_info.reloc_rows=(ha_rows) 100;
+ if (mi_create(filename,keys,&keyinfo[first_key],
+ use_blob ? 7 : 6, &recinfo[0],
+ 0,(MI_UNIQUEDEF*) 0,
+ &create_info,create_flag))
+ goto err;
+ if (use_log)
+ mi_log(1);
+ if (!(file=mi_open(filename,2,HA_OPEN_ABORT_IF_LOCKED)))
+ goto err;
+ if (!silent)
+ printf("- Writing key:s\n");
+ if (key_cacheing)
+ init_key_cache(dflt_key_cache,key_cache_block_size,key_cache_size,0,0);
+ if (locking)
+ mi_lock_database(file,F_WRLCK);
+ if (write_cacheing)
+ mi_extra(file,HA_EXTRA_WRITE_CACHE,0);
+ if (opt_quick_mode)
+ mi_extra(file,HA_EXTRA_QUICK,0);
+
+ for (i=0 ; i < recant ; i++)
+ {
+ n1=rnd(1000); n2=rnd(100); n3=rnd(5000);
+ sprintf(record,"%6d:%4d:%8d:Pos: %4d ",n1,n2,n3,write_count);
+ int4store(record+STANDARD_LENGTH-4,(long) i);
+ fix_length(record,(uint) STANDARD_LENGTH+rnd(60));
+ put_blob_in_record(record+blob_pos,&blob_buffer);
+ DBUG_PRINT("test",("record: %d",i));
+
+ if (mi_write(file,record))
+ {
+ if (my_errno != HA_ERR_FOUND_DUPP_KEY || key3[n3] == 0)
+ {
+ printf("Error: %d in write at record: %d\n",my_errno,i);
+ goto err;
+ }
+ if (verbose) printf(" Double key: %d\n",n3);
+ }
+ else
+ {
+ if (key3[n3] == 1 && first_key <3 && first_key+keys >= 3)
+ {
+ printf("Error: Didn't get error when writing second key: '%8d'\n",n3);
+ goto err;
+ }
+ write_count++; key1[n1]++; key3[n3]=1;
+ }
+
+ /* Check if we can find key without flushing database */
+ if (i == recant/2)
+ {
+ for (j=rnd(1000)+1 ; j>0 && key1[j] == 0 ; j--) ;
+ if (!j)
+ for (j=999 ; j>0 && key1[j] == 0 ; j--) ;
+ sprintf(key,"%6d",j);
+ if (mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT))
+ {
+ printf("Test in loop: Can't find key: \"%s\"\n",key);
+ goto err;
+ }
+ }
+ }
+ if (testflag==1) goto end;
+
+ if (write_cacheing)
+ {
+ if (mi_extra(file,HA_EXTRA_NO_CACHE,0))
+ {
+ puts("got error from mi_extra(HA_EXTRA_NO_CACHE)");
+ goto end;
+ }
+ }
+ if (key_cacheing)
+ resize_key_cache(dflt_key_cache,key_cache_block_size,key_cache_size*2,0,0);
+
+ if (!silent)
+ printf("- Delete\n");
+ for (i=0 ; i<recant/10 ; i++)
+ {
+ for (j=rnd(1000)+1 ; j>0 && key1[j] == 0 ; j--) ;
+ if (j != 0)
+ {
+ sprintf(key,"%6d",j);
+ if (mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT))
+ {
+ printf("can't find key1: \"%s\"\n",key);
+ goto err;
+ }
+ if (opt_delete == (uint) remove_count) /* While testing */
+ goto end;
+ if (mi_delete(file,read_record))
+ {
+ printf("error: %d; can't delete record: \"%s\"\n", my_errno,read_record);
+ goto err;
+ }
+ opt_delete++;
+ key1[atoi(read_record+keyinfo[0].seg[0].start)]--;
+ key3[atoi(read_record+keyinfo[2].seg[0].start)]=0;
+ }
+ else
+ puts("Warning: Skipping delete test because no dupplicate keys");
+ }
+ if (testflag==2) goto end;
+
+ if (!silent)
+ printf("- Update\n");
+ for (i=0 ; i<recant/10 ; i++)
+ {
+ n1=rnd(1000); n2=rnd(100); n3=rnd(5000);
+ sprintf(record2,"%6d:%4d:%8d:XXX: %4d ",n1,n2,n3,update);
+ int4store(record2+STANDARD_LENGTH-4,(long) i);
+ fix_length(record2,(uint) STANDARD_LENGTH+rnd(60));
+
+ for (j=rnd(1000)+1 ; j>0 && key1[j] == 0 ; j--) ;
+ if (j != 0)
+ {
+ sprintf(key,"%6d",j);
+ if (mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT))
+ {
+ printf("can't find key1: \"%s\"\n",key);
+ goto err;
+ }
+ if (use_blob)
+ {
+ if (i & 1)
+ put_blob_in_record(record+blob_pos,&blob_buffer);
+ else
+ bmove(record+blob_pos,read_record+blob_pos,8);
+ }
+ if (mi_update(file,read_record,record2))
+ {
+ if (my_errno != HA_ERR_FOUND_DUPP_KEY || key3[n3] == 0)
+ {
+ printf("error: %d; can't update:\nFrom: \"%s\"\nTo: \"%s\"\n",
+ my_errno,read_record,record2);
+ goto err;
+ }
+ if (verbose)
+ printf("Double key when tried to update:\nFrom: \"%s\"\nTo: \"%s\"\n",record,record2);
+ }
+ else
+ {
+ key1[atoi(read_record+keyinfo[0].seg[0].start)]--;
+ key3[atoi(read_record+keyinfo[2].seg[0].start)]=0;
+ key1[n1]++; key3[n3]=1;
+ update++;
+ }
+ }
+ }
+ if (testflag == 3)
+ goto end;
+
+ for (i=999, dupp_keys=j=0 ; i>0 ; i--)
+ {
+ if (key1[i] > dupp_keys)
+ {
+ dupp_keys=key1[i]; j=i;
+ }
+ }
+ sprintf(key,"%6d",j);
+ start=keyinfo[0].seg[0].start;
+ length=keyinfo[0].seg[0].length;
+ if (dupp_keys)
+ {
+ if (!silent)
+ printf("- Same key: first - next -> last - prev -> first\n");
+ DBUG_PRINT("progpos",("first - next -> last - prev -> first"));
+ if (verbose) printf(" Using key: \"%s\" Keys: %d\n",key,dupp_keys);
+
+ if (mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT))
+ goto err;
+ if (mi_rsame(file,read_record2,-1))
+ goto err;
+ if (memcmp(read_record,read_record2,reclength) != 0)
+ {
+ printf("mi_rsame didn't find same record\n");
+ goto end;
+ }
+ info.recpos=mi_position(file);
+ if (mi_rfirst(file,read_record2,0) ||
+ mi_rsame_with_pos(file,read_record2,0,info.recpos) ||
+ memcmp(read_record,read_record2,reclength) != 0)
+ {
+ printf("mi_rsame_with_pos didn't find same record\n");
+ goto end;
+ }
+ {
+ int skr=mi_rnext(file,read_record2,0);
+ if ((skr && my_errno != HA_ERR_END_OF_FILE) ||
+ mi_rprev(file,read_record2,-1) ||
+ memcmp(read_record,read_record2,reclength) != 0)
+ {
+ printf("mi_rsame_with_pos lost position\n");
+ goto end;
+ }
+ }
+ ant=1;
+ while (mi_rnext(file,read_record2,0) == 0 &&
+ memcmp(read_record2+start,key,length) == 0) ant++;
+ if (ant != dupp_keys)
+ {
+ printf("next: Found: %d keys of %d\n",ant,dupp_keys);
+ goto end;
+ }
+ ant=0;
+ while (mi_rprev(file,read_record3,0) == 0 &&
+ bcmp(read_record3+start,key,length) == 0) ant++;
+ if (ant != dupp_keys)
+ {
+ printf("prev: Found: %d records of %d\n",ant,dupp_keys);
+ goto end;
+ }
+
+ /* Check of mi_rnext_same */
+ if (mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT))
+ goto err;
+ ant=1;
+ while (!mi_rnext_same(file,read_record3) && ant < dupp_keys+10)
+ ant++;
+ if (ant != dupp_keys || my_errno != HA_ERR_END_OF_FILE)
+ {
+ printf("mi_rnext_same: Found: %d records of %d\n",ant,dupp_keys);
+ goto end;
+ }
+ }
+
+ if (!silent)
+ printf("- All keys: first - next -> last - prev -> first\n");
+ DBUG_PRINT("progpos",("All keys: first - next -> last - prev -> first"));
+ ant=1;
+ if (mi_rfirst(file,read_record,0))
+ {
+ printf("Can't find first record\n");
+ goto end;
+ }
+ while ((error=mi_rnext(file,read_record3,0)) == 0 && ant < write_count+10)
+ ant++;
+ if (ant != write_count - opt_delete || error != HA_ERR_END_OF_FILE)
+ {
+ printf("next: I found: %d records of %d (error: %d)\n",
+ ant, write_count - opt_delete, error);
+ goto end;
+ }
+ if (mi_rlast(file,read_record2,0) ||
+ bcmp(read_record2,read_record3,reclength))
+ {
+ printf("Can't find last record\n");
+ DBUG_DUMP("record2",(byte*) read_record2,reclength);
+ DBUG_DUMP("record3",(byte*) read_record3,reclength);
+ goto end;
+ }
+ ant=1;
+ while (mi_rprev(file,read_record3,0) == 0 && ant < write_count+10)
+ ant++;
+ if (ant != write_count - opt_delete)
+ {
+ printf("prev: I found: %d records of %d\n",ant,write_count);
+ goto end;
+ }
+ if (bcmp(read_record,read_record3,reclength))
+ {
+ printf("Can't find first record\n");
+ goto end;
+ }
+
+ if (!silent)
+ printf("- Test if: Read first - next - prev - prev - next == first\n");
+ DBUG_PRINT("progpos",("- Read first - next - prev - prev - next == first"));
+ if (mi_rfirst(file,read_record,0) ||
+ mi_rnext(file,read_record3,0) ||
+ mi_rprev(file,read_record3,0) ||
+ mi_rprev(file,read_record3,0) == 0 ||
+ mi_rnext(file,read_record3,0))
+ goto err;
+ if (bcmp(read_record,read_record3,reclength) != 0)
+ printf("Can't find first record\n");
+
+ if (!silent)
+ printf("- Test if: Read last - prev - next - next - prev == last\n");
+ DBUG_PRINT("progpos",("Read last - prev - next - next - prev == last"));
+ if (mi_rlast(file,read_record2,0) ||
+ mi_rprev(file,read_record3,0) ||
+ mi_rnext(file,read_record3,0) ||
+ mi_rnext(file,read_record3,0) == 0 ||
+ mi_rprev(file,read_record3,0))
+ goto err;
+ if (bcmp(read_record2,read_record3,reclength))
+ printf("Can't find last record\n");
+
+ if (!silent)
+ puts("- Test read key-part");
+ strmov(key2,key);
+ for(i=strlen(key2) ; i-- > 1 ;)
+ {
+ key2[i]=0;
+
+ /* The following row is just to catch some bugs in the key code */
+ bzero((char*) file->lastkey,file->s->base.max_key_length*2);
+ if (mi_rkey(file,read_record,0,key2,(uint) i,HA_READ_PREFIX))
+ goto err;
+ if (bcmp(read_record+start,key,(uint) i))
+ {
+ puts("Didn't find right record");
+ goto end;
+ }
+ }
+ if (dupp_keys > 2)
+ {
+ if (!silent)
+ printf("- Read key (first) - next - delete - next -> last\n");
+ DBUG_PRINT("progpos",("first - next - delete - next -> last"));
+ if (mi_rkey(file,read_record,0,key,0,HA_READ_KEY_EXACT)) goto err;
+ if (mi_rnext(file,read_record3,0)) goto err;
+ if (mi_delete(file,read_record3)) goto err;
+ opt_delete++;
+ ant=1;
+ while (mi_rnext(file,read_record3,0) == 0 &&
+ bcmp(read_record3+start,key,length) == 0) ant++;
+ if (ant != dupp_keys-1)
+ {
+ printf("next: I can only find: %d keys of %d\n",ant,dupp_keys-1);
+ goto end;
+ }
+ }
+ if (dupp_keys>4)
+ {
+ if (!silent)
+ printf("- Read last of key - prev - delete - prev -> first\n");
+ DBUG_PRINT("progpos",("last - prev - delete - prev -> first"));
+ if (mi_rprev(file,read_record3,0)) goto err;
+ if (mi_rprev(file,read_record3,0)) goto err;
+ if (mi_delete(file,read_record3)) goto err;
+ opt_delete++;
+ ant=1;
+ while (mi_rprev(file,read_record3,0) == 0 &&
+ bcmp(read_record3+start,key,length) == 0) ant++;
+ if (ant != dupp_keys-2)
+ {
+ printf("next: I can only find: %d keys of %d\n",ant,dupp_keys-2);
+ goto end;
+ }
+ }
+ if (dupp_keys > 6)
+ {
+ if (!silent)
+ printf("- Read first - delete - next -> last\n");
+ DBUG_PRINT("progpos",("first - delete - next -> last"));
+ if (mi_rkey(file,read_record3,0,key,0,HA_READ_KEY_EXACT)) goto err;
+ if (mi_delete(file,read_record3)) goto err;
+ opt_delete++;
+ ant=1;
+ if (mi_rnext(file,read_record,0))
+ goto err; /* Skall finnas poster */
+ while (mi_rnext(file,read_record3,0) == 0 &&
+ bcmp(read_record3+start,key,length) == 0) ant++;
+ if (ant != dupp_keys-3)
+ {
+ printf("next: I can only find: %d keys of %d\n",ant,dupp_keys-3);
+ goto end;
+ }
+
+ if (!silent)
+ printf("- Read last - delete - prev -> first\n");
+ DBUG_PRINT("progpos",("last - delete - prev -> first"));
+ if (mi_rprev(file,read_record3,0)) goto err;
+ if (mi_delete(file,read_record3)) goto err;
+ opt_delete++;
+ ant=0;
+ while (mi_rprev(file,read_record3,0) == 0 &&
+ bcmp(read_record3+start,key,length) == 0) ant++;
+ if (ant != dupp_keys-4)
+ {
+ printf("next: I can only find: %d keys of %d\n",ant,dupp_keys-4);
+ goto end;
+ }
+ }
+
+ if (!silent)
+ puts("- Test if: Read rrnd - same");
+ DBUG_PRINT("progpos",("Read rrnd - same"));
+ for (i=0 ; i < write_count ; i++)
+ {
+ if (mi_rrnd(file,read_record,i == 0 ? 0L : HA_OFFSET_ERROR) == 0)
+ break;
+ }
+ if (i == write_count)
+ goto err;
+
+ bmove(read_record2,read_record,reclength);
+ for (i=min(2,keys) ; i-- > 0 ;)
+ {
+ if (mi_rsame(file,read_record2,(int) i)) goto err;
+ if (bcmp(read_record,read_record2,reclength) != 0)
+ {
+ printf("is_rsame didn't find same record\n");
+ goto end;
+ }
+ }
+ if (!silent)
+ puts("- Test mi_records_in_range");
+ mi_status(file,&info,HA_STATUS_VARIABLE);
+ for (i=0 ; i < info.keys ; i++)
+ {
+ key_range min_key, max_key;
+ if (mi_rfirst(file,read_record,(int) i) ||
+ mi_rlast(file,read_record2,(int) i))
+ goto err;
+ copy_key(file,(uint) i,(uchar*) read_record,(uchar*) key);
+ copy_key(file,(uint) i,(uchar*) read_record2,(uchar*) key2);
+ min_key.key= key;
+ min_key.length= USE_WHOLE_KEY;
+ min_key.flag= HA_READ_KEY_EXACT;
+ max_key.key= key2;
+ max_key.length= USE_WHOLE_KEY;
+ max_key.flag= HA_READ_AFTER_KEY;
+
+ range_records= mi_records_in_range(file,(int) i, &min_key, &max_key);
+ if (range_records < info.records*8/10 ||
+ range_records > info.records*12/10)
+ {
+ printf("mi_records_range returned %ld; Should be about %ld\n",
+ (long) range_records,(long) info.records);
+ goto end;
+ }
+ if (verbose)
+ {
+ printf("mi_records_range returned %ld; Exact is %ld (diff: %4.2g %%)\n",
+ (long) range_records, (long) info.records,
+ labs((long) range_records - (long) info.records)*100.0/
+ info.records);
+ }
+ }
+ for (i=0 ; i < 5 ; i++)
+ {
+ for (j=rnd(1000)+1 ; j>0 && key1[j] == 0 ; j--) ;
+ for (k=rnd(1000)+1 ; k>0 && key1[k] == 0 ; k--) ;
+ if (j != 0 && k != 0)
+ {
+ key_range min_key, max_key;
+ if (j > k)
+ swap_variables(int, j, k);
+ sprintf(key,"%6d",j);
+ sprintf(key2,"%6d",k);
+
+ min_key.key= key;
+ min_key.length= USE_WHOLE_KEY;
+ min_key.flag= HA_READ_AFTER_KEY;
+ max_key.key= key2;
+ max_key.length= USE_WHOLE_KEY;
+ max_key.flag= HA_READ_BEFORE_KEY;
+ range_records= mi_records_in_range(file, 0, &min_key, &max_key);
+ records=0;
+ for (j++ ; j < k ; j++)
+ records+=key1[j];
+ if ((long) range_records < (long) records*7/10-2 ||
+ (long) range_records > (long) records*14/10+2)
+ {
+ printf("mi_records_range for key: %d returned %lu; Should be about %lu\n",
+ i, (ulong) range_records, (ulong) records);
+ goto end;
+ }
+ if (verbose && records)
+ {
+ printf("mi_records_range returned %lu; Exact is %lu (diff: %4.2g %%)\n",
+ (ulong) range_records, (ulong) records,
+ labs((long) range_records-(long) records)*100.0/records);
+
+ }
+ }
+ }
+
+ if (!silent)
+ printf("- mi_info\n");
+ mi_status(file,&info,HA_STATUS_VARIABLE | HA_STATUS_CONST);
+ if (info.records != write_count-opt_delete || info.deleted > opt_delete + update
+ || info.keys != keys)
+ {
+ puts("Wrong info from mi_info");
+ printf("Got: records: %lu delete: %lu i_keys: %d\n",
+ (ulong) info.records, (ulong) info.deleted, info.keys);
+ }
+ if (verbose)
+ {
+ char buff[80];
+ get_date(buff,3,info.create_time);
+ printf("info: Created %s\n",buff);
+ get_date(buff,3,info.check_time);
+ printf("info: checked %s\n",buff);
+ get_date(buff,3,info.update_time);
+ printf("info: Modified %s\n",buff);
+ }
+
+ mi_panic(HA_PANIC_WRITE);
+ mi_panic(HA_PANIC_READ);
+ if (mi_is_changed(file))
+ puts("Warning: mi_is_changed reported that datafile was changed");
+
+ if (!silent)
+ printf("- mi_extra(CACHE) + mi_rrnd.... + mi_extra(NO_CACHE)\n");
+ if (mi_extra(file,HA_EXTRA_RESET,0) || mi_extra(file,HA_EXTRA_CACHE,0))
+ {
+ if (locking || (!use_blob && !pack_fields))
+ {
+ puts("got error from mi_extra(HA_EXTRA_CACHE)");
+ goto end;
+ }
+ }
+ ant=0;
+ while ((error=mi_rrnd(file,record,HA_OFFSET_ERROR)) != HA_ERR_END_OF_FILE &&
+ ant < write_count + 10)
+ ant+= error ? 0 : 1;
+ if (ant != write_count-opt_delete)
+ {
+ printf("rrnd with cache: I can only find: %d records of %d\n",
+ ant,write_count-opt_delete);
+ goto end;
+ }
+ if (mi_extra(file,HA_EXTRA_NO_CACHE,0))
+ {
+ puts("got error from mi_extra(HA_EXTRA_NO_CACHE)");
+ goto end;
+ }
+
+ ant=0;
+ mi_scan_init(file);
+ while ((error=mi_scan(file,record)) != HA_ERR_END_OF_FILE &&
+ ant < write_count + 10)
+ ant+= error ? 0 : 1;
+ if (ant != write_count-opt_delete)
+ {
+ printf("scan with cache: I can only find: %d records of %d\n",
+ ant,write_count-opt_delete);
+ goto end;
+ }
+
+ if (testflag == 4) goto end;
+
+ if (!silent)
+ printf("- Removing keys\n");
+ DBUG_PRINT("progpos",("Removing keys"));
+ lastpos = HA_OFFSET_ERROR;
+ /* DBUG_POP(); */
+ mi_extra(file,HA_EXTRA_RESET,0);
+ found_parts=0;
+ while ((error=mi_rrnd(file,read_record,HA_OFFSET_ERROR)) !=
+ HA_ERR_END_OF_FILE)
+ {
+ info.recpos=mi_position(file);
+ if (lastpos >= info.recpos && lastpos != HA_OFFSET_ERROR)
+ {
+ printf("mi_rrnd didn't advance filepointer; old: %ld, new: %ld\n",
+ (long) lastpos, (long) info.recpos);
+ goto err;
+ }
+ lastpos=info.recpos;
+ if (error == 0)
+ {
+ if (opt_delete == (uint) remove_count) /* While testing */
+ goto end;
+ if (mi_rsame(file,read_record,-1))
+ {
+ printf("can't find record %lx\n",(long) info.recpos);
+ goto err;
+ }
+ if (use_blob)
+ {
+ ulong blob_length,pos;
+ uchar *ptr;
+ longget(blob_length,read_record+blob_pos+4);
+ ptr=(uchar*) blob_length;
+ longget(blob_length,read_record+blob_pos);
+ for (pos=0 ; pos < blob_length ; pos++)
+ {
+ if (ptr[pos] != (uchar) (blob_length+pos))
+ {
+ printf("found blob with wrong info at %ld\n",(long) lastpos);
+ use_blob=0;
+ break;
+ }
+ }
+ }
+ if (mi_delete(file,read_record))
+ {
+ printf("can't delete record: %6.6s, delete_count: %d\n",
+ read_record, opt_delete);
+ goto err;
+ }
+ opt_delete++;
+ }
+ else
+ found_parts++;
+ }
+ if (my_errno != HA_ERR_END_OF_FILE && my_errno != HA_ERR_RECORD_DELETED)
+ printf("error: %d from mi_rrnd\n",my_errno);
+ if (write_count != opt_delete)
+ {
+ printf("Deleted only %d of %d records (%d parts)\n",opt_delete,write_count,
+ found_parts);
+ goto err;
+ }
+end:
+ if (mi_close(file))
+ goto err;
+ mi_panic(HA_PANIC_CLOSE); /* Should close log */
+ if (!silent)
+ {
+ printf("\nFollowing test have been made:\n");
+ printf("Write records: %d\nUpdate records: %d\nSame-key-read: %d\nDelete records: %d\n", write_count,update,dupp_keys,opt_delete);
+ if (rec_pointer_size)
+ printf("Record pointer size: %d\n",rec_pointer_size);
+ printf("myisam_block_size: %u\n", myisam_block_size);
+ if (key_cacheing)
+ {
+ puts("Key cache used");
+ printf("key_cache_block_size: %u\n", key_cache_block_size);
+ if (write_cacheing)
+ puts("Key cache resized");
+ }
+ if (write_cacheing)
+ puts("Write cacheing used");
+ if (write_cacheing)
+ puts("quick mode");
+ if (async_io && locking)
+ puts("Asyncron io with locking used");
+ else if (locking)
+ puts("Locking used");
+ if (use_blob)
+ puts("blobs used");
+#if 0
+ printf("key cache status: \n\
+blocks used:%10lu\n\
+w_requests: %10lu\n\
+writes: %10lu\n\
+r_requests: %10lu\n\
+reads: %10lu\n",
+ my_blocks_used,
+ my_cache_w_requests, my_cache_write,
+ my_cache_r_requests, my_cache_read);
+#endif
+ }
+ end_key_cache(dflt_key_cache,1);
+ if (blob_buffer)
+ my_free(blob_buffer,MYF(0));
+ my_end(silent ? MY_CHECK_ERROR : MY_CHECK_ERROR | MY_GIVE_INFO);
+ return(0);
+err:
+ printf("got error: %d when using MyISAM-database\n",my_errno);
+ if (file)
+ VOID(mi_close(file));
+ return(1);
+} /* main */
+
+
+ /* l{ser optioner */
+ /* OBS! intierar endast DEBUG - ingen debuggning h{r ! */
+
+static void get_options(int argc, char **argv)
+{
+ char *pos,*progname;
+ DEBUGGER_OFF;
+
+ progname= argv[0];
+
+ while (--argc >0 && *(pos = *(++argv)) == '-' ) {
+ switch(*++pos) {
+ case 'B':
+ pack_type= HA_BINARY_PACK_KEY;
+ break;
+ case 'b':
+ use_blob=1;
+ break;
+ case 'K': /* Use key cacheing */
+ key_cacheing=1;
+ if (*++pos)
+ key_cache_size=atol(pos);
+ break;
+ case 'W': /* Use write cacheing */
+ write_cacheing=1;
+ if (*++pos)
+ my_default_record_cache_size=atoi(pos);
+ break;
+ case 'd':
+ remove_count= atoi(++pos);
+ break;
+ case 'i':
+ if (*++pos)
+ srand(atoi(pos));
+ break;
+ case 'l':
+ use_log=1;
+ break;
+ case 'L':
+ locking=1;
+ break;
+ case 'A': /* use asyncron io */
+ async_io=1;
+ if (*++pos)
+ my_default_record_cache_size=atoi(pos);
+ break;
+ case 'v': /* verbose */
+ verbose=1;
+ break;
+ case 'm': /* records */
+ if ((recant=atoi(++pos)) < 10)
+ {
+ fprintf(stderr,"record count must be >= 10\n");
+ exit(1);
+ }
+ break;
+ case 'e': /* myisam_block_length */
+ if ((myisam_block_size=atoi(++pos)) < MI_MIN_KEY_BLOCK_LENGTH ||
+ myisam_block_size > MI_MAX_KEY_BLOCK_LENGTH)
+ {
+ fprintf(stderr,"Wrong myisam_block_length\n");
+ exit(1);
+ }
+ myisam_block_size=1 << my_bit_log2(myisam_block_size);
+ break;
+ case 'E': /* myisam_block_length */
+ if ((key_cache_block_size=atoi(++pos)) < MI_MIN_KEY_BLOCK_LENGTH ||
+ key_cache_block_size > MI_MAX_KEY_BLOCK_LENGTH)
+ {
+ fprintf(stderr,"Wrong key_cache_block_size\n");
+ exit(1);
+ }
+ key_cache_block_size=1 << my_bit_log2(key_cache_block_size);
+ break;
+ case 'f':
+ if ((first_key=atoi(++pos)) < 0 || first_key >= MYISAM_KEYS)
+ first_key=0;
+ break;
+ case 'k':
+ if ((keys=(uint) atoi(++pos)) < 1 ||
+ keys > (uint) (MYISAM_KEYS-first_key))
+ keys=MYISAM_KEYS-first_key;
+ break;
+ case 'P':
+ pack_type=0; /* Don't use DIFF_LENGTH */
+ pack_seg=0;
+ break;
+ case 'R': /* Length of record pointer */
+ rec_pointer_size=atoi(++pos);
+ if (rec_pointer_size > 7)
+ rec_pointer_size=0;
+ break;
+ case 'S':
+ pack_fields=0; /* Static-length-records */
+ break;
+ case 's':
+ silent=1;
+ break;
+ case 't':
+ testflag=atoi(++pos); /* testmod */
+ break;
+ case 'q':
+ opt_quick_mode=1;
+ break;
+ case 'c':
+ create_flag|= HA_CREATE_CHECKSUM;
+ break;
+ case 'D':
+ create_flag|=HA_CREATE_DELAY_KEY_WRITE;
+ break;
+ case '?':
+ case 'I':
+ case 'V':
+ printf("%s Ver 1.2 for %s at %s\n",progname,SYSTEM_TYPE,MACHINE_TYPE);
+ puts("By Monty, for your professional use\n");
+ printf("Usage: %s [-?AbBcDIKLPRqSsVWltv] [-k#] [-f#] [-m#] [-e#] [-E#] [-t#]\n",
+ progname);
+ exit(0);
+ case '#':
+ DEBUGGER_ON;
+ DBUG_PUSH (++pos);
+ break;
+ default:
+ printf("Illegal option: '%c'\n",*pos);
+ break;
+ }
+ }
+ return;
+} /* get options */
+
+ /* Get a random value 0 <= x <= n */
+
+static uint rnd(uint max_value)
+{
+ return (uint) ((rand() & 32767)/32767.0*max_value);
+} /* rnd */
+
+
+ /* Create a variable length record */
+
+static void fix_length(byte *rec, uint length)
+{
+ bmove(rec+STANDARD_LENGTH,
+ "0123456789012345678901234567890123456789012345678901234567890",
+ length-STANDARD_LENGTH);
+ strfill(rec+length,STANDARD_LENGTH+60-length,' ');
+} /* fix_length */
+
+
+ /* Put maybe a blob in record */
+
+static void put_blob_in_record(char *blob_pos, char **blob_buffer)
+{
+ ulong i,length;
+ if (use_blob)
+ {
+ if (rnd(10) == 0)
+ {
+ if (! *blob_buffer &&
+ !(*blob_buffer=my_malloc((uint) use_blob,MYF(MY_WME))))
+ {
+ use_blob=0;
+ return;
+ }
+ length=rnd(use_blob);
+ for (i=0 ; i < length ; i++)
+ (*blob_buffer)[i]=(char) (length+i);
+ int4store(blob_pos,length);
+ memcpy_fixed(blob_pos+4,(char*) blob_buffer,sizeof(char*));
+ }
+ else
+ {
+ int4store(blob_pos,0);
+ }
+ }
+ return;
+}
+
+
+static void copy_key(MI_INFO *info,uint inx,uchar *rec,uchar *key_buff)
+{
+ HA_KEYSEG *keyseg;
+
+ for (keyseg=info->s->keyinfo[inx].seg ; keyseg->type ; keyseg++)
+ {
+ memcpy(key_buff,rec+keyseg->start,(size_t) keyseg->length);
+ key_buff+=keyseg->length;
+ }
+ return;
+}
diff --git a/storage/myisam/mi_test3.c b/storage/myisam/mi_test3.c
new file mode 100644
index 00000000000..be4277cc65c
--- /dev/null
+++ b/storage/myisam/mi_test3.c
@@ -0,0 +1,502 @@
+/* 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 */
+
+/* Test av locking */
+
+#ifndef __NETWARE__
+
+#include "myisam.h"
+#include <sys/types.h>
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+
+#if defined(HAVE_LRAND48)
+#define rnd(X) (lrand48() % X)
+#define rnd_init(X) srand48(X)
+#else
+#define rnd(X) (random() % X)
+#define rnd_init(X) srandom(X)
+#endif
+
+
+const char *filename= "test3";
+uint tests=10,forks=10,key_cacheing=0,use_log=0;
+
+static void get_options(int argc, char *argv[]);
+void start_test(int id);
+int test_read(MI_INFO *,int),test_write(MI_INFO *,int,int),
+ test_update(MI_INFO *,int,int),test_rrnd(MI_INFO *,int);
+
+struct record {
+ char id[8];
+ char nr[4];
+ char text[10];
+} record;
+
+
+int main(int argc,char **argv)
+{
+ int status,wait_ret;
+ uint i=0;
+ MI_KEYDEF keyinfo[10];
+ MI_COLUMNDEF recinfo[10];
+ HA_KEYSEG keyseg[10][2];
+ MY_INIT(argv[0]);
+ get_options(argc,argv);
+
+ bzero((char*) keyinfo,sizeof(keyinfo));
+ bzero((char*) recinfo,sizeof(recinfo));
+ bzero((char*) keyseg,sizeof(keyseg));
+ keyinfo[0].seg= &keyseg[0][0];
+ keyinfo[0].seg[0].start=0;
+ keyinfo[0].seg[0].length=8;
+ keyinfo[0].seg[0].type=HA_KEYTYPE_TEXT;
+ keyinfo[0].seg[0].flag=HA_SPACE_PACK;
+ keyinfo[0].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[0].keysegs=1;
+ keyinfo[0].flag = (uint8) HA_PACK_KEY;
+ keyinfo[1].seg= &keyseg[1][0];
+ keyinfo[1].seg[0].start=8;
+ keyinfo[1].seg[0].length=4; /* Long is always 4 in myisam */
+ keyinfo[1].seg[0].type=HA_KEYTYPE_LONG_INT;
+ keyinfo[1].seg[0].flag=0;
+ keyinfo[1].key_alg=HA_KEY_ALG_BTREE;
+ keyinfo[1].keysegs=1;
+ keyinfo[1].flag =HA_NOSAME;
+
+ recinfo[0].type=0;
+ recinfo[0].length=sizeof(record.id);
+ recinfo[1].type=0;
+ recinfo[1].length=sizeof(record.nr);
+ recinfo[2].type=0;
+ recinfo[2].length=sizeof(record.text);
+
+ puts("- Creating myisam-file");
+ my_delete(filename,MYF(0)); /* Remove old locks under gdb */
+ if (mi_create(filename,2,&keyinfo[0],2,&recinfo[0],0,(MI_UNIQUEDEF*) 0,
+ (MI_CREATE_INFO*) 0,0))
+ exit(1);
+
+ rnd_init(0);
+ printf("- Starting %d processes\n",forks); fflush(stdout);
+ for (i=0 ; i < forks; i++)
+ {
+ if (!fork())
+ {
+ start_test(i+1);
+ sleep(1);
+ return 0;
+ }
+ VOID(rnd(1));
+ }
+
+ for (i=0 ; i < forks ; i++)
+ while ((wait_ret=wait(&status)) && wait_ret == -1);
+ return 0;
+}
+
+
+static void get_options(int argc, char **argv)
+{
+ char *pos,*progname;
+ DEBUGGER_OFF;
+
+ progname= argv[0];
+
+ while (--argc >0 && *(pos = *(++argv)) == '-' ) {
+ switch(*++pos) {
+ case 'l':
+ use_log=1;
+ break;
+ case 'f':
+ forks=atoi(++pos);
+ break;
+ case 't':
+ tests=atoi(++pos);
+ break;
+ case 'K': /* Use key cacheing */
+ key_cacheing=1;
+ break;
+ case 'A': /* All flags */
+ use_log=key_cacheing=1;
+ break;
+ case '?':
+ case 'I':
+ case 'V':
+ printf("%s Ver 1.0 for %s at %s\n",progname,SYSTEM_TYPE,MACHINE_TYPE);
+ puts("By Monty, for your professional use\n");
+ puts("Test av locking with threads\n");
+ printf("Usage: %s [-?lKA] [-f#] [-t#]\n",progname);
+ exit(0);
+ case '#':
+ DEBUGGER_ON;
+ DBUG_PUSH (++pos);
+ break;
+ default:
+ printf("Illegal option: '%c'\n",*pos);
+ break;
+ }
+ }
+ return;
+}
+
+
+void start_test(int id)
+{
+ uint i;
+ int error,lock_type;
+ MI_ISAMINFO isam_info;
+ MI_INFO *file,*file1,*file2=0,*lock;
+
+ if (use_log)
+ mi_log(1);
+ if (!(file1=mi_open(filename,O_RDWR,HA_OPEN_WAIT_IF_LOCKED)) ||
+ !(file2=mi_open(filename,O_RDWR,HA_OPEN_WAIT_IF_LOCKED)))
+ {
+ fprintf(stderr,"Can't open isam-file: %s\n",filename);
+ exit(1);
+ }
+ if (key_cacheing && rnd(2) == 0)
+ init_key_cache(dflt_key_cache, KEY_CACHE_BLOCK_SIZE, 65536L, 0, 0);
+ printf("Process %d, pid: %d\n",id,getpid()); fflush(stdout);
+
+ for (error=i=0 ; i < tests && !error; i++)
+ {
+ file= (rnd(2) == 1) ? file1 : file2;
+ lock=0 ; lock_type=0;
+ if (rnd(10) == 0)
+ {
+ if (mi_lock_database(lock=(rnd(2) ? file1 : file2),
+ lock_type=(rnd(2) == 0 ? F_RDLCK : F_WRLCK)))
+ {
+ fprintf(stderr,"%2d: start: Can't lock table %d\n",id,my_errno);
+ error=1;
+ break;
+ }
+ }
+ switch (rnd(4)) {
+ case 0: error=test_read(file,id); break;
+ case 1: error=test_rrnd(file,id); break;
+ case 2: error=test_write(file,id,lock_type); break;
+ case 3: error=test_update(file,id,lock_type); break;
+ }
+ if (lock)
+ mi_lock_database(lock,F_UNLCK);
+ }
+ if (!error)
+ {
+ mi_status(file1,&isam_info,HA_STATUS_VARIABLE);
+ printf("%2d: End of test. Records: %ld Deleted: %ld\n",
+ id,(long) isam_info.records, (long) isam_info.deleted);
+ fflush(stdout);
+ }
+
+ mi_close(file1);
+ mi_close(file2);
+ if (use_log)
+ mi_log(0);
+ if (error)
+ {
+ printf("%2d: Aborted\n",id); fflush(stdout);
+ exit(1);
+ }
+}
+
+
+int test_read(MI_INFO *file,int id)
+{
+ uint i,lock,found,next,prev;
+ ulong find;
+
+ lock=0;
+ if (rnd(2) == 0)
+ {
+ lock=1;
+ if (mi_lock_database(file,F_RDLCK))
+ {
+ fprintf(stderr,"%2d: Can't lock table %d\n",id,my_errno);
+ return 1;
+ }
+ }
+
+ found=next=prev=0;
+ for (i=0 ; i < 100 ; i++)
+ {
+ find=rnd(100000);
+ if (!mi_rkey(file,record.id,1,(byte*) &find,
+ sizeof(find),HA_READ_KEY_EXACT))
+ found++;
+ else
+ {
+ if (my_errno != HA_ERR_KEY_NOT_FOUND)
+ {
+ fprintf(stderr,"%2d: Got error %d from read in read\n",id,my_errno);
+ return 1;
+ }
+ else if (!mi_rnext(file,record.id,1))
+ next++;
+ else
+ {
+ if (my_errno != HA_ERR_END_OF_FILE)
+ {
+ fprintf(stderr,"%2d: Got error %d from rnext in read\n",id,my_errno);
+ return 1;
+ }
+ else if (!mi_rprev(file,record.id,1))
+ prev++;
+ else
+ {
+ if (my_errno != HA_ERR_END_OF_FILE)
+ {
+ fprintf(stderr,"%2d: Got error %d from rnext in read\n",
+ id,my_errno);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ if (lock)
+ {
+ if (mi_lock_database(file,F_UNLCK))
+ {
+ fprintf(stderr,"%2d: Can't unlock table\n",id);
+ return 1;
+ }
+ }
+ printf("%2d: read: found: %5d next: %5d prev: %5d\n",
+ id,found,next,prev);
+ fflush(stdout);
+ return 0;
+}
+
+
+int test_rrnd(MI_INFO *file,int id)
+{
+ uint count,lock;
+
+ lock=0;
+ if (rnd(2) == 0)
+ {
+ lock=1;
+ if (mi_lock_database(file,F_RDLCK))
+ {
+ fprintf(stderr,"%2d: Can't lock table (%d)\n",id,my_errno);
+ mi_close(file);
+ return 1;
+ }
+ if (rnd(2) == 0)
+ mi_extra(file,HA_EXTRA_CACHE,0);
+ }
+
+ count=0;
+ if (mi_rrnd(file,record.id,0L))
+ {
+ if (my_errno == HA_ERR_END_OF_FILE)
+ goto end;
+ fprintf(stderr,"%2d: Can't read first record (%d)\n",id,my_errno);
+ return 1;
+ }
+ for (count=1 ; !mi_rrnd(file,record.id,HA_OFFSET_ERROR) ;count++) ;
+ if (my_errno != HA_ERR_END_OF_FILE)
+ {
+ fprintf(stderr,"%2d: Got error %d from rrnd\n",id,my_errno);
+ return 1;
+ }
+
+end:
+ if (lock)
+ {
+ mi_extra(file,HA_EXTRA_NO_CACHE,0);
+ if (mi_lock_database(file,F_UNLCK))
+ {
+ fprintf(stderr,"%2d: Can't unlock table\n",id);
+ exit(0);
+ }
+ }
+ printf("%2d: rrnd: %5d\n",id,count); fflush(stdout);
+ return 0;
+}
+
+
+int test_write(MI_INFO *file,int id,int lock_type)
+{
+ uint i,tries,count,lock;
+
+ lock=0;
+ if (rnd(2) == 0 || lock_type == F_RDLCK)
+ {
+ lock=1;
+ if (mi_lock_database(file,F_WRLCK))
+ {
+ if (lock_type == F_RDLCK && my_errno == EDEADLK)
+ {
+ printf("%2d: write: deadlock\n",id); fflush(stdout);
+ return 0;
+ }
+ fprintf(stderr,"%2d: Can't lock table (%d)\n",id,my_errno);
+ mi_close(file);
+ return 1;
+ }
+ if (rnd(2) == 0)
+ mi_extra(file,HA_EXTRA_WRITE_CACHE,0);
+ }
+
+ sprintf(record.id,"%7d",getpid());
+ strnmov(record.text,"Testing...", sizeof(record.text));
+
+ tries=(uint) rnd(100)+10;
+ for (i=count=0 ; i < tries ; i++)
+ {
+ uint32 tmp=rnd(80000)+20000;
+ int4store(record.nr,tmp);
+ if (!mi_write(file,record.id))
+ count++;
+ else
+ {
+ if (my_errno != HA_ERR_FOUND_DUPP_KEY)
+ {
+ fprintf(stderr,"%2d: Got error %d (errno %d) from write\n",id,my_errno,
+ errno);
+ return 1;
+ }
+ }
+ }
+ if (lock)
+ {
+ mi_extra(file,HA_EXTRA_NO_CACHE,0);
+ if (mi_lock_database(file,F_UNLCK))
+ {
+ fprintf(stderr,"%2d: Can't unlock table\n",id);
+ exit(0);
+ }
+ }
+ printf("%2d: write: %5d\n",id,count); fflush(stdout);
+ return 0;
+}
+
+
+int test_update(MI_INFO *file,int id,int lock_type)
+{
+ uint i,lock,found,next,prev,update;
+ uint32 tmp;
+ char find[4];
+ struct record new_record;
+
+ lock=0;
+ if (rnd(2) == 0 || lock_type == F_RDLCK)
+ {
+ lock=1;
+ if (mi_lock_database(file,F_WRLCK))
+ {
+ if (lock_type == F_RDLCK && my_errno == EDEADLK)
+ {
+ printf("%2d: write: deadlock\n",id); fflush(stdout);
+ return 0;
+ }
+ fprintf(stderr,"%2d: Can't lock table (%d)\n",id,my_errno);
+ return 1;
+ }
+ }
+ bzero((char*) &new_record,sizeof(new_record));
+ strmov(new_record.text,"Updated");
+
+ found=next=prev=update=0;
+ for (i=0 ; i < 100 ; i++)
+ {
+ tmp=rnd(100000);
+ int4store(find,tmp);
+ if (!mi_rkey(file,record.id,1,(byte*) find,
+ sizeof(find),HA_READ_KEY_EXACT))
+ found++;
+ else
+ {
+ if (my_errno != HA_ERR_KEY_NOT_FOUND)
+ {
+ fprintf(stderr,"%2d: Got error %d from read in update\n",id,my_errno);
+ return 1;
+ }
+ else if (!mi_rnext(file,record.id,1))
+ next++;
+ else
+ {
+ if (my_errno != HA_ERR_END_OF_FILE)
+ {
+ fprintf(stderr,"%2d: Got error %d from rnext in update\n",
+ id,my_errno);
+ return 1;
+ }
+ else if (!mi_rprev(file,record.id,1))
+ prev++;
+ else
+ {
+ if (my_errno != HA_ERR_END_OF_FILE)
+ {
+ fprintf(stderr,"%2d: Got error %d from rnext in update\n",
+ id,my_errno);
+ return 1;
+ }
+ continue;
+ }
+ }
+ }
+ memcpy_fixed(new_record.id,record.id,sizeof(record.id));
+ tmp=rnd(20000)+40000;
+ int4store(new_record.nr,tmp);
+ if (!mi_update(file,record.id,new_record.id))
+ update++;
+ else
+ {
+ if (my_errno != HA_ERR_RECORD_CHANGED &&
+ my_errno != HA_ERR_RECORD_DELETED &&
+ my_errno != HA_ERR_FOUND_DUPP_KEY)
+ {
+ fprintf(stderr,"%2d: Got error %d from update\n",id,my_errno);
+ return 1;
+ }
+ }
+ }
+ if (lock)
+ {
+ if (mi_lock_database(file,F_UNLCK))
+ {
+ fprintf(stderr,"Can't unlock table,id, error%d\n",my_errno);
+ return 1;
+ }
+ }
+ printf("%2d: update: %5d\n",id,update); fflush(stdout);
+ return 0;
+}
+
+#else /* __NETWARE__ */
+
+#include <stdio.h>
+
+main()
+{
+ fprintf(stderr,"this test has not been ported to NetWare\n");
+ return 0;
+}
+
+#endif /* __NETWARE__ */
diff --git a/storage/myisam/mi_test_all.res b/storage/myisam/mi_test_all.res
new file mode 100644
index 00000000000..16b517d3f76
--- /dev/null
+++ b/storage/myisam/mi_test_all.res
@@ -0,0 +1,53 @@
+myisamchk: MyISAM file test1
+myisamchk: warning: Size of indexfile is: 1024 Should be: 2048
+MyISAM-table 'test1' is usable but should be fixed
+mi_test2 -s -L -K -R1 -m2000 ; Should give error 135
+Error: 135 in write at record: 1105
+got error: 135 when using MyISAM-database
+myisamchk: MyISAM file test2
+myisamchk: warning: Datafile is almost full, 65532 of 65534 used
+MyISAM-table 'test2' is usable but should be fixed
+Commands Used count Errors Recover errors
+open 1 0 0
+write 50 0 0
+update 5 0 0
+delete 50 0 0
+close 1 0 0
+extra 6 0 0
+Total 113 0 0
+Commands Used count Errors Recover errors
+open 2 0 0
+write 100 0 0
+update 10 0 0
+delete 100 0 0
+close 2 0 0
+extra 12 0 0
+Total 226 0 0
+
+real 0m0.791s
+user 0m0.137s
+sys 0m0.117s
+
+real 0m0.659s
+user 0m0.252s
+sys 0m0.102s
+
+real 0m0.571s
+user 0m0.188s
+sys 0m0.098s
+
+real 0m1.111s
+user 0m0.236s
+sys 0m0.037s
+
+real 0m0.621s
+user 0m0.242s
+sys 0m0.022s
+
+real 0m0.698s
+user 0m0.248s
+sys 0m0.021s
+
+real 0m0.683s
+user 0m0.265s
+sys 0m0.079s
diff --git a/storage/myisam/mi_test_all.sh b/storage/myisam/mi_test_all.sh
new file mode 100755
index 00000000000..07e71d65675
--- /dev/null
+++ b/storage/myisam/mi_test_all.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+#
+# Execute some simple basic test on MyISAM libary to check if things
+# works at all.
+
+valgrind="valgrind --alignment=8 --leak-check=yes"
+silent="-s"
+
+if test -f mi_test1$MACH ; then suffix=$MACH else suffix=""; fi
+mi_test1$suffix $silent
+myisamchk$suffix -se test1
+mi_test1$suffix $silent -N -S
+myisamchk$suffix -se test1
+mi_test1$suffix $silent -P --checksum
+myisamchk$suffix -se test1
+mi_test1$suffix $silent -P -N -S
+myisamchk$suffix -se test1
+mi_test1$suffix $silent -B -N -R2
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -k 480 --unique
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -N -S -R1
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -p -S
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -p -S -N --unique
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -p -S -N --key_length=127 --checksum
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -p -S -N --key_length=128
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -p -S --key_length=480
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -B
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -B --key_length=64 --unique
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -B -k 480 --checksum
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -B -k 480 -N --unique --checksum
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -m
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -m -P --unique --checksum
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -m -P --key_length=480 --key_cache
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -m -p
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -w -S --unique
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -w --key_length=64 --checksum
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -w -N --key_length=480
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -w -S --key_length=480 --checksum
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -b -N
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -a -b --key_length=480
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent -p -B --key_length=480
+myisamchk$suffix -sm test1
+
+mi_test1$suffix $silent --checksum
+myisamchk$suffix -se test1
+myisamchk$suffix -rs test1
+myisamchk$suffix -se test1
+myisamchk$suffix -rqs test1
+myisamchk$suffix -se test1
+myisamchk$suffix -rs --correct-checksum test1
+myisamchk$suffix -se test1
+myisamchk$suffix -rqs --correct-checksum test1
+myisamchk$suffix -se test1
+myisamchk$suffix -ros --correct-checksum test1
+myisamchk$suffix -se test1
+myisamchk$suffix -rqos --correct-checksum test1
+myisamchk$suffix -se test1
+
+# check of myisampack / myisamchk
+myisampack$suffix --force -s test1
+myisamchk$suffix -es test1
+myisamchk$suffix -rqs test1
+myisamchk$suffix -es test1
+myisamchk$suffix -rs test1
+myisamchk$suffix -es test1
+myisamchk$suffix -rus test1
+myisamchk$suffix -es test1
+
+mi_test1$suffix $silent --checksum -S
+myisamchk$suffix -se test1
+myisamchk$suffix -ros test1
+myisamchk$suffix -rqs test1
+myisamchk$suffix -se test1
+
+myisampack$suffix --force -s test1
+myisamchk$suffix -rqs test1
+myisamchk$suffix -es test1
+myisamchk$suffix -rus test1
+myisamchk$suffix -es test1
+
+mi_test1$suffix $silent --checksum --unique
+myisamchk$suffix -se test1
+mi_test1$suffix $silent --unique -S
+myisamchk$suffix -se test1
+
+
+mi_test1$suffix $silent --key_multiple -N -S
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent --key_multiple -a -p --key_length=480
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent --key_multiple -a -B --key_length=480
+myisamchk$suffix -sm test1
+mi_test1$suffix $silent --key_multiple -P -S
+myisamchk$suffix -sm test1
+
+mi_test2$suffix $silent -L -K -W -P
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -L -K -W -P -A
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -L -K -W -P -S -R1 -m500
+echo "mi_test2$suffix $silent -L -K -R1 -m2000 ; Should give error 135"
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -L -K -R1 -m2000
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -L -K -P -S -R3 -m50 -b1000000
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -L -B
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -D -B -c
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -m10000 -e8192 -K
+myisamchk$suffix -sm test2
+mi_test2$suffix $silent -m10000 -e16384 -E16384 -K -L
+myisamchk$suffix -sm test2
+
+mi_test2$suffix $silent -L -K -W -P -m50 -l
+myisamlog$suffix
+mi_test2$suffix $silent -L -K -W -P -m50 -l -b100
+myisamlog$suffix
+time mi_test2$suffix $silent
+time mi_test2$suffix $silent -K -B
+time mi_test2$suffix $silent -L -B
+time mi_test2$suffix $silent -L -K -B
+time mi_test2$suffix $silent -L -K -W -B
+time mi_test2$suffix $silent -L -K -W -S -B
+time mi_test2$suffix $silent -D -K -W -S -B
diff --git a/storage/myisam/mi_unique.c b/storage/myisam/mi_unique.c
new file mode 100644
index 00000000000..f2d5f01be25
--- /dev/null
+++ b/storage/myisam/mi_unique.c
@@ -0,0 +1,217 @@
+/* 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 */
+
+/* Functions to check if a row is unique */
+
+#include "myisamdef.h"
+#include <m_ctype.h>
+
+my_bool mi_check_unique(MI_INFO *info, MI_UNIQUEDEF *def, byte *record,
+ ha_checksum unique_hash, my_off_t disk_pos)
+{
+ my_off_t lastpos=info->lastpos;
+ MI_KEYDEF *key= &info->s->keyinfo[def->key];
+ uchar *key_buff=info->lastkey2;
+ DBUG_ENTER("mi_check_unique");
+
+ mi_unique_store(record+key->seg->start, unique_hash);
+ _mi_make_key(info,def->key,key_buff,record,0);
+
+ if (_mi_search(info,info->s->keyinfo+def->key,key_buff,MI_UNIQUE_HASH_LENGTH,
+ SEARCH_FIND,info->s->state.key_root[def->key]))
+ {
+ info->page_changed=1; /* Can't optimize read next */
+ info->lastpos= lastpos;
+ DBUG_RETURN(0); /* No matching rows */
+ }
+
+ for (;;)
+ {
+ if (info->lastpos != disk_pos &&
+ !(*info->s->compare_unique)(info,def,record,info->lastpos))
+ {
+ my_errno=HA_ERR_FOUND_DUPP_UNIQUE;
+ info->errkey= (int) def->key;
+ info->dupp_key_pos= info->lastpos;
+ info->page_changed=1; /* Can't optimize read next */
+ info->lastpos=lastpos;
+ DBUG_PRINT("info",("Found duplicate"));
+ DBUG_RETURN(1); /* Found identical */
+ }
+ if (_mi_search_next(info,info->s->keyinfo+def->key, info->lastkey,
+ MI_UNIQUE_HASH_LENGTH, SEARCH_BIGGER,
+ info->s->state.key_root[def->key]) ||
+ bcmp(info->lastkey, key_buff, MI_UNIQUE_HASH_LENGTH))
+ {
+ info->page_changed=1; /* Can't optimize read next */
+ info->lastpos=lastpos;
+ DBUG_RETURN(0); /* end of tree */
+ }
+ }
+}
+
+
+/* Calculate a hash for a row */
+
+ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const byte *record)
+{
+ const byte *pos, *end;
+ ha_checksum crc= 0;
+ ulong seed1=0, seed2= 4;
+ HA_KEYSEG *keyseg;
+
+ for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
+ {
+ enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
+ uint length=keyseg->length;
+
+ if (keyseg->null_bit)
+ {
+ if (record[keyseg->null_pos] & keyseg->null_bit)
+ {
+ /*
+ Change crc in a way different from an empty string or 0.
+ (This is an optimisation; The code will work even if this isn't
+ done)
+ */
+ crc=((crc << 8) + 511+
+ (crc >> (8*sizeof(ha_checksum)-8)));
+ continue;
+ }
+ }
+ pos= record+keyseg->start;
+ if (keyseg->flag & HA_VAR_LENGTH_PART)
+ {
+ uint pack_length= keyseg->bit_start;
+ uint tmp_length= (pack_length == 1 ? (uint) *(uchar*) pos :
+ uint2korr(pos));
+ pos+= pack_length; /* Skip VARCHAR length */
+ set_if_smaller(length,tmp_length);
+ }
+ else if (keyseg->flag & HA_BLOB_PART)
+ {
+ uint tmp_length=_mi_calc_blob_length(keyseg->bit_start,pos);
+ memcpy_fixed((byte*) &pos,pos+keyseg->bit_start,sizeof(char*));
+ if (!length || length > tmp_length)
+ length=tmp_length; /* The whole blob */
+ }
+ end= pos+length;
+ if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
+ type == HA_KEYTYPE_VARTEXT2)
+ {
+ keyseg->charset->coll->hash_sort(keyseg->charset,
+ (const uchar*) pos, length, &seed1,
+ &seed2);
+ crc^= seed1;
+ }
+ else
+ while (pos != end)
+ crc=((crc << 8) +
+ (((uchar) *(uchar*) pos++))) +
+ (crc >> (8*sizeof(ha_checksum)-8));
+ }
+ return crc;
+}
+
+ /*
+ Returns 0 if both rows have equal unique value
+ */
+
+int mi_unique_comp(MI_UNIQUEDEF *def, const byte *a, const byte *b,
+ my_bool null_are_equal)
+{
+ const byte *pos_a, *pos_b, *end;
+ HA_KEYSEG *keyseg;
+
+ for (keyseg=def->seg ; keyseg < def->end ; keyseg++)
+ {
+ enum ha_base_keytype type=(enum ha_base_keytype) keyseg->type;
+ uint a_length, b_length;
+ a_length= b_length= keyseg->length;
+
+ /* If part is NULL it's regarded as different */
+ if (keyseg->null_bit)
+ {
+ uint tmp;
+ if ((tmp=(a[keyseg->null_pos] & keyseg->null_bit)) !=
+ (uint) (b[keyseg->null_pos] & keyseg->null_bit))
+ return 1;
+ if (tmp)
+ {
+ if (!null_are_equal)
+ return 1;
+ continue;
+ }
+ }
+ pos_a= a+keyseg->start;
+ pos_b= b+keyseg->start;
+ if (keyseg->flag & HA_VAR_LENGTH_PART)
+ {
+ uint pack_length= keyseg->bit_start;
+ if (pack_length == 1)
+ {
+ a_length= (uint) *(uchar*) pos_a++;
+ b_length= (uint) *(uchar*) pos_b++;
+ }
+ else
+ {
+ a_length= uint2korr(pos_a);
+ b_length= uint2korr(pos_b);
+ pos_a+= 2; /* Skip VARCHAR length */
+ pos_b+= 2;
+ }
+ set_if_smaller(a_length, keyseg->length); /* Safety */
+ set_if_smaller(b_length, keyseg->length); /* safety */
+ }
+ else if (keyseg->flag & HA_BLOB_PART)
+ {
+ /* Only compare 'length' characters if length != 0 */
+ a_length= _mi_calc_blob_length(keyseg->bit_start,pos_a);
+ b_length= _mi_calc_blob_length(keyseg->bit_start,pos_b);
+ /* Check that a and b are of equal length */
+ if (keyseg->length)
+ {
+ /*
+ This is used in some cases when we are not interested in comparing
+ the whole length of the blob.
+ */
+ set_if_smaller(a_length, keyseg->length);
+ set_if_smaller(b_length, keyseg->length);
+ }
+ memcpy_fixed((byte*) &pos_a,pos_a+keyseg->bit_start,sizeof(char*));
+ memcpy_fixed((byte*) &pos_b,pos_b+keyseg->bit_start,sizeof(char*));
+ }
+ if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT1 ||
+ type == HA_KEYTYPE_VARTEXT2)
+ {
+ if (mi_compare_text(keyseg->charset, (uchar *) pos_a, a_length,
+ (uchar *) pos_b, b_length, 0, 1))
+ return 1;
+ }
+ else
+ {
+ if (a_length != b_length)
+ return 1;
+ end= pos_a+a_length;
+ while (pos_a != end)
+ {
+ if (*pos_a++ != *pos_b++)
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/storage/myisam/mi_update.c b/storage/myisam/mi_update.c
new file mode 100644
index 00000000000..cda60694008
--- /dev/null
+++ b/storage/myisam/mi_update.c
@@ -0,0 +1,228 @@
+/* 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 */
+
+/* Update an old row in a MyISAM table */
+
+#include "fulltext.h"
+#include "rt_index.h"
+
+int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec)
+{
+ int flag,key_changed,save_errno;
+ reg3 my_off_t pos;
+ uint i;
+ uchar old_key[MI_MAX_KEY_BUFF],*new_key;
+ bool auto_key_changed=0;
+ ulonglong changed;
+ MYISAM_SHARE *share=info->s;
+ ha_checksum old_checksum;
+ DBUG_ENTER("mi_update");
+ LINT_INIT(new_key);
+ LINT_INIT(changed);
+ LINT_INIT(old_checksum);
+
+ DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage",
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ DBUG_RETURN(my_errno= HA_ERR_CRASHED););
+ if (!(info->update & HA_STATE_AKTIV))
+ {
+ DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND);
+ }
+ if (share->options & HA_OPTION_READ_ONLY_DATA)
+ {
+ DBUG_RETURN(my_errno=EACCES);
+ }
+ if (info->state->key_file_length >= share->base.margin_key_file_length)
+ {
+ DBUG_RETURN(my_errno=HA_ERR_INDEX_FILE_FULL);
+ }
+ pos=info->lastpos;
+ if (_mi_readinfo(info,F_WRLCK,1))
+ DBUG_RETURN(my_errno);
+
+ if (share->calc_checksum)
+ old_checksum=info->checksum=(*share->calc_checksum)(info,oldrec);
+ if ((*share->compare_record)(info,oldrec))
+ {
+ save_errno=my_errno;
+ goto err_end; /* Record has changed */
+ }
+
+
+ /* Calculate and check all unique constraints */
+ key_changed=0;
+ for (i=0 ; i < share->state.header.uniques ; i++)
+ {
+ MI_UNIQUEDEF *def=share->uniqueinfo+i;
+ if (mi_unique_comp(def, newrec, oldrec,1) &&
+ mi_check_unique(info, def, newrec, mi_unique_hash(def, newrec),
+ info->lastpos))
+ {
+ save_errno=my_errno;
+ goto err_end;
+ }
+ }
+ if (_mi_mark_file_changed(info))
+ {
+ save_errno=my_errno;
+ goto err_end;
+ }
+
+ /* Check which keys changed from the original row */
+
+ new_key=info->lastkey2;
+ changed=0;
+ for (i=0 ; i < share->base.keys ; i++)
+ {
+ if (((ulonglong) 1 << i) & share->state.key_map)
+ {
+ if (share->keyinfo[i].flag & HA_FULLTEXT )
+ {
+ if (_mi_ft_cmp(info,i,oldrec, newrec))
+ {
+ if ((int) i == info->lastinx)
+ {
+ /*
+ We are changeing the index we are reading on. Mark that
+ the index data has changed and we need to do a full search
+ when doing read-next
+ */
+ key_changed|=HA_STATE_WRITTEN;
+ }
+ changed|=((ulonglong) 1 << i);
+ if (_mi_ft_update(info,i,(char*) old_key,oldrec,newrec,pos))
+ goto err;
+ }
+ }
+ else
+ {
+ uint new_length=_mi_make_key(info,i,new_key,newrec,pos);
+ uint old_length=_mi_make_key(info,i,old_key,oldrec,pos);
+ if (new_length != old_length ||
+ memcmp((byte*) old_key,(byte*) new_key,new_length))
+ {
+ if ((int) i == info->lastinx)
+ key_changed|=HA_STATE_WRITTEN; /* Mark that keyfile changed */
+ changed|=((ulonglong) 1 << i);
+ share->keyinfo[i].version++;
+ if (share->keyinfo[i].ck_delete(info,i,old_key,old_length)) goto err;
+ if (share->keyinfo[i].ck_insert(info,i,new_key,new_length)) goto err;
+ if (share->base.auto_key == i+1)
+ auto_key_changed=1;
+ }
+ }
+ }
+ }
+ /*
+ If we are running with external locking, we must update the index file
+ that something has changed.
+ */
+ if (changed || !my_disable_locking)
+ key_changed|= HA_STATE_CHANGED;
+
+ if (share->calc_checksum)
+ {
+ info->checksum=(*share->calc_checksum)(info,newrec);
+ /* Store new checksum in index file header */
+ key_changed|= HA_STATE_CHANGED;
+ }
+ {
+ /*
+ Don't update index file if data file is not extended and no status
+ information changed
+ */
+ MI_STATUS_INFO state;
+ ha_rows org_split;
+ my_off_t org_delete_link;
+
+ memcpy((char*) &state, (char*) info->state, sizeof(state));
+ org_split= share->state.split;
+ org_delete_link= share->state.dellink;
+ if ((*share->update_record)(info,pos,newrec))
+ goto err;
+ if (!key_changed &&
+ (memcmp((char*) &state, (char*) info->state, sizeof(state)) ||
+ org_split != share->state.split ||
+ org_delete_link != share->state.dellink))
+ key_changed|= HA_STATE_CHANGED; /* Must update index file */
+ }
+ if (auto_key_changed)
+ update_auto_increment(info,newrec);
+ if (share->calc_checksum)
+ share->state.checksum+=(info->checksum - old_checksum);
+
+ info->update= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED | HA_STATE_AKTIV |
+ key_changed);
+ myisam_log_record(MI_LOG_UPDATE,info,newrec,info->lastpos,0);
+ VOID(_mi_writeinfo(info,key_changed ? WRITEINFO_UPDATE_KEYFILE : 0));
+ allow_break(); /* Allow SIGHUP & SIGINT */
+ if (info->invalidator != 0)
+ {
+ DBUG_PRINT("info", ("invalidator... '%s' (update)", info->filename));
+ (*info->invalidator)(info->filename);
+ info->invalidator=0;
+ }
+ DBUG_RETURN(0);
+
+err:
+ DBUG_PRINT("error",("key: %d errno: %d",i,my_errno));
+ save_errno=my_errno;
+ if (changed)
+ key_changed|= HA_STATE_CHANGED;
+ if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL)
+ {
+ info->errkey= (int) i;
+ flag=0;
+ do
+ {
+ if (((ulonglong) 1 << i) & changed)
+ {
+ if (share->keyinfo[i].flag & HA_FULLTEXT)
+ {
+ if ((flag++ && _mi_ft_del(info,i,(char*) new_key,newrec,pos)) ||
+ _mi_ft_add(info,i,(char*) old_key,oldrec,pos))
+ break;
+ }
+ else
+ {
+ uint new_length=_mi_make_key(info,i,new_key,newrec,pos);
+ uint old_length= _mi_make_key(info,i,old_key,oldrec,pos);
+ if ((flag++ && _mi_ck_delete(info,i,new_key,new_length)) ||
+ _mi_ck_write(info,i,old_key,old_length))
+ break;
+ }
+ }
+ } while (i-- != 0);
+ }
+ else
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info);
+ }
+ info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_ROW_CHANGED |
+ key_changed);
+
+ err_end:
+ myisam_log_record(MI_LOG_UPDATE,info,newrec,info->lastpos,my_errno);
+ VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
+ allow_break(); /* Allow SIGHUP & SIGINT */
+ if (save_errno == HA_ERR_KEY_NOT_FOUND)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ save_errno=HA_ERR_CRASHED;
+ }
+ DBUG_RETURN(my_errno=save_errno);
+} /* mi_update */
diff --git a/storage/myisam/mi_write.c b/storage/myisam/mi_write.c
new file mode 100644
index 00000000000..768258a0c82
--- /dev/null
+++ b/storage/myisam/mi_write.c
@@ -0,0 +1,1003 @@
+/* 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 */
+
+/* Write a row to a MyISAM table */
+
+#include "fulltext.h"
+#include "rt_index.h"
+
+#define MAX_POINTER_LENGTH 8
+
+ /* Functions declared in this file */
+
+static int w_search(MI_INFO *info,MI_KEYDEF *keyinfo,
+ uint comp_flag, uchar *key,
+ uint key_length, my_off_t pos, uchar *father_buff,
+ uchar *father_keypos, my_off_t father_page,
+ my_bool insert_last);
+static int _mi_balance_page(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
+ uchar *curr_buff,uchar *father_buff,
+ uchar *father_keypos,my_off_t father_page);
+static uchar *_mi_find_last_pos(MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uint *return_key_length,
+ uchar **after_key);
+int _mi_ck_write_tree(register MI_INFO *info, uint keynr,uchar *key,
+ uint key_length);
+int _mi_ck_write_btree(register MI_INFO *info, uint keynr,uchar *key,
+ uint key_length);
+
+ /* Write new record to database */
+
+int mi_write(MI_INFO *info, byte *record)
+{
+ MYISAM_SHARE *share=info->s;
+ uint i;
+ int save_errno;
+ my_off_t filepos;
+ uchar *buff;
+ my_bool lock_tree= share->concurrent_insert;
+ DBUG_ENTER("mi_write");
+ DBUG_PRINT("enter",("isam: %d data: %d",info->s->kfile,info->dfile));
+
+ DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage",
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ DBUG_RETURN(my_errno= HA_ERR_CRASHED););
+ if (share->options & HA_OPTION_READ_ONLY_DATA)
+ {
+ DBUG_RETURN(my_errno=EACCES);
+ }
+ if (_mi_readinfo(info,F_WRLCK,1))
+ DBUG_RETURN(my_errno);
+ dont_break(); /* Dont allow SIGHUP or SIGINT */
+#if !defined(NO_LOCKING) && defined(USE_RECORD_LOCK)
+ if (!info->locked && my_lock(info->dfile,F_WRLCK,0L,F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE) | info->lock_wait))
+ goto err;
+#endif
+ filepos= ((share->state.dellink != HA_OFFSET_ERROR) ?
+ share->state.dellink :
+ info->state->data_file_length);
+
+ if (share->base.reloc == (ha_rows) 1 &&
+ share->base.records == (ha_rows) 1 &&
+ info->state->records == (ha_rows) 1)
+ { /* System file */
+ my_errno=HA_ERR_RECORD_FILE_FULL;
+ goto err2;
+ }
+ if (info->state->key_file_length >= share->base.margin_key_file_length)
+ {
+ my_errno=HA_ERR_INDEX_FILE_FULL;
+ goto err2;
+ }
+ if (_mi_mark_file_changed(info))
+ goto err2;
+
+ /* Calculate and check all unique constraints */
+ for (i=0 ; i < share->state.header.uniques ; i++)
+ {
+ if (mi_check_unique(info,share->uniqueinfo+i,record,
+ mi_unique_hash(share->uniqueinfo+i,record),
+ HA_OFFSET_ERROR))
+ goto err2;
+ }
+
+ /* Write all keys to indextree */
+
+ buff=info->lastkey2;
+ for (i=0 ; i < share->base.keys ; i++)
+ {
+ if (((ulonglong) 1 << i) & share->state.key_map)
+ {
+ bool local_lock_tree= (lock_tree &&
+ !(info->bulk_insert &&
+ is_tree_inited(&info->bulk_insert[i])));
+ if (local_lock_tree)
+ {
+ rw_wrlock(&share->key_root_lock[i]);
+ share->keyinfo[i].version++;
+ }
+ if (share->keyinfo[i].flag & HA_FULLTEXT )
+ {
+ if (_mi_ft_add(info,i,(char*) buff,record,filepos))
+ {
+ if (local_lock_tree)
+ rw_unlock(&share->key_root_lock[i]);
+ DBUG_PRINT("error",("Got error: %d on write",my_errno));
+ goto err;
+ }
+ }
+ else
+ {
+ if (share->keyinfo[i].ck_insert(info,i,buff,
+ _mi_make_key(info,i,buff,record,filepos)))
+ {
+ if (local_lock_tree)
+ rw_unlock(&share->key_root_lock[i]);
+ DBUG_PRINT("error",("Got error: %d on write",my_errno));
+ goto err;
+ }
+ }
+ if (local_lock_tree)
+ rw_unlock(&share->key_root_lock[i]);
+ }
+ }
+ if (share->calc_checksum)
+ info->checksum=(*share->calc_checksum)(info,record);
+ if (!(info->opt_flag & OPT_NO_ROWS))
+ {
+ if ((*share->write_record)(info,record))
+ goto err;
+ share->state.checksum+=info->checksum;
+ }
+ if (share->base.auto_key)
+ update_auto_increment(info,record);
+ info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_WRITTEN |
+ HA_STATE_ROW_CHANGED);
+ info->state->records++;
+ info->lastpos=filepos;
+ myisam_log_record(MI_LOG_WRITE,info,record,filepos,0);
+ VOID(_mi_writeinfo(info, WRITEINFO_UPDATE_KEYFILE));
+ if (info->invalidator != 0)
+ {
+ DBUG_PRINT("info", ("invalidator... '%s' (update)", info->filename));
+ (*info->invalidator)(info->filename);
+ info->invalidator=0;
+ }
+ allow_break(); /* Allow SIGHUP & SIGINT */
+ DBUG_RETURN(0);
+
+err:
+ save_errno=my_errno;
+ if (my_errno == HA_ERR_FOUND_DUPP_KEY || my_errno == HA_ERR_RECORD_FILE_FULL ||
+ my_errno == HA_ERR_NULL_IN_SPATIAL)
+ {
+ if (info->bulk_insert)
+ {
+ uint j;
+ for (j=0 ; j < share->base.keys ; j++)
+ mi_flush_bulk_insert(info, j);
+ }
+ info->errkey= (int) i;
+ while ( i-- > 0)
+ {
+ if (((ulonglong) 1 << i) & share->state.key_map)
+ {
+ bool local_lock_tree= (lock_tree &&
+ !(info->bulk_insert &&
+ is_tree_inited(&info->bulk_insert[i])));
+ if (local_lock_tree)
+ rw_wrlock(&share->key_root_lock[i]);
+ if (share->keyinfo[i].flag & HA_FULLTEXT)
+ {
+ if (_mi_ft_del(info,i,(char*) buff,record,filepos))
+ {
+ if (local_lock_tree)
+ rw_unlock(&share->key_root_lock[i]);
+ break;
+ }
+ }
+ else
+ {
+ uint key_length=_mi_make_key(info,i,buff,record,filepos);
+ if (_mi_ck_delete(info,i,buff,key_length))
+ {
+ if (local_lock_tree)
+ rw_unlock(&share->key_root_lock[i]);
+ break;
+ }
+ }
+ if (local_lock_tree)
+ rw_unlock(&share->key_root_lock[i]);
+ }
+ }
+ }
+ else
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info);
+ }
+ info->update= (HA_STATE_CHANGED | HA_STATE_WRITTEN | HA_STATE_ROW_CHANGED);
+ my_errno=save_errno;
+err2:
+ save_errno=my_errno;
+ myisam_log_record(MI_LOG_WRITE,info,record,filepos,my_errno);
+ VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE));
+ allow_break(); /* Allow SIGHUP & SIGINT */
+ DBUG_RETURN(my_errno=save_errno);
+} /* mi_write */
+
+
+ /* Write one key to btree */
+
+int _mi_ck_write(MI_INFO *info, uint keynr, uchar *key, uint key_length)
+{
+ DBUG_ENTER("_mi_ck_write");
+
+ if (info->bulk_insert && is_tree_inited(&info->bulk_insert[keynr]))
+ {
+ DBUG_RETURN(_mi_ck_write_tree(info, keynr, key, key_length));
+ }
+ else
+ {
+ DBUG_RETURN(_mi_ck_write_btree(info, keynr, key, key_length));
+ }
+} /* _mi_ck_write */
+
+
+/**********************************************************************
+ * Normal insert code *
+ **********************************************************************/
+
+int _mi_ck_write_btree(register MI_INFO *info, uint keynr, uchar *key,
+ uint key_length)
+{
+ int error;
+ uint comp_flag;
+ MI_KEYDEF *keyinfo=info->s->keyinfo+keynr;
+ my_off_t *root=&info->s->state.key_root[keynr];
+ DBUG_ENTER("_mi_ck_write_btree");
+
+ if (keyinfo->flag & HA_SORT_ALLOWS_SAME)
+ comp_flag=SEARCH_BIGGER; /* Put after same key */
+ else if (keyinfo->flag & (HA_NOSAME|HA_FULLTEXT))
+ {
+ comp_flag=SEARCH_FIND | SEARCH_UPDATE; /* No duplicates */
+ if (keyinfo->flag & HA_NULL_ARE_EQUAL)
+ comp_flag|= SEARCH_NULL_ARE_EQUAL;
+ }
+ else
+ comp_flag=SEARCH_SAME; /* Keys in rec-pos order */
+
+ error=_mi_ck_real_write_btree(info, keyinfo, key, key_length,
+ root, comp_flag);
+ if (info->ft1_to_ft2)
+ {
+ if (!error)
+ error= _mi_ft_convert_to_ft2(info, keynr, key);
+ delete_dynamic(info->ft1_to_ft2);
+ my_free((gptr)info->ft1_to_ft2, MYF(0));
+ info->ft1_to_ft2=0;
+ }
+ DBUG_RETURN(error);
+} /* _mi_ck_write_btree */
+
+int _mi_ck_real_write_btree(MI_INFO *info, MI_KEYDEF *keyinfo,
+ uchar *key, uint key_length, my_off_t *root, uint comp_flag)
+{
+ int error;
+ DBUG_ENTER("_mi_ck_real_write_btree");
+ /* key_length parameter is used only if comp_flag is SEARCH_FIND */
+ if (*root == HA_OFFSET_ERROR ||
+ (error=w_search(info, keyinfo, comp_flag, key, key_length,
+ *root, (uchar *) 0, (uchar*) 0,
+ (my_off_t) 0, 1)) > 0)
+ error=_mi_enlarge_root(info,keyinfo,key,root);
+ DBUG_RETURN(error);
+} /* _mi_ck_real_write_btree */
+
+
+ /* Make a new root with key as only pointer */
+
+int _mi_enlarge_root(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ my_off_t *root)
+{
+ uint t_length,nod_flag;
+ MI_KEY_PARAM s_temp;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_enlarge_root");
+
+ nod_flag= (*root != HA_OFFSET_ERROR) ? share->base.key_reflength : 0;
+ _mi_kpointer(info,info->buff+2,*root); /* if nod */
+ t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,(uchar*) 0,
+ (uchar*) 0, (uchar*) 0, key,&s_temp);
+ mi_putint(info->buff,t_length+2+nod_flag,nod_flag);
+ (*keyinfo->store_key)(keyinfo,info->buff+2+nod_flag,&s_temp);
+ info->buff_used=info->page_changed=1; /* info->buff is used */
+ if ((*root= _mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR ||
+ _mi_write_keypage(info,keyinfo,*root,DFLT_INIT_HITS,info->buff))
+ DBUG_RETURN(-1);
+ DBUG_RETURN(0);
+} /* _mi_enlarge_root */
+
+
+ /*
+ Search after a position for a key and store it there
+ Returns -1 = error
+ 0 = ok
+ 1 = key should be stored in higher tree
+ */
+
+static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ uint comp_flag, uchar *key, uint key_length, my_off_t page,
+ uchar *father_buff, uchar *father_keypos,
+ my_off_t father_page, my_bool insert_last)
+{
+ int error,flag;
+ uint nod_flag, search_key_length;
+ uchar *temp_buff,*keypos;
+ uchar keybuff[MI_MAX_KEY_BUFF];
+ my_bool was_last_key;
+ my_off_t next_page, dupp_key_pos;
+ DBUG_ENTER("w_search");
+ DBUG_PRINT("enter",("page: %ld",page));
+
+ search_key_length= (comp_flag & SEARCH_FIND) ? key_length : USE_WHOLE_KEY;
+ if (!(temp_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
+ MI_MAX_KEY_BUFF*2)))
+ DBUG_RETURN(-1);
+ if (!_mi_fetch_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff,0))
+ goto err;
+
+ flag=(*keyinfo->bin_search)(info,keyinfo,temp_buff,key,search_key_length,
+ comp_flag, &keypos, keybuff, &was_last_key);
+ nod_flag=mi_test_if_nod(temp_buff);
+ if (flag == 0)
+ {
+ uint tmp_key_length;
+ /* get position to record with duplicated key */
+ tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,keybuff);
+ if (tmp_key_length)
+ dupp_key_pos=_mi_dpos(info,0,keybuff+tmp_key_length);
+ else
+ dupp_key_pos= HA_OFFSET_ERROR;
+
+ if (keyinfo->flag & HA_FULLTEXT)
+ {
+ uint off;
+ int subkeys;
+
+ get_key_full_length_rdonly(off, keybuff);
+ subkeys=ft_sintXkorr(keybuff+off);
+ comp_flag=SEARCH_SAME;
+ if (subkeys >= 0)
+ {
+ /* normal word, one-level tree structure */
+ flag=(*keyinfo->bin_search)(info, keyinfo, temp_buff, key,
+ USE_WHOLE_KEY, comp_flag,
+ &keypos, keybuff, &was_last_key);
+ }
+ else
+ {
+ /* popular word. two-level tree. going down */
+ my_off_t root=dupp_key_pos;
+ keyinfo=&info->s->ft2_keyinfo;
+ get_key_full_length_rdonly(off, key);
+ key+=off;
+ keypos-=keyinfo->keylength+nod_flag; /* we'll modify key entry 'in vivo' */
+ error=_mi_ck_real_write_btree(info, keyinfo, key, 0,
+ &root, comp_flag);
+ _mi_dpointer(info, keypos+HA_FT_WLEN, root);
+ subkeys--; /* should there be underflow protection ? */
+ DBUG_ASSERT(subkeys < 0);
+ ft_intXstore(keypos, subkeys);
+ if (!error)
+ error=_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff);
+ my_afree((byte*) temp_buff);
+ DBUG_RETURN(error);
+ }
+ }
+ else /* not HA_FULLTEXT, normal HA_NOSAME key */
+ {
+ info->dupp_key_pos= dupp_key_pos;
+ my_afree((byte*) temp_buff);
+ my_errno=HA_ERR_FOUND_DUPP_KEY;
+ DBUG_RETURN(-1);
+ }
+ }
+ if (flag == MI_FOUND_WRONG_KEY)
+ DBUG_RETURN(-1);
+ if (!was_last_key)
+ insert_last=0;
+ next_page=_mi_kpos(nod_flag,keypos);
+ if (next_page == HA_OFFSET_ERROR ||
+ (error=w_search(info, keyinfo, comp_flag, key, key_length, next_page,
+ temp_buff, keypos, page, insert_last)) >0)
+ {
+ error=_mi_insert(info,keyinfo,key,temp_buff,keypos,keybuff,father_buff,
+ father_keypos,father_page, insert_last);
+ if (_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff))
+ goto err;
+ }
+ my_afree((byte*) temp_buff);
+ DBUG_RETURN(error);
+err:
+ my_afree((byte*) temp_buff);
+ DBUG_PRINT("exit",("Error: %d",my_errno));
+ DBUG_RETURN (-1);
+} /* w_search */
+
+
+ /* Insert new key at right of key_pos */
+ /* Returns 2 if key contains key to upper level */
+
+int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ uchar *key, uchar *anc_buff, uchar *key_pos, uchar *key_buff,
+ uchar *father_buff, uchar *father_key_pos, my_off_t father_page,
+ my_bool insert_last)
+{
+ uint a_length,nod_flag;
+ int t_length;
+ uchar *endpos, *prev_key;
+ MI_KEY_PARAM s_temp;
+ DBUG_ENTER("_mi_insert");
+ DBUG_PRINT("enter",("key_pos: %lx",key_pos));
+ DBUG_EXECUTE("key",_mi_print_key(DBUG_FILE,keyinfo->seg,key,USE_WHOLE_KEY););
+
+ nod_flag=mi_test_if_nod(anc_buff);
+ a_length=mi_getint(anc_buff);
+ endpos= anc_buff+ a_length;
+ prev_key=(key_pos == anc_buff+2+nod_flag ? (uchar*) 0 : key_buff);
+ t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,
+ (key_pos == endpos ? (uchar*) 0 : key_pos),
+ prev_key, prev_key,
+ key,&s_temp);
+#ifndef DBUG_OFF
+ if (key_pos != anc_buff+2+nod_flag && (keyinfo->flag &
+ (HA_BINARY_PACK_KEY | HA_PACK_KEY)))
+ {
+ DBUG_DUMP("prev_key",(byte*) key_buff,_mi_keylength(keyinfo,key_buff));
+ }
+ if (keyinfo->flag & HA_PACK_KEY)
+ {
+ DBUG_PRINT("test",("t_length: %d ref_len: %d",
+ t_length,s_temp.ref_length));
+ DBUG_PRINT("test",("n_ref_len: %d n_length: %d key_pos: %lx",
+ s_temp.n_ref_length,s_temp.n_length,s_temp.key));
+ }
+#endif
+ if (t_length > 0)
+ {
+ if (t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_RETURN(-1);
+ }
+ bmove_upp((byte*) endpos+t_length,(byte*) endpos,(uint) (endpos-key_pos));
+ }
+ else
+ {
+ if (-t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_RETURN(-1);
+ }
+ bmove(key_pos,key_pos-t_length,(uint) (endpos-key_pos)+t_length);
+ }
+ (*keyinfo->store_key)(keyinfo,key_pos,&s_temp);
+ a_length+=t_length;
+ mi_putint(anc_buff,a_length,nod_flag);
+ if (a_length <= keyinfo->block_length)
+ {
+ if (keyinfo->block_length - a_length < 32 &&
+ keyinfo->flag & HA_FULLTEXT && key_pos == endpos &&
+ info->s->base.key_reflength <= info->s->base.rec_reflength &&
+ info->s->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD))
+ {
+ /*
+ Normal word. One-level tree. Page is almost full.
+ Let's consider converting.
+ We'll compare 'key' and the first key at anc_buff
+ */
+ uchar *a=key, *b=anc_buff+2+nod_flag;
+ uint alen, blen, ft2len=info->s->ft2_keyinfo.keylength;
+ /* the very first key on the page is always unpacked */
+ DBUG_ASSERT((*b & 128) == 0);
+#if HA_FT_MAXLEN >= 127
+ blen= mi_uint2korr(b); b+=2;
+#else
+ blen= *b++;
+#endif
+ get_key_length(alen,a);
+ DBUG_ASSERT(info->ft1_to_ft2==0);
+ if (alen == blen &&
+ mi_compare_text(keyinfo->seg->charset, a, alen, b, blen, 0, 0)==0)
+ {
+ /* yup. converting */
+ info->ft1_to_ft2=(DYNAMIC_ARRAY *)
+ my_malloc(sizeof(DYNAMIC_ARRAY), MYF(MY_WME));
+ my_init_dynamic_array(info->ft1_to_ft2, ft2len, 300, 50);
+
+ /*
+ now, adding all keys from the page to dynarray
+ if the page is a leaf (if not keys will be deleted later)
+ */
+ if (!nod_flag)
+ {
+ /* let's leave the first key on the page, though, because
+ we cannot easily dispatch an empty page here */
+ b+=blen+ft2len+2;
+ for (a=anc_buff+a_length ; b < a ; b+=ft2len+2)
+ insert_dynamic(info->ft1_to_ft2, (char*) b);
+
+ /* fixing the page's length - it contains only one key now */
+ mi_putint(anc_buff,2+blen+ft2len+2,0);
+ }
+ /* the rest will be done when we're back from recursion */
+ }
+ }
+ DBUG_RETURN(0); /* There is room on page */
+ }
+ /* Page is full */
+ if (nod_flag)
+ insert_last=0;
+ if (!(keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY)) &&
+ father_buff && !insert_last)
+ DBUG_RETURN(_mi_balance_page(info,keyinfo,key,anc_buff,father_buff,
+ father_key_pos,father_page));
+ DBUG_RETURN(_mi_split_page(info,keyinfo,key,anc_buff,key_buff, insert_last));
+} /* _mi_insert */
+
+
+ /* split a full page in two and assign emerging item to key */
+
+int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo,
+ uchar *key, uchar *buff, uchar *key_buff,
+ my_bool insert_last_key)
+{
+ uint length,a_length,key_ref_length,t_length,nod_flag,key_length;
+ uchar *key_pos,*pos, *after_key;
+ my_off_t new_pos;
+ MI_KEY_PARAM s_temp;
+ DBUG_ENTER("mi_split_page");
+ DBUG_DUMP("buff",(byte*) buff,mi_getint(buff));
+
+ if (info->s->keyinfo+info->lastinx == keyinfo)
+ info->page_changed=1; /* Info->buff is used */
+ info->buff_used=1;
+ nod_flag=mi_test_if_nod(buff);
+ key_ref_length=2+nod_flag;
+ if (insert_last_key)
+ key_pos=_mi_find_last_pos(keyinfo,buff,key_buff, &key_length, &after_key);
+ else
+ key_pos=_mi_find_half_pos(nod_flag,keyinfo,buff,key_buff, &key_length,
+ &after_key);
+ if (!key_pos)
+ DBUG_RETURN(-1);
+
+ length=(uint) (key_pos-buff);
+ a_length=mi_getint(buff);
+ mi_putint(buff,length,nod_flag);
+
+ key_pos=after_key;
+ if (nod_flag)
+ {
+ DBUG_PRINT("test",("Splitting nod"));
+ pos=key_pos-nod_flag;
+ memcpy((byte*) info->buff+2,(byte*) pos,(size_t) nod_flag);
+ }
+
+ /* Move middle item to key and pointer to new page */
+ if ((new_pos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
+ DBUG_RETURN(-1);
+ _mi_kpointer(info,_mi_move_key(keyinfo,key,key_buff),new_pos);
+
+ /* Store new page */
+ if (!(*keyinfo->get_key)(keyinfo,nod_flag,&key_pos,key_buff))
+ DBUG_RETURN(-1);
+
+ t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,(uchar *) 0,
+ (uchar*) 0, (uchar*) 0,
+ key_buff, &s_temp);
+ length=(uint) ((buff+a_length)-key_pos);
+ memcpy((byte*) info->buff+key_ref_length+t_length,(byte*) key_pos,
+ (size_t) length);
+ (*keyinfo->store_key)(keyinfo,info->buff+key_ref_length,&s_temp);
+ mi_putint(info->buff,length+t_length+key_ref_length,nod_flag);
+
+ if (_mi_write_keypage(info,keyinfo,new_pos,DFLT_INIT_HITS,info->buff))
+ DBUG_RETURN(-1);
+ DBUG_DUMP("key",(byte*) key,_mi_keylength(keyinfo,key));
+ DBUG_RETURN(2); /* Middle key up */
+} /* _mi_split_page */
+
+
+ /*
+ Calculate how to much to move to split a page in two
+ Returns pointer to start of key.
+ key will contain the key.
+ return_key_length will contain the length of key
+ after_key will contain the position to where the next key starts
+ */
+
+uchar *_mi_find_half_pos(uint nod_flag, MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uint *return_key_length,
+ uchar **after_key)
+{
+ uint keys,length,key_ref_length;
+ uchar *end,*lastpos;
+ DBUG_ENTER("_mi_find_half_pos");
+
+ key_ref_length=2+nod_flag;
+ length=mi_getint(page)-key_ref_length;
+ page+=key_ref_length;
+ if (!(keyinfo->flag &
+ (HA_PACK_KEY | HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY |
+ HA_BINARY_PACK_KEY)))
+ {
+ key_ref_length=keyinfo->keylength+nod_flag;
+ keys=length/(key_ref_length*2);
+ *return_key_length=keyinfo->keylength;
+ end=page+keys*key_ref_length;
+ *after_key=end+key_ref_length;
+ memcpy(key,end,key_ref_length);
+ DBUG_RETURN(end);
+ }
+
+ end=page+length/2-key_ref_length; /* This is aprox. half */
+ *key='\0';
+ do
+ {
+ lastpos=page;
+ if (!(length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key)))
+ DBUG_RETURN(0);
+ } while (page < end);
+ *return_key_length=length;
+ *after_key=page;
+ DBUG_PRINT("exit",("returns: %lx page: %lx half: %lx",lastpos,page,end));
+ DBUG_RETURN(lastpos);
+} /* _mi_find_half_pos */
+
+
+ /*
+ Split buffer at last key
+ Returns pointer to the start of the key before the last key
+ key will contain the last key
+ */
+
+static uchar *_mi_find_last_pos(MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uint *return_key_length,
+ uchar **after_key)
+{
+ uint keys,length,last_length,key_ref_length;
+ uchar *end,*lastpos,*prevpos;
+ uchar key_buff[MI_MAX_KEY_BUFF];
+ DBUG_ENTER("_mi_find_last_pos");
+
+ key_ref_length=2;
+ length=mi_getint(page)-key_ref_length;
+ page+=key_ref_length;
+ if (!(keyinfo->flag &
+ (HA_PACK_KEY | HA_SPACE_PACK_USED | HA_VAR_LENGTH_KEY |
+ HA_BINARY_PACK_KEY)))
+ {
+ keys=length/keyinfo->keylength-2;
+ *return_key_length=length=keyinfo->keylength;
+ end=page+keys*length;
+ *after_key=end+length;
+ memcpy(key,end,length);
+ DBUG_RETURN(end);
+ }
+
+ LINT_INIT(prevpos);
+ LINT_INIT(last_length);
+ end=page+length-key_ref_length;
+ *key='\0';
+ length=0;
+ lastpos=page;
+ while (page < end)
+ {
+ prevpos=lastpos; lastpos=page;
+ last_length=length;
+ memcpy(key, key_buff, length); /* previous key */
+ if (!(length=(*keyinfo->get_key)(keyinfo,0,&page,key_buff)))
+ {
+ mi_print_error(keyinfo->share, HA_ERR_CRASHED);
+ my_errno=HA_ERR_CRASHED;
+ DBUG_RETURN(0);
+ }
+ }
+ *return_key_length=last_length;
+ *after_key=lastpos;
+ DBUG_PRINT("exit",("returns: %lx page: %lx end: %lx",prevpos,page,end));
+ DBUG_RETURN(prevpos);
+} /* _mi_find_last_pos */
+
+
+ /* Balance page with not packed keys with page on right/left */
+ /* returns 0 if balance was done */
+
+static int _mi_balance_page(register MI_INFO *info, MI_KEYDEF *keyinfo,
+ uchar *key, uchar *curr_buff, uchar *father_buff,
+ uchar *father_key_pos, my_off_t father_page)
+{
+ my_bool right;
+ uint k_length,father_length,father_keylength,nod_flag,curr_keylength,
+ right_length,left_length,new_right_length,new_left_length,extra_length,
+ length,keys;
+ uchar *pos,*buff,*extra_buff;
+ my_off_t next_page,new_pos;
+ byte tmp_part_key[MI_MAX_KEY_BUFF];
+ DBUG_ENTER("_mi_balance_page");
+
+ k_length=keyinfo->keylength;
+ father_length=mi_getint(father_buff);
+ father_keylength=k_length+info->s->base.key_reflength;
+ nod_flag=mi_test_if_nod(curr_buff);
+ curr_keylength=k_length+nod_flag;
+ info->page_changed=1;
+
+ if ((father_key_pos != father_buff+father_length &&
+ (info->state->records & 1)) ||
+ father_key_pos == father_buff+2+info->s->base.key_reflength)
+ {
+ right=1;
+ next_page= _mi_kpos(info->s->base.key_reflength,
+ father_key_pos+father_keylength);
+ buff=info->buff;
+ DBUG_PRINT("test",("use right page: %lu",next_page));
+ }
+ else
+ {
+ right=0;
+ father_key_pos-=father_keylength;
+ next_page= _mi_kpos(info->s->base.key_reflength,father_key_pos);
+ /* Fix that curr_buff is to left */
+ buff=curr_buff; curr_buff=info->buff;
+ DBUG_PRINT("test",("use left page: %lu",next_page));
+ } /* father_key_pos ptr to parting key */
+
+ if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,info->buff,0))
+ goto err;
+ DBUG_DUMP("next",(byte*) info->buff,mi_getint(info->buff));
+
+ /* Test if there is room to share keys */
+
+ left_length=mi_getint(curr_buff);
+ right_length=mi_getint(buff);
+ keys=(left_length+right_length-4-nod_flag*2)/curr_keylength;
+
+ if ((right ? right_length : left_length) + curr_keylength <=
+ keyinfo->block_length)
+ { /* Merge buffs */
+ new_left_length=2+nod_flag+(keys/2)*curr_keylength;
+ new_right_length=2+nod_flag+((keys+1)/2)*curr_keylength;
+ mi_putint(curr_buff,new_left_length,nod_flag);
+ mi_putint(buff,new_right_length,nod_flag);
+
+ if (left_length < new_left_length)
+ { /* Move keys buff -> leaf */
+ pos=curr_buff+left_length;
+ memcpy((byte*) pos,(byte*) father_key_pos, (size_t) k_length);
+ memcpy((byte*) pos+k_length, (byte*) buff+2,
+ (size_t) (length=new_left_length - left_length - k_length));
+ pos=buff+2+length;
+ memcpy((byte*) father_key_pos,(byte*) pos,(size_t) k_length);
+ bmove((byte*) buff+2,(byte*) pos+k_length,new_right_length);
+ }
+ else
+ { /* Move keys -> buff */
+
+ bmove_upp((byte*) buff+new_right_length,(byte*) buff+right_length,
+ right_length-2);
+ length=new_right_length-right_length-k_length;
+ memcpy((byte*) buff+2+length,father_key_pos,(size_t) k_length);
+ pos=curr_buff+new_left_length;
+ memcpy((byte*) father_key_pos,(byte*) pos,(size_t) k_length);
+ memcpy((byte*) buff+2,(byte*) pos+k_length,(size_t) length);
+ }
+
+ if (_mi_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,info->buff) ||
+ _mi_write_keypage(info,keyinfo,father_page,DFLT_INIT_HITS,father_buff))
+ goto err;
+ DBUG_RETURN(0);
+ }
+
+ /* curr_buff[] and buff[] are full, lets split and make new nod */
+
+ extra_buff=info->buff+info->s->base.max_key_block_length;
+ new_left_length=new_right_length=2+nod_flag+(keys+1)/3*curr_keylength;
+ if (keys == 5) /* Too few keys to balance */
+ new_left_length-=curr_keylength;
+ extra_length=nod_flag+left_length+right_length-
+ new_left_length-new_right_length-curr_keylength;
+ DBUG_PRINT("info",("left_length: %d right_length: %d new_left_length: %d new_right_length: %d extra_length: %d",
+ left_length, right_length,
+ new_left_length, new_right_length,
+ extra_length));
+ mi_putint(curr_buff,new_left_length,nod_flag);
+ mi_putint(buff,new_right_length,nod_flag);
+ mi_putint(extra_buff,extra_length+2,nod_flag);
+
+ /* move first largest keys to new page */
+ pos=buff+right_length-extra_length;
+ memcpy((byte*) extra_buff+2,pos,(size_t) extra_length);
+ /* Save new parting key */
+ memcpy(tmp_part_key, pos-k_length,k_length);
+ /* Make place for new keys */
+ bmove_upp((byte*) buff+new_right_length,(byte*) pos-k_length,
+ right_length-extra_length-k_length-2);
+ /* Copy keys from left page */
+ pos= curr_buff+new_left_length;
+ memcpy((byte*) buff+2,(byte*) pos+k_length,
+ (size_t) (length=left_length-new_left_length-k_length));
+ /* Copy old parting key */
+ memcpy((byte*) buff+2+length,father_key_pos,(size_t) k_length);
+
+ /* Move new parting keys up to caller */
+ memcpy((byte*) (right ? key : father_key_pos),pos,(size_t) k_length);
+ memcpy((byte*) (right ? father_key_pos : key),tmp_part_key, k_length);
+
+ if ((new_pos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
+ goto err;
+ _mi_kpointer(info,key+k_length,new_pos);
+ if (_mi_write_keypage(info,keyinfo,(right ? new_pos : next_page),
+ DFLT_INIT_HITS,info->buff) ||
+ _mi_write_keypage(info,keyinfo,(right ? next_page : new_pos),
+ DFLT_INIT_HITS,extra_buff))
+ goto err;
+
+ DBUG_RETURN(1); /* Middle key up */
+
+err:
+ DBUG_RETURN(-1);
+} /* _mi_balance_page */
+
+/**********************************************************************
+ * Bulk insert code *
+ **********************************************************************/
+
+typedef struct {
+ MI_INFO *info;
+ uint keynr;
+} bulk_insert_param;
+
+int _mi_ck_write_tree(register MI_INFO *info, uint keynr, uchar *key,
+ uint key_length)
+{
+ int error;
+ DBUG_ENTER("_mi_ck_write_tree");
+
+ error= tree_insert(&info->bulk_insert[keynr], key,
+ key_length + info->s->rec_reflength,
+ info->bulk_insert[keynr].custom_arg) ? 0 : HA_ERR_OUT_OF_MEM ;
+
+ DBUG_RETURN(error);
+} /* _mi_ck_write_tree */
+
+
+/* typeof(_mi_keys_compare)=qsort_cmp2 */
+
+static int keys_compare(bulk_insert_param *param, uchar *key1, uchar *key2)
+{
+ uint not_used;
+ return ha_key_cmp(param->info->s->keyinfo[param->keynr].seg,
+ key1, key2, USE_WHOLE_KEY, SEARCH_SAME,
+ &not_used);
+}
+
+
+static int keys_free(uchar *key, TREE_FREE mode, bulk_insert_param *param)
+{
+ /*
+ Probably I can use info->lastkey here, but I'm not sure,
+ and to be safe I'd better use local lastkey.
+ */
+ uchar lastkey[MI_MAX_KEY_BUFF];
+ uint keylen;
+ MI_KEYDEF *keyinfo;
+
+ switch (mode) {
+ case free_init:
+ if (param->info->s->concurrent_insert)
+ {
+ rw_wrlock(&param->info->s->key_root_lock[param->keynr]);
+ param->info->s->keyinfo[param->keynr].version++;
+ }
+ return 0;
+ case free_free:
+ keyinfo=param->info->s->keyinfo+param->keynr;
+ keylen=_mi_keylength(keyinfo, key);
+ memcpy(lastkey, key, keylen);
+ return _mi_ck_write_btree(param->info,param->keynr,lastkey,
+ keylen - param->info->s->rec_reflength);
+ case free_end:
+ if (param->info->s->concurrent_insert)
+ rw_unlock(&param->info->s->key_root_lock[param->keynr]);
+ return 0;
+ }
+ return -1;
+}
+
+
+int mi_init_bulk_insert(MI_INFO *info, ulong cache_size, ha_rows rows)
+{
+ MYISAM_SHARE *share=info->s;
+ MI_KEYDEF *key=share->keyinfo;
+ bulk_insert_param *params;
+ uint i, num_keys, total_keylength;
+ ulonglong key_map=0;
+ DBUG_ENTER("_mi_init_bulk_insert");
+ DBUG_PRINT("enter",("cache_size: %lu", cache_size));
+
+ DBUG_ASSERT(!info->bulk_insert &&
+ (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT));
+
+ for (i=total_keylength=num_keys=0 ; i < share->base.keys ; i++)
+ {
+ if (!(key[i].flag & HA_NOSAME) && share->base.auto_key != i+1
+ && test(share->state.key_map & ((ulonglong) 1 << i)))
+ {
+ num_keys++;
+ key_map |=((ulonglong) 1 << i);
+ total_keylength+=key[i].maxlength+TREE_ELEMENT_EXTRA_SIZE;
+ }
+ }
+
+ if (num_keys==0 ||
+ num_keys * MI_MIN_SIZE_BULK_INSERT_TREE > cache_size)
+ DBUG_RETURN(0);
+
+ if (rows && rows*total_keylength < cache_size)
+ cache_size=rows;
+ else
+ cache_size/=total_keylength*16;
+
+ info->bulk_insert=(TREE *)
+ my_malloc((sizeof(TREE)*share->base.keys+
+ sizeof(bulk_insert_param)*num_keys),MYF(0));
+
+ if (!info->bulk_insert)
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ params=(bulk_insert_param *)(info->bulk_insert+share->base.keys);
+ for (i=0 ; i < share->base.keys ; i++)
+ {
+ if (test(key_map & ((ulonglong) 1 << i)))
+ {
+ params->info=info;
+ params->keynr=i;
+ /* Only allocate a 16'th of the buffer at a time */
+ init_tree(&info->bulk_insert[i],
+ cache_size * key[i].maxlength,
+ cache_size * key[i].maxlength, 0,
+ (qsort_cmp2)keys_compare, 0,
+ (tree_element_free) keys_free, (void *)params++);
+ }
+ else
+ info->bulk_insert[i].root=0;
+ }
+
+ DBUG_RETURN(0);
+}
+
+void mi_flush_bulk_insert(MI_INFO *info, uint inx)
+{
+ if (info->bulk_insert)
+ {
+ if (is_tree_inited(&info->bulk_insert[inx]))
+ reset_tree(&info->bulk_insert[inx]);
+ }
+}
+
+void mi_end_bulk_insert(MI_INFO *info)
+{
+ if (info->bulk_insert)
+ {
+ uint i;
+ for (i=0 ; i < info->s->base.keys ; i++)
+ {
+ if (is_tree_inited(& info->bulk_insert[i]))
+ {
+ delete_tree(& info->bulk_insert[i]);
+ }
+ }
+ my_free((void *)info->bulk_insert, MYF(0));
+ info->bulk_insert=0;
+ }
+}
diff --git a/storage/myisam/myisam_ftdump.c b/storage/myisam/myisam_ftdump.c
new file mode 100644
index 00000000000..28aac0a8ecf
--- /dev/null
+++ b/storage/myisam/myisam_ftdump.c
@@ -0,0 +1,277 @@
+/* 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 */
+
+/* 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 "ftdefs.h"
+#include <my_getopt.h>
+
+static void usage();
+static void complain(int val);
+static my_bool get_one_option(int, const struct my_option *, char *);
+
+static int count=0, stats=0, dump=0, lstats=0;
+static my_bool verbose;
+static char *query=NULL;
+static uint lengths[256];
+
+#define MAX_LEN (HA_FT_MAXBYTELEN+10)
+#define HOW_OFTEN_TO_WRITE 10000
+
+static struct my_option my_long_options[] =
+{
+ {"dump", 'd', "Dump index (incl. data offsets and word weights).",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"stats", 's', "Report global stats.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose", 'v', "Be verbose.",
+ (gptr*) &verbose, (gptr*) &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"count", 'c', "Calculate per-word stats (counts and global weights).",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"length", 'l', "Report length distribution.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"help", 'h', "Display help and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"help", '?', "Synonym for -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[])
+{
+ int error=0, subkeys;
+ uint keylen, keylen2=0, inx, doc_cnt=0;
+ float weight= 1.0;
+ double gws, min_gws=0, avg_gws=0;
+ MI_INFO *info;
+ char buf[MAX_LEN], buf2[MAX_LEN], buf_maxlen[MAX_LEN], buf_min_gws[MAX_LEN];
+ ulong total=0, maxlen=0, uniq=0, max_doc_cnt=0;
+ struct { MI_INFO *info; } aio0, *aio=&aio0; /* for GWS_IN_USE */
+
+ MY_INIT(argv[0]);
+ if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
+ exit(error);
+ if (count || dump)
+ verbose=0;
+ if (!count && !dump && !lstats && !query)
+ stats=1;
+
+ if (verbose)
+ setbuf(stdout,NULL);
+
+ if (argc < 2)
+ usage();
+
+ {
+ char *end;
+ inx= (uint) strtoll(argv[1], &end, 10);
+ if (*end)
+ usage();
+ }
+
+ init_key_cache(dflt_key_cache,MI_KEY_BLOCK_LENGTH,USE_BUFFER_INIT, 0, 0);
+
+ if (!(info=mi_open(argv[0],2,HA_OPEN_ABORT_IF_LOCKED)))
+ {
+ error=my_errno;
+ goto err;
+ }
+
+ *buf2=0;
+ aio->info=info;
+
+ if ((inx >= info->s->base.keys) ||
+ !(info->s->keyinfo[inx].flag & HA_FULLTEXT))
+ {
+ printf("Key %d in table %s is not a FULLTEXT key\n", inx, info->filename);
+ goto err;
+ }
+
+ mi_lock_database(info, F_EXTRA_LCK);
+
+ info->lastpos= HA_OFFSET_ERROR;
+ info->update|= HA_STATE_PREV_FOUND;
+
+ while (!(error=mi_rnext(info,NULL,inx)))
+ {
+ keylen=*(info->lastkey);
+
+ subkeys=ft_sintXkorr(info->lastkey+keylen+1);
+ if (subkeys >= 0)
+ weight=*(float*)&subkeys;
+
+#ifdef HAVE_SNPRINTF
+ snprintf(buf,MAX_LEN,"%.*s",(int) keylen,info->lastkey+1);
+#else
+ sprintf(buf,"%.*s",(int) keylen,info->lastkey+1);
+#endif
+ my_casedn_str(default_charset_info,buf);
+ total++;
+ lengths[keylen]++;
+
+ if (count || stats)
+ {
+ doc_cnt++;
+ if (strcmp(buf, buf2))
+ {
+ if (*buf2)
+ {
+ uniq++;
+ avg_gws+=gws=GWS_IN_USE;
+ if (count)
+ printf("%9u %20.7f %s\n",doc_cnt,gws,buf2);
+ if (maxlen<keylen2)
+ {
+ maxlen=keylen2;
+ strmov(buf_maxlen, buf2);
+ }
+ if (max_doc_cnt < doc_cnt)
+ {
+ max_doc_cnt=doc_cnt;
+ strmov(buf_min_gws, buf2);
+ min_gws=gws;
+ }
+ }
+ strmov(buf2, buf);
+ keylen2=keylen;
+ doc_cnt=0;
+ }
+ }
+ if (dump)
+ {
+ if (subkeys>=0)
+ printf("%9lx %20.7f %s\n", (long) info->lastpos,weight,buf);
+ else
+ printf("%9lx => %17d %s\n",(long) info->lastpos,-subkeys,buf);
+ }
+ if (verbose && (total%HOW_OFTEN_TO_WRITE)==0)
+ printf("%10ld\r",total);
+ }
+ mi_lock_database(info, F_UNLCK);
+
+ if (count || stats)
+ {
+ doc_cnt++;
+ if (*buf2)
+ {
+ uniq++;
+ avg_gws+=gws=GWS_IN_USE;
+ if (count)
+ printf("%9u %20.7f %s\n",doc_cnt,gws,buf2);
+ if (maxlen<keylen2)
+ {
+ maxlen=keylen2;
+ strmov(buf_maxlen, buf2);
+ }
+ if (max_doc_cnt < doc_cnt)
+ {
+ max_doc_cnt=doc_cnt;
+ strmov(buf_min_gws, buf2);
+ min_gws=gws;
+ }
+ }
+ }
+
+ if (stats)
+ {
+ count=0;
+ for (inx=0;inx<256;inx++)
+ {
+ count+=lengths[inx];
+ if ((ulong) count >= total/2)
+ break;
+ }
+ printf("Total rows: %lu\nTotal words: %lu\n"
+ "Unique words: %lu\nLongest word: %lu chars (%s)\n"
+ "Median length: %u\n"
+ "Average global weight: %f\n"
+ "Most common word: %lu times, weight: %f (%s)\n",
+ (long) info->state->records, total, uniq, maxlen, buf_maxlen,
+ inx, avg_gws/uniq, max_doc_cnt, min_gws, buf_min_gws);
+ }
+ if (lstats)
+ {
+ count=0;
+ for (inx=0; inx<256; inx++)
+ {
+ count+=lengths[inx];
+ if (count && lengths[inx])
+ printf("%3u: %10lu %5.2f%% %20lu %4.1f%%\n", inx,
+ (ulong) lengths[inx],100.0*lengths[inx]/total,(ulong) count,
+ 100.0*count/total);
+ }
+ }
+
+err:
+ if (error && error != HA_ERR_END_OF_FILE)
+ printf("got error %d\n",my_errno);
+ if (info)
+ mi_close(info);
+ return 0;
+}
+
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument __attribute__((unused)))
+{
+ switch(optid) {
+ case 'd':
+ dump=1;
+ complain(count || query);
+ break;
+ case 's':
+ stats=1;
+ complain(query!=0);
+ break;
+ case 'c':
+ count= 1;
+ complain(dump || query);
+ break;
+ case 'l':
+ lstats=1;
+ complain(query!=0);
+ break;
+ case '?':
+ case 'h':
+ usage();
+ }
+ return 0;
+}
+
+#include <help_start.h>
+
+static void usage()
+{
+ printf("Use: myisam_ftdump <table_name> <index_num>\n");
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+ NETWARE_SET_SCREEN_MODE(1);
+ exit(1);
+}
+
+#include <help_end.h>
+
+static void complain(int val) /* Kinda assert :-) */
+{
+ if (val)
+ {
+ printf("You cannot use these options together!\n");
+ exit(1);
+ }
+}
diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c
new file mode 100644
index 00000000000..519e123e9da
--- /dev/null
+++ b/storage/myisam/myisamchk.c
@@ -0,0 +1,1771 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* Describe, check and repair of MyISAM tables */
+
+#include "fulltext.h"
+
+#include <m_ctype.h>
+#include <stdarg.h>
+#include <my_getopt.h>
+#ifdef HAVE_SYS_VADVICE_H
+#include <sys/vadvise.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+SET_STACK_SIZE(9000) /* Minimum stack size for program */
+
+#ifndef USE_RAID
+#define my_raid_create(A,B,C,D,E,F,G) my_create(A,B,C,G)
+#define my_raid_delete(A,B,C) my_delete(A,B)
+#endif
+
+#ifdef OS2
+#define _sanity(a,b)
+#endif
+
+static uint decode_bits;
+static char **default_argv;
+static const char *load_default_groups[]= { "myisamchk", 0 };
+static const char *set_collation_name, *opt_tmpdir;
+static CHARSET_INFO *set_collation;
+static long opt_myisam_block_size;
+static long opt_key_cache_block_size;
+static const char *my_progname_short;
+static int stopwords_inited= 0;
+static MY_TMPDIR myisamchk_tmpdir;
+
+static const char *type_names[]=
+{ "impossible","char","binary", "short", "long", "float",
+ "double","number","unsigned short",
+ "unsigned long","longlong","ulonglong","int24",
+ "uint24","int8","varchar", "varbin","?",
+ "?"};
+
+static const char *prefix_packed_txt="packed ",
+ *bin_packed_txt="prefix ",
+ *diff_txt="stripped ",
+ *null_txt="NULL",
+ *blob_txt="BLOB ";
+
+static const char *field_pack[]=
+{"","no endspace", "no prespace",
+ "no zeros", "blob", "constant", "table-lockup",
+ "always zero","varchar","unique-hash","?","?"};
+
+
+static void get_options(int *argc,char * * *argv);
+static void print_version(void);
+static void usage(void);
+static int myisamchk(MI_CHECK *param, char *filename);
+static void descript(MI_CHECK *param, register MI_INFO *info, my_string name);
+static int mi_sort_records(MI_CHECK *param, register MI_INFO *info,
+ my_string name, uint sort_key,
+ my_bool write_info, my_bool update_index);
+static int sort_record_index(MI_SORT_PARAM *sort_param, MI_INFO *info,
+ MI_KEYDEF *keyinfo,
+ my_off_t page,uchar *buff,uint sortkey,
+ File new_file, my_bool update_index);
+
+MI_CHECK check_param;
+
+ /* Main program */
+
+int main(int argc, char **argv)
+{
+ int error;
+ MY_INIT(argv[0]);
+ my_progname_short= my_progname+dirname_length(my_progname);
+
+#ifdef __EMX__
+ _wildcard (&argc, &argv);
+#endif
+
+ myisamchk_init(&check_param);
+ check_param.opt_lock_memory=1; /* Lock memory if possible */
+ check_param.using_global_keycache = 0;
+ get_options(&argc,(char***) &argv);
+ myisam_quick_table_bits=decode_bits;
+ error=0;
+ while (--argc >= 0)
+ {
+ int new_error=myisamchk(&check_param, *(argv++));
+ if ((check_param.testflag & T_REP_ANY) != T_REP)
+ check_param.testflag&= ~T_REP;
+ VOID(fflush(stdout));
+ VOID(fflush(stderr));
+ if ((check_param.error_printed | check_param.warning_printed) &&
+ (check_param.testflag & T_FORCE_CREATE) &&
+ (!(check_param.testflag & (T_REP | T_REP_BY_SORT | T_SORT_RECORDS |
+ T_SORT_INDEX))))
+ {
+ uint old_testflag=check_param.testflag;
+ if (!(check_param.testflag & T_REP))
+ check_param.testflag|= T_REP_BY_SORT;
+ check_param.testflag&= ~T_EXTEND; /* Don't needed */
+ error|=myisamchk(&check_param, argv[-1]);
+ check_param.testflag= old_testflag;
+ VOID(fflush(stdout));
+ VOID(fflush(stderr));
+ }
+ else
+ error|=new_error;
+ if (argc && (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO))
+ {
+ puts("\n---------\n");
+ VOID(fflush(stdout));
+ }
+ }
+ if (check_param.total_files > 1)
+ { /* Only if descript */
+ char buff[22],buff2[22];
+ if (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO)
+ puts("\n---------\n");
+ printf("\nTotal of all %d MyISAM-files:\nData records: %9s Deleted blocks: %9s\n",check_param.total_files,llstr(check_param.total_records,buff),
+ llstr(check_param.total_deleted,buff2));
+ }
+ free_defaults(default_argv);
+ free_tmpdir(&myisamchk_tmpdir);
+ ft_free_stopwords();
+ my_end(check_param.testflag & T_INFO ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
+ exit(error);
+#ifndef _lint
+ return 0; /* No compiler warning */
+#endif
+} /* main */
+
+enum options_mc {
+ OPT_CHARSETS_DIR=256, OPT_SET_COLLATION,OPT_START_CHECK_POS,
+ OPT_CORRECT_CHECKSUM, OPT_KEY_BUFFER_SIZE,
+ OPT_KEY_CACHE_BLOCK_SIZE, OPT_MYISAM_BLOCK_SIZE,
+ OPT_READ_BUFFER_SIZE, OPT_WRITE_BUFFER_SIZE, OPT_SORT_BUFFER_SIZE,
+ OPT_SORT_KEY_BLOCKS, OPT_DECODE_BITS, OPT_FT_MIN_WORD_LEN,
+ OPT_FT_MAX_WORD_LEN, OPT_FT_STOPWORD_FILE,
+ OPT_MAX_RECORD_LENGTH
+};
+
+static struct my_option my_long_options[] =
+{
+ {"analyze", 'a',
+ "Analyze distribution of keys. Will make some joins in MySQL faster. You can check the calculated distribution.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"block-search", 'b',
+ "No help available.",
+ 0, 0, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"backup", 'B',
+ "Make a backup of the .MYD file as 'filename-time.BAK'.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"character-sets-dir", OPT_CHARSETS_DIR,
+ "Directory where character sets are.",
+ (gptr*) &charsets_dir, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"check", 'c',
+ "Check table for errors.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"check-only-changed", 'C',
+ "Check only tables that have changed since last check. It also applies to other requested actions (e.g. --analyze will be ignored if the table is already analyzed).",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"correct-checksum", OPT_CORRECT_CHECKSUM,
+ "Correct checksum information for table.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifndef DBUG_OFF
+ {"debug", '#',
+ "Output debug log. Often this is 'd:t:o,filename'.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"description", 'd',
+ "Prints some information about table.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"data-file-length", 'D',
+ "Max length of data file (when recreating data-file when it's full).",
+ (gptr*) &check_param.max_data_file_length,
+ (gptr*) &check_param.max_data_file_length,
+ 0, GET_LL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"extend-check", 'e',
+ "If used when checking a table, ensure that the table is 100 percent consistent, which will take a long time. If used when repairing a table, try to recover every possible row from the data file. Normally this will also find a lot of garbage rows; Don't use this option with repair if you are not totally desperate.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"fast", 'F',
+ "Check only tables that haven't been closed properly. It also applies to other requested actions (e.g. --analyze will be ignored if the table is already analyzed).",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"force", 'f',
+ "Restart with -r if there are any errors in the table. States will be updated as with --update-state.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"HELP", 'H',
+ "Display this help and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"help", '?',
+ "Display this help and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"information", 'i',
+ "Print statistics information about table that is checked.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"keys-used", 'k',
+ "Tell MyISAM to update only some specific keys. # is a bit mask of which keys to use. This can be used to get faster inserts.",
+ (gptr*) &check_param.keys_in_use,
+ (gptr*) &check_param.keys_in_use,
+ 0, GET_ULL, REQUIRED_ARG, -1, 0, 0, 0, 0, 0},
+ {"max-record-length", OPT_MAX_RECORD_LENGTH,
+ "Skip rows bigger than this if myisamchk can't allocate memory to hold it",
+ (gptr*) &check_param.max_record_length,
+ (gptr*) &check_param.max_record_length,
+ 0, GET_ULL, REQUIRED_ARG, LONGLONG_MAX, 0, LONGLONG_MAX, 0, 0, 0},
+ {"medium-check", 'm',
+ "Faster than extend-check, but only finds 99.99% of all errors. Should be good enough for most cases.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"quick", 'q', "Faster repair by not modifying the data file.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"read-only", 'T',
+ "Don't mark table as checked.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"recover", 'r',
+ "Can fix almost anything except unique keys that aren't unique.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"parallel-recover", 'p',
+ "Same as '-r' but creates all the keys in parallel.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"safe-recover", 'o',
+ "Uses old recovery method; Slower than '-r' but can handle a couple of cases where '-r' reports that it can't fix the data file.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"sort-recover", 'n',
+ "Force recovering with sorting even if the temporary file was very big.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef DEBUG
+ {"start-check-pos", OPT_START_CHECK_POS,
+ "No help available.",
+ 0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"set-auto-increment", 'A',
+ "Force auto_increment to start at this or higher value. If no value is given, then sets the next auto_increment value to the highest used value for the auto key + 1.",
+ (gptr*) &check_param.auto_increment_value,
+ (gptr*) &check_param.auto_increment_value,
+ 0, GET_ULL, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"set-collation", OPT_SET_COLLATION,
+ "Change the collation used by the index",
+ (gptr*) &set_collation_name, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"set-variable", 'O',
+ "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"silent", 's',
+ "Only print errors. One can use two -s to make myisamchk very silent.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"sort-index", 'S',
+ "Sort index blocks. This speeds up 'read-next' in applications.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"sort-records", 'R',
+ "Sort records according to an index. This makes your data much more localized and may speed up things. (It may be VERY slow to do a sort the first time!)",
+ (gptr*) &check_param.opt_sort_key,
+ (gptr*) &check_param.opt_sort_key,
+ 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"tmpdir", 't',
+ "Path for temporary files.",
+ (gptr*) &opt_tmpdir,
+ 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"update-state", 'U',
+ "Mark tables as crashed if any errors were found.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"unpack", 'u',
+ "Unpack file packed with myisampack.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose", 'v',
+ "Print more information. This can be used with --description and --check. Use many -v for more verbosity!",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V',
+ "Print version and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"wait", 'w',
+ "Wait if table is locked.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ { "key_buffer_size", OPT_KEY_BUFFER_SIZE, "",
+ (gptr*) &check_param.use_buffers, (gptr*) &check_param.use_buffers, 0,
+ GET_ULONG, REQUIRED_ARG, (long) USE_BUFFER_INIT, (long) MALLOC_OVERHEAD,
+ (long) ~0L, (long) MALLOC_OVERHEAD, (long) IO_SIZE, 0},
+ { "key_cache_block_size", OPT_KEY_CACHE_BLOCK_SIZE, "",
+ (gptr*) &opt_key_cache_block_size,
+ (gptr*) &opt_key_cache_block_size, 0,
+ GET_LONG, REQUIRED_ARG, MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH,
+ MI_MAX_KEY_BLOCK_LENGTH, 0, MI_MIN_KEY_BLOCK_LENGTH, 0},
+ { "myisam_block_size", OPT_MYISAM_BLOCK_SIZE, "",
+ (gptr*) &opt_myisam_block_size, (gptr*) &opt_myisam_block_size, 0,
+ GET_LONG, REQUIRED_ARG, MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH,
+ MI_MAX_KEY_BLOCK_LENGTH, 0, MI_MIN_KEY_BLOCK_LENGTH, 0},
+ { "read_buffer_size", OPT_READ_BUFFER_SIZE, "",
+ (gptr*) &check_param.read_buffer_length,
+ (gptr*) &check_param.read_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
+ (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
+ (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
+ { "write_buffer_size", OPT_WRITE_BUFFER_SIZE, "",
+ (gptr*) &check_param.write_buffer_length,
+ (gptr*) &check_param.write_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
+ (long) READ_BUFFER_INIT, (long) MALLOC_OVERHEAD,
+ (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
+ { "sort_buffer_size", OPT_SORT_BUFFER_SIZE, "",
+ (gptr*) &check_param.sort_buffer_length,
+ (gptr*) &check_param.sort_buffer_length, 0, GET_ULONG, REQUIRED_ARG,
+ (long) SORT_BUFFER_INIT, (long) (MIN_SORT_BUFFER + MALLOC_OVERHEAD),
+ (long) ~0L, (long) MALLOC_OVERHEAD, (long) 1L, 0},
+ { "sort_key_blocks", OPT_SORT_KEY_BLOCKS, "",
+ (gptr*) &check_param.sort_key_blocks,
+ (gptr*) &check_param.sort_key_blocks, 0, GET_ULONG, REQUIRED_ARG,
+ BUFFERS_WHEN_SORTING, 4L, 100L, 0L, 1L, 0},
+ { "decode_bits", OPT_DECODE_BITS, "", (gptr*) &decode_bits,
+ (gptr*) &decode_bits, 0, GET_UINT, REQUIRED_ARG, 9L, 4L, 17L, 0L, 1L, 0},
+ { "ft_min_word_len", OPT_FT_MIN_WORD_LEN, "", (gptr*) &ft_min_word_len,
+ (gptr*) &ft_min_word_len, 0, GET_ULONG, REQUIRED_ARG, 4, 1, HA_FT_MAXCHARLEN,
+ 0, 1, 0},
+ { "ft_max_word_len", OPT_FT_MAX_WORD_LEN, "", (gptr*) &ft_max_word_len,
+ (gptr*) &ft_max_word_len, 0, GET_ULONG, REQUIRED_ARG, HA_FT_MAXCHARLEN, 10,
+ HA_FT_MAXCHARLEN, 0, 1, 0},
+ { "ft_stopword_file", OPT_FT_STOPWORD_FILE,
+ "Use stopwords from this file instead of built-in list.",
+ (gptr*) &ft_stopword_file, (gptr*) &ft_stopword_file, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+#include <help_start.h>
+
+static void print_version(void)
+{
+ printf("%s Ver 2.7 for %s at %s\n", my_progname, SYSTEM_TYPE,
+ MACHINE_TYPE);
+ NETWARE_SET_SCREEN_MODE(1);
+}
+
+
+static void usage(void)
+{
+ print_version();
+ puts("By Monty, for your professional use");
+ puts("This software comes with NO WARRANTY: see the PUBLIC for details.\n");
+ puts("Description, check and repair of MyISAM tables.");
+ puts("Used without options all tables on the command will be checked for errors");
+ printf("Usage: %s [OPTIONS] tables[.MYI]\n", my_progname_short);
+ printf("\nGlobal options:\n");
+#ifndef DBUG_OFF
+ printf("\
+ -#, --debug=... Output debug log. Often this is 'd:t:o,filename'.\n");
+#endif
+ printf("\
+ -?, --help Display this help and exit.\n\
+ -O, --set-variable var=option.\n\
+ Change the value of a variable. Please note that\n\
+ this option is deprecated; you can set variables\n\
+ directly with '--variable-name=value'.\n\
+ -t, --tmpdir=path Path for temporary files. Multiple paths can be\n\
+ specified, separated by ");
+#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
+ printf("semicolon (;)");
+#else
+ printf("colon (:)");
+#endif
+ printf(", they will be used\n\
+ in a round-robin fashion.\n\
+ -s, --silent Only print errors. One can use two -s to make\n\
+ myisamchk very silent.\n\
+ -v, --verbose Print more information. This can be used with\n\
+ --description and --check. Use many -v for more verbosity.\n\
+ -V, --version Print version and exit.\n\
+ -w, --wait Wait if table is locked.\n\n");
+#ifdef DEBUG
+ puts(" --start-check-pos=# Start reading file at given offset.\n");
+#endif
+
+ puts("Check options (check is the default action for myisamchk):\n\
+ -c, --check Check table for errors.\n\
+ -e, --extend-check Check the table VERY throughly. Only use this in\n\
+ extreme cases as myisamchk should normally be able to\n\
+ find out if the table is ok even without this switch.\n\
+ -F, --fast Check only tables that haven't been closed properly.\n\
+ -C, --check-only-changed\n\
+ Check only tables that have changed since last check.\n\
+ -f, --force Restart with '-r' if there are any errors in the table.\n\
+ States will be updated as with '--update-state'.\n\
+ -i, --information Print statistics information about table that is checked.\n\
+ -m, --medium-check Faster than extend-check, but only finds 99.99% of\n\
+ all errors. Should be good enough for most cases.\n\
+ -U --update-state Mark tables as crashed if you find any errors.\n\
+ -T, --read-only Don't mark table as checked.\n");
+
+ puts("Repair options (When using '-r' or '-o'):\n\
+ -B, --backup Make a backup of the .MYD file as 'filename-time.BAK'.\n\
+ --correct-checksum Correct checksum information for table.\n\
+ -D, --data-file-length=# Max length of data file (when recreating data\n\
+ file when it's full).\n\
+ -e, --extend-check Try to recover every possible row from the data file\n\
+ Normally this will also find a lot of garbage rows;\n\
+ Don't use this option if you are not totally desperate.\n\
+ -f, --force Overwrite old temporary files.\n\
+ -k, --keys-used=# Tell MyISAM to update only some specific keys. # is a\n\
+ bit mask of which keys to use. This can be used to\n\
+ get faster inserts.\n\
+ --max-record-length=#\n\
+ Skip rows bigger than this if myisamchk can't allocate\n\
+ memory to hold it.\n\
+ -r, --recover Can fix almost anything except unique keys that aren't\n\
+ unique.\n\
+ -n, --sort-recover Forces recovering with sorting even if the temporary\n\
+ file would be very big.\n\
+ -p, --parallel-recover\n\
+ Uses the same technique as '-r' and '-n', but creates\n\
+ all the keys in parallel, in different threads.\n\
+ -o, --safe-recover Uses old recovery method; Slower than '-r' but can\n\
+ handle a couple of cases where '-r' reports that it\n\
+ can't fix the data file.\n\
+ --character-sets-dir=...\n\
+ Directory where character sets are.\n\
+ --set-collation=name\n\
+ Change the collation used by the index.\n\
+ -q, --quick Faster repair by not modifying the data file.\n\
+ One can give a second '-q' to force myisamchk to\n\
+ modify the original datafile in case of duplicate keys.\n\
+ NOTE: Tables where the data file is currupted can't be\n\
+ fixed with this option.\n\
+ -u, --unpack Unpack file packed with myisampack.\n\
+");
+
+ puts("Other actions:\n\
+ -a, --analyze Analyze distribution of keys. Will make some joins in\n\
+ MySQL faster. You can check the calculated distribution\n\
+ by using '--description --verbose table_name'.\n\
+ -d, --description Prints some information about table.\n\
+ -A, --set-auto-increment[=value]\n\
+ Force auto_increment to start at this or higher value\n\
+ If no value is given, then sets the next auto_increment\n\
+ value to the highest used value for the auto key + 1.\n\
+ -S, --sort-index Sort index blocks. This speeds up 'read-next' in\n\
+ applications.\n\
+ -R, --sort-records=#\n\
+ Sort records according to an index. This makes your\n\
+ data much more localized and may speed up things\n\
+ (It may be VERY slow to do a sort the first time!).\n\
+ -b, --block-search=#\n\
+ Find a record, a block at given offset belongs to.");
+
+ print_defaults("my", load_default_groups);
+ my_print_variables(my_long_options);
+}
+
+#include <help_end.h>
+
+ /* Read options */
+
+static my_bool
+get_one_option(int optid,
+ const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ switch (optid) {
+ case 'a':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_STATISTICS;
+ else
+ check_param.testflag|= T_STATISTICS;
+ break;
+ case 'A':
+ if (argument)
+ check_param.auto_increment_value= strtoull(argument, NULL, 0);
+ else
+ check_param.auto_increment_value= 0; /* Set to max used value */
+ check_param.testflag|= T_AUTO_INC;
+ break;
+ case 'b':
+ check_param.search_after_block= strtoul(argument, NULL, 10);
+ break;
+ case 'B':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_BACKUP_DATA;
+ else
+ check_param.testflag|= T_BACKUP_DATA;
+ break;
+ case 'c':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_CHECK;
+ else
+ check_param.testflag|= T_CHECK;
+ break;
+ case 'C':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~(T_CHECK | T_CHECK_ONLY_CHANGED);
+ else
+ check_param.testflag|= T_CHECK | T_CHECK_ONLY_CHANGED;
+ break;
+ case 'D':
+ check_param.max_data_file_length=strtoll(argument, NULL, 10);
+ break;
+ case 's': /* silent */
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~(T_SILENT | T_VERY_SILENT);
+ else
+ {
+ if (check_param.testflag & T_SILENT)
+ check_param.testflag|= T_VERY_SILENT;
+ check_param.testflag|= T_SILENT;
+ check_param.testflag&= ~T_WRITE_LOOP;
+ }
+ break;
+ case 'w':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_WAIT_FOREVER;
+ else
+ check_param.testflag|= T_WAIT_FOREVER;
+ break;
+ case 'd': /* description if isam-file */
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_DESCRIPT;
+ else
+ check_param.testflag|= T_DESCRIPT;
+ break;
+ case 'e': /* extend check */
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_EXTEND;
+ else
+ check_param.testflag|= T_EXTEND;
+ break;
+ case 'i':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_INFO;
+ else
+ check_param.testflag|= T_INFO;
+ break;
+ case 'f':
+ if (argument == disabled_my_option)
+ {
+ check_param.tmpfile_createflag= O_RDWR | O_TRUNC | O_EXCL;
+ check_param.testflag&= ~(T_FORCE_CREATE | T_UPDATE_STATE);
+ }
+ else
+ {
+ check_param.tmpfile_createflag= O_RDWR | O_TRUNC;
+ check_param.testflag|= T_FORCE_CREATE | T_UPDATE_STATE;
+ }
+ break;
+ case 'F':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_FAST;
+ else
+ check_param.testflag|= T_FAST;
+ break;
+ case 'k':
+ check_param.keys_in_use= (ulonglong) strtoll(argument, NULL, 10);
+ break;
+ case 'm':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_MEDIUM;
+ else
+ check_param.testflag|= T_MEDIUM; /* Medium check */
+ break;
+ case 'r': /* Repair table */
+ check_param.testflag&= ~T_REP_ANY;
+ if (argument != disabled_my_option)
+ check_param.testflag|= T_REP_BY_SORT;
+ break;
+ case 'p':
+ check_param.testflag&= ~T_REP_ANY;
+ if (argument != disabled_my_option)
+ check_param.testflag|= T_REP_PARALLEL;
+ break;
+ case 'o':
+ check_param.testflag&= ~T_REP_ANY;
+ check_param.force_sort= 0;
+ if (argument != disabled_my_option)
+ {
+ check_param.testflag|= T_REP;
+ my_disable_async_io= 1; /* More safety */
+ }
+ break;
+ case 'n':
+ check_param.testflag&= ~T_REP_ANY;
+ if (argument == disabled_my_option)
+ check_param.force_sort= 0;
+ else
+ {
+ check_param.testflag|= T_REP_BY_SORT;
+ check_param.force_sort= 1;
+ }
+ break;
+ case 'q':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~(T_QUICK | T_FORCE_UNIQUENESS);
+ else
+ check_param.testflag|=
+ (check_param.testflag & T_QUICK) ? T_FORCE_UNIQUENESS : T_QUICK;
+ break;
+ case 'u':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~(T_UNPACK | T_REP_BY_SORT);
+ else
+ check_param.testflag|= T_UNPACK | T_REP_BY_SORT;
+ break;
+ case 'v': /* Verbose */
+ if (argument == disabled_my_option)
+ {
+ check_param.testflag&= ~T_VERBOSE;
+ check_param.verbose=0;
+ }
+ else
+ {
+ check_param.testflag|= T_VERBOSE;
+ check_param.verbose++;
+ }
+ break;
+ case 'R': /* Sort records */
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_SORT_RECORDS;
+ else
+ {
+ check_param.testflag|= T_SORT_RECORDS;
+ check_param.opt_sort_key= (uint) atoi(argument) - 1;
+ if (check_param.opt_sort_key >= MI_MAX_KEY)
+ {
+ fprintf(stderr,
+ "The value of the sort key is bigger than max key: %d.\n",
+ MI_MAX_KEY);
+ exit(1);
+ }
+ }
+ break;
+ case 'S': /* Sort index */
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_SORT_INDEX;
+ else
+ check_param.testflag|= T_SORT_INDEX;
+ break;
+ case 'T':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_READONLY;
+ else
+ check_param.testflag|= T_READONLY;
+ break;
+ case 'U':
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_UPDATE_STATE;
+ else
+ check_param.testflag|= T_UPDATE_STATE;
+ break;
+ case '#':
+ if (argument == disabled_my_option)
+ {
+ DBUG_POP();
+ }
+ else
+ {
+ DBUG_PUSH(argument ? argument : "d:t:o,/tmp/myisamchk.trace");
+ }
+ break;
+ case 'V':
+ print_version();
+ exit(0);
+ case OPT_CORRECT_CHECKSUM:
+ if (argument == disabled_my_option)
+ check_param.testflag&= ~T_CALC_CHECKSUM;
+ else
+ check_param.testflag|= T_CALC_CHECKSUM;
+ break;
+#ifdef DEBUG /* Only useful if debugging */
+ case OPT_START_CHECK_POS:
+ check_param.start_check_pos= strtoull(argument, NULL, 0);
+ break;
+#endif
+ case 'H':
+ my_print_help(my_long_options);
+ exit(0);
+ case '?':
+ usage();
+ exit(0);
+ }
+ return 0;
+}
+
+
+static void get_options(register int *argc,register char ***argv)
+{
+ int ho_error;
+
+ load_defaults("my", load_default_groups, argc, argv);
+ default_argv= *argv;
+ if (isatty(fileno(stdout)))
+ check_param.testflag|=T_WRITE_LOOP;
+
+ if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
+ exit(ho_error);
+
+ /* If using repair, then update checksum if one uses --update-state */
+ if ((check_param.testflag & T_UPDATE_STATE) &&
+ (check_param.testflag & T_REP_ANY))
+ check_param.testflag|= T_CALC_CHECKSUM;
+
+ if (*argc == 0)
+ {
+ usage();
+ exit(-1);
+ }
+
+ if ((check_param.testflag & T_UNPACK) &&
+ (check_param.testflag & (T_QUICK | T_SORT_RECORDS)))
+ {
+ VOID(fprintf(stderr,
+ "%s: --unpack can't be used with --quick or --sort-records\n",
+ my_progname_short));
+ exit(1);
+ }
+ if ((check_param.testflag & T_READONLY) &&
+ (check_param.testflag &
+ (T_REP_ANY | T_STATISTICS | T_AUTO_INC |
+ T_SORT_RECORDS | T_SORT_INDEX | T_FORCE_CREATE)))
+ {
+ VOID(fprintf(stderr,
+ "%s: Can't use --readonly when repairing or sorting\n",
+ my_progname_short));
+ exit(1);
+ }
+
+ if (init_tmpdir(&myisamchk_tmpdir, opt_tmpdir))
+ exit(1);
+
+ check_param.tmpdir=&myisamchk_tmpdir;
+ check_param.key_cache_block_size= opt_key_cache_block_size;
+
+ if (set_collation_name)
+ if (!(set_collation= get_charset_by_name(set_collation_name,
+ MYF(MY_WME))))
+ exit(1);
+
+ myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
+ return;
+} /* get options */
+
+
+ /* Check table */
+
+static int myisamchk(MI_CHECK *param, my_string filename)
+{
+ int error,lock_type,recreate;
+ int rep_quick= param->testflag & (T_QUICK | T_FORCE_UNIQUENESS);
+ uint raid_chunks;
+ MI_INFO *info;
+ File datafile;
+ char llbuff[22],llbuff2[22];
+ my_bool state_updated=0;
+ MYISAM_SHARE *share;
+ DBUG_ENTER("myisamchk");
+
+ param->out_flag=error=param->warning_printed=param->error_printed=
+ recreate=0;
+ datafile=0;
+ param->isam_file_name=filename; /* For error messages */
+ if (!(info=mi_open(filename,
+ (param->testflag & (T_DESCRIPT | T_READONLY)) ?
+ O_RDONLY : O_RDWR,
+ HA_OPEN_FOR_REPAIR |
+ ((param->testflag & T_WAIT_FOREVER) ?
+ HA_OPEN_WAIT_IF_LOCKED :
+ (param->testflag & T_DESCRIPT) ?
+ HA_OPEN_IGNORE_IF_LOCKED : HA_OPEN_ABORT_IF_LOCKED))))
+ {
+ /* Avoid twice printing of isam file name */
+ param->error_printed=1;
+ switch (my_errno) {
+ case HA_ERR_CRASHED:
+ mi_check_print_error(param,"'%s' doesn't have a correct index definition. You need to recreate it before you can do a repair",filename);
+ break;
+ case HA_ERR_NOT_A_TABLE:
+ mi_check_print_error(param,"'%s' is not a MyISAM-table",filename);
+ break;
+ case HA_ERR_CRASHED_ON_USAGE:
+ mi_check_print_error(param,"'%s' is marked as crashed",filename);
+ break;
+ case HA_ERR_CRASHED_ON_REPAIR:
+ mi_check_print_error(param,"'%s' is marked as crashed after last repair",filename);
+ break;
+ case HA_ERR_OLD_FILE:
+ mi_check_print_error(param,"'%s' is a old type of MyISAM-table", filename);
+ break;
+ case HA_ERR_END_OF_FILE:
+ mi_check_print_error(param,"Couldn't read complete header from '%s'", filename);
+ break;
+ case EAGAIN:
+ mi_check_print_error(param,"'%s' is locked. Use -w to wait until unlocked",filename);
+ break;
+ case ENOENT:
+ mi_check_print_error(param,"File '%s' doesn't exist",filename);
+ break;
+ case EACCES:
+ mi_check_print_error(param,"You don't have permission to use '%s'",filename);
+ break;
+ default:
+ mi_check_print_error(param,"%d when opening MyISAM-table '%s'",
+ my_errno,filename);
+ break;
+ }
+ DBUG_RETURN(1);
+ }
+ share=info->s;
+ share->options&= ~HA_OPTION_READ_ONLY_DATA; /* We are modifing it */
+ share->tot_locks-= share->r_locks;
+ share->r_locks=0;
+ raid_chunks=share->base.raid_chunks;
+
+ /*
+ Skip the checking of the file if:
+ We are using --fast and the table is closed properly
+ We are using --check-only-changed-tables and the table hasn't changed
+ */
+ if (param->testflag & (T_FAST | T_CHECK_ONLY_CHANGED))
+ {
+ my_bool need_to_check= mi_is_crashed(info) || share->state.open_count != 0;
+
+ if ((param->testflag & (T_REP_ANY | T_SORT_RECORDS)) &&
+ ((share->state.changed & (STATE_CHANGED | STATE_CRASHED |
+ STATE_CRASHED_ON_REPAIR) ||
+ !(param->testflag & T_CHECK_ONLY_CHANGED))))
+ need_to_check=1;
+
+ if (info->s->base.keys && info->state->records)
+ {
+ if ((param->testflag & T_STATISTICS) &&
+ (share->state.changed & STATE_NOT_ANALYZED))
+ need_to_check=1;
+ if ((param->testflag & T_SORT_INDEX) &&
+ (share->state.changed & STATE_NOT_SORTED_PAGES))
+ need_to_check=1;
+ if ((param->testflag & T_REP_BY_SORT) &&
+ (share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
+ need_to_check=1;
+ }
+ if ((param->testflag & T_CHECK_ONLY_CHANGED) &&
+ (share->state.changed & (STATE_CHANGED | STATE_CRASHED |
+ STATE_CRASHED_ON_REPAIR)))
+ need_to_check=1;
+ if (!need_to_check)
+ {
+ if (!(param->testflag & T_SILENT) || param->testflag & T_INFO)
+ printf("MyISAM file: %s is already checked\n",filename);
+ if (mi_close(info))
+ {
+ mi_check_print_error(param,"%d when closing MyISAM-table '%s'",
+ my_errno,filename);
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+ }
+ }
+ if ((param->testflag & (T_REP_ANY | T_STATISTICS |
+ T_SORT_RECORDS | T_SORT_INDEX)) &&
+ (((param->testflag & T_UNPACK) &&
+ share->data_file_type == COMPRESSED_RECORD) ||
+ mi_uint2korr(share->state.header.state_info_length) !=
+ MI_STATE_INFO_SIZE ||
+ mi_uint2korr(share->state.header.base_info_length) !=
+ MI_BASE_INFO_SIZE ||
+ ((param->keys_in_use & ~share->state.key_map) &
+ (((ulonglong) 1L << share->base.keys)-1)) ||
+ test_if_almost_full(info) ||
+ info->s->state.header.file_version[3] != myisam_file_magic[3] ||
+ (set_collation &&
+ set_collation->number != share->state.header.language) ||
+ myisam_block_size != MI_KEY_BLOCK_LENGTH))
+ {
+ if (set_collation)
+ param->language= set_collation->number;
+ if (recreate_table(param, &info,filename))
+ {
+ VOID(fprintf(stderr,
+ "MyISAM-table '%s' is not fixed because of errors\n",
+ filename));
+ return(-1);
+ }
+ recreate=1;
+ if (!(param->testflag & T_REP_ANY))
+ {
+ param->testflag|=T_REP_BY_SORT; /* if only STATISTICS */
+ if (!(param->testflag & T_SILENT))
+ printf("- '%s' has old table-format. Recreating index\n",filename);
+ rep_quick|=T_QUICK;
+ }
+ share=info->s;
+ share->tot_locks-= share->r_locks;
+ share->r_locks=0;
+ }
+
+ if (param->testflag & T_DESCRIPT)
+ {
+ param->total_files++;
+ param->total_records+=info->state->records;
+ param->total_deleted+=info->state->del;
+ descript(param, info, filename);
+ }
+ else
+ {
+ if (!stopwords_inited++)
+ ft_init_stopwords();
+
+ if (!(param->testflag & T_READONLY))
+ lock_type = F_WRLCK; /* table is changed */
+ else
+ lock_type= F_RDLCK;
+ if (info->lock_type == F_RDLCK)
+ info->lock_type=F_UNLCK; /* Read only table */
+ if (_mi_readinfo(info,lock_type,0))
+ {
+ mi_check_print_error(param,"Can't lock indexfile of '%s', error: %d",
+ filename,my_errno);
+ param->error_printed=0;
+ goto end2;
+ }
+ /*
+ _mi_readinfo() has locked the table.
+ We mark the table as locked (without doing file locks) to be able to
+ use functions that only works on locked tables (like row caching).
+ */
+ mi_lock_database(info, F_EXTRA_LCK);
+ datafile=info->dfile;
+
+ if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
+ {
+ if (param->testflag & T_REP_ANY)
+ {
+ ulonglong tmp=share->state.key_map;
+ share->state.key_map= (((ulonglong) 1 << share->base.keys)-1)
+ & param->keys_in_use;
+ if (tmp != share->state.key_map)
+ info->update|=HA_STATE_CHANGED;
+ }
+ if (rep_quick && chk_del(param, info, param->testflag & ~T_VERBOSE))
+ {
+ if (param->testflag & T_FORCE_CREATE)
+ {
+ rep_quick=0;
+ mi_check_print_info(param,"Creating new data file\n");
+ }
+ else
+ {
+ error=1;
+ mi_check_print_error(param,
+ "Quick-recover aborted; Run recovery without switch 'q'");
+ }
+ }
+ if (!error)
+ {
+ if ((param->testflag & (T_REP_BY_SORT | T_REP_PARALLEL)) &&
+ (share->state.key_map ||
+ (rep_quick && !param->keys_in_use && !recreate)) &&
+ mi_test_if_sort_rep(info, info->state->records,
+ info->s->state.key_map,
+ param->force_sort))
+ {
+ if (param->testflag & T_REP_BY_SORT)
+ error=mi_repair_by_sort(param,info,filename,rep_quick);
+ else
+ error=mi_repair_parallel(param,info,filename,rep_quick);
+ state_updated=1;
+ }
+ else if (param->testflag & T_REP_ANY)
+ error=mi_repair(param, info,filename,rep_quick);
+ }
+ if (!error && param->testflag & T_SORT_RECORDS)
+ {
+ /*
+ The data file is nowadays reopened in the repair code so we should
+ soon remove the following reopen-code
+ */
+#ifndef TO_BE_REMOVED
+ if (param->out_flag & O_NEW_DATA)
+ { /* Change temp file to org file */
+ VOID(my_close(info->dfile,MYF(MY_WME))); /* Close new file */
+ error|=change_to_newfile(filename,MI_NAME_DEXT,DATA_TMP_EXT,
+ raid_chunks,
+ MYF(0));
+ if (mi_open_datafile(info,info->s, -1))
+ error=1;
+ param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */
+ param->read_cache.file=info->dfile;
+ }
+#endif
+ if (! error)
+ {
+ uint key;
+ /*
+ We can't update the index in mi_sort_records if we have a
+ prefix compressed or fulltext index
+ */
+ my_bool update_index=1;
+ for (key=0 ; key < share->base.keys; key++)
+ if (share->keyinfo[key].flag & (HA_BINARY_PACK_KEY|HA_FULLTEXT))
+ update_index=0;
+
+ error=mi_sort_records(param,info,filename,param->opt_sort_key,
+ /* what is the following parameter for ? */
+ (my_bool) !(param->testflag & T_REP),
+ update_index);
+ datafile=info->dfile; /* This is now locked */
+ if (!error && !update_index)
+ {
+ if (param->verbose)
+ puts("Table had a compressed index; We must now recreate the index");
+ error=mi_repair_by_sort(param,info,filename,1);
+ }
+ }
+ }
+ if (!error && param->testflag & T_SORT_INDEX)
+ error=mi_sort_index(param,info,filename);
+ if (!error)
+ share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
+ STATE_CRASHED_ON_REPAIR);
+ else
+ mi_mark_crashed(info);
+ }
+ else if ((param->testflag & T_CHECK) || !(param->testflag & T_AUTO_INC))
+ {
+ if (!(param->testflag & T_SILENT) || param->testflag & T_INFO)
+ printf("Checking MyISAM file: %s\n",filename);
+ if (!(param->testflag & T_SILENT))
+ printf("Data records: %7s Deleted blocks: %7s\n",
+ llstr(info->state->records,llbuff),
+ llstr(info->state->del,llbuff2));
+ error =chk_status(param,info);
+ share->state.key_map &=param->keys_in_use;
+ error =chk_size(param,info);
+ if (!error || !(param->testflag & (T_FAST | T_FORCE_CREATE)))
+ error|=chk_del(param, info,param->testflag);
+ if ((!error || (!(param->testflag & (T_FAST | T_FORCE_CREATE)) &&
+ !param->start_check_pos)))
+ {
+ error|=chk_key(param, info);
+ if (!error && (param->testflag & (T_STATISTICS | T_AUTO_INC)))
+ error=update_state_info(param, info,
+ ((param->testflag & T_STATISTICS) ?
+ UPDATE_STAT : 0) |
+ ((param->testflag & T_AUTO_INC) ?
+ UPDATE_AUTO_INC : 0));
+ }
+ if ((!rep_quick && !error) ||
+ !(param->testflag & (T_FAST | T_FORCE_CREATE)))
+ {
+ if (param->testflag & (T_EXTEND | T_MEDIUM))
+ VOID(init_key_cache(dflt_key_cache,opt_key_cache_block_size,
+ param->use_buffers, 0, 0));
+ VOID(init_io_cache(&param->read_cache,datafile,
+ (uint) param->read_buffer_length,
+ READ_CACHE,
+ (param->start_check_pos ?
+ param->start_check_pos :
+ share->pack.header_length),
+ 1,
+ MYF(MY_WME)));
+ lock_memory(param);
+ if ((info->s->options & (HA_OPTION_PACK_RECORD |
+ HA_OPTION_COMPRESS_RECORD)) ||
+ (param->testflag & (T_EXTEND | T_MEDIUM)))
+ error|=chk_data_link(param, info, param->testflag & T_EXTEND);
+ error|=flush_blocks(param, share->key_cache, share->kfile);
+ VOID(end_io_cache(&param->read_cache));
+ }
+ if (!error)
+ {
+ if ((share->state.changed & STATE_CHANGED) &&
+ (param->testflag & T_UPDATE_STATE))
+ info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
+ share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
+ STATE_CRASHED_ON_REPAIR);
+ }
+ else if (!mi_is_crashed(info) &&
+ (param->testflag & T_UPDATE_STATE))
+ { /* Mark crashed */
+ mi_mark_crashed(info);
+ info->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
+ }
+ }
+ }
+ if ((param->testflag & T_AUTO_INC) ||
+ ((param->testflag & T_REP_ANY) && info->s->base.auto_key))
+ update_auto_increment_key(param, info,
+ (my_bool) !test(param->testflag & T_AUTO_INC));
+
+ if (!(param->testflag & T_DESCRIPT))
+ {
+ if (info->update & HA_STATE_CHANGED && ! (param->testflag & T_READONLY))
+ error|=update_state_info(param, info,
+ UPDATE_OPEN_COUNT |
+ (((param->testflag & T_REP_ANY) ?
+ UPDATE_TIME : 0) |
+ (state_updated ? UPDATE_STAT : 0) |
+ ((param->testflag & T_SORT_RECORDS) ?
+ UPDATE_SORT : 0)));
+ VOID(lock_file(param, share->kfile,0L,F_UNLCK,"indexfile",filename));
+ info->update&= ~HA_STATE_CHANGED;
+ }
+ mi_lock_database(info, F_UNLCK);
+end2:
+ if (mi_close(info))
+ {
+ mi_check_print_error(param,"%d when closing MyISAM-table '%s'",my_errno,filename);
+ DBUG_RETURN(1);
+ }
+ if (error == 0)
+ {
+ if (param->out_flag & O_NEW_DATA)
+ error|=change_to_newfile(filename,MI_NAME_DEXT,DATA_TMP_EXT,
+ raid_chunks,
+ ((param->testflag & T_BACKUP_DATA) ?
+ MYF(MY_REDEL_MAKE_BACKUP) : MYF(0)));
+ if (param->out_flag & O_NEW_INDEX)
+ error|=change_to_newfile(filename,MI_NAME_IEXT,INDEX_TMP_EXT,0,
+ MYF(0));
+ }
+ VOID(fflush(stdout)); VOID(fflush(stderr));
+ if (param->error_printed)
+ {
+ if (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX))
+ {
+ VOID(fprintf(stderr,
+ "MyISAM-table '%s' is not fixed because of errors\n",
+ filename));
+ if (param->testflag & T_REP_ANY)
+ VOID(fprintf(stderr,
+ "Try fixing it by using the --safe-recover (-o), the --force (-f) option or by not using the --quick (-q) flag\n"));
+ }
+ else if (!(param->error_printed & 2) &&
+ !(param->testflag & T_FORCE_CREATE))
+ VOID(fprintf(stderr,
+ "MyISAM-table '%s' is corrupted\nFix it using switch \"-r\" or \"-o\"\n",
+ filename));
+ }
+ else if (param->warning_printed &&
+ ! (param->testflag & (T_REP_ANY | T_SORT_RECORDS | T_SORT_INDEX |
+ T_FORCE_CREATE)))
+ VOID(fprintf(stderr, "MyISAM-table '%s' is usable but should be fixed\n",
+ filename));
+ VOID(fflush(stderr));
+ DBUG_RETURN(error);
+} /* myisamchk */
+
+
+ /* Write info about table */
+
+static void descript(MI_CHECK *param, register MI_INFO *info, my_string name)
+{
+ uint key,keyseg_nr,field,start;
+ reg3 MI_KEYDEF *keyinfo;
+ reg2 HA_KEYSEG *keyseg;
+ reg4 const char *text;
+ char buff[160],length[10],*pos,*end;
+ enum en_fieldtype type;
+ MYISAM_SHARE *share=info->s;
+ char llbuff[22],llbuff2[22];
+ DBUG_ENTER("describe");
+
+ printf("\nMyISAM file: %s\n",name);
+ fputs("Record format: ",stdout);
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ puts("Compressed");
+ else if (share->options & HA_OPTION_PACK_RECORD)
+ puts("Packed");
+ else
+ puts("Fixed length");
+ printf("Character set: %s (%d)\n",
+ get_charset_name(share->state.header.language),
+ share->state.header.language);
+
+ if (param->testflag & T_VERBOSE)
+ {
+ printf("File-version: %d\n",
+ (int) share->state.header.file_version[3]);
+ if (share->state.create_time)
+ {
+ get_date(buff,1,share->state.create_time);
+ printf("Creation time: %s\n",buff);
+ }
+ if (share->state.check_time)
+ {
+ get_date(buff,1,share->state.check_time);
+ printf("Recover time: %s\n",buff);
+ }
+ pos=buff;
+ if (share->state.changed & STATE_CRASHED)
+ strmov(buff,"crashed");
+ else
+ {
+ if (share->state.open_count)
+ pos=strmov(pos,"open,");
+ if (share->state.changed & STATE_CHANGED)
+ pos=strmov(pos,"changed,");
+ else
+ pos=strmov(pos,"checked,");
+ if (!(share->state.changed & STATE_NOT_ANALYZED))
+ pos=strmov(pos,"analyzed,");
+ if (!(share->state.changed & STATE_NOT_OPTIMIZED_KEYS))
+ pos=strmov(pos,"optimized keys,");
+ if (!(share->state.changed & STATE_NOT_SORTED_PAGES))
+ pos=strmov(pos,"sorted index pages,");
+ pos[-1]=0; /* Remove extra ',' */
+ }
+ printf("Status: %s\n",buff);
+ if (share->base.auto_key)
+ {
+ printf("Auto increment key: %13d Last value: %13s\n",
+ share->base.auto_key,
+ llstr(share->state.auto_increment,llbuff));
+ }
+ if (share->base.raid_type)
+ {
+ printf("RAID: Type: %u Chunks: %u Chunksize: %lu\n",
+ share->base.raid_type,
+ share->base.raid_chunks,
+ share->base.raid_chunksize);
+ }
+ if (share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD))
+ printf("Checksum: %23s\n",llstr(info->s->state.checksum,llbuff));
+;
+ if (share->options & HA_OPTION_DELAY_KEY_WRITE)
+ printf("Keys are only flushed at close\n");
+
+ }
+ printf("Data records: %13s Deleted blocks: %13s\n",
+ llstr(info->state->records,llbuff),llstr(info->state->del,llbuff2));
+ if (param->testflag & T_SILENT)
+ DBUG_VOID_RETURN; /* This is enough */
+
+ if (param->testflag & T_VERBOSE)
+ {
+#ifdef USE_RELOC
+ printf("Init-relocation: %13s\n",llstr(share->base.reloc,llbuff));
+#endif
+ printf("Datafile parts: %13s Deleted data: %13s\n",
+ llstr(share->state.split,llbuff),
+ llstr(info->state->empty,llbuff2));
+ printf("Datafile pointer (bytes):%9d Keyfile pointer (bytes):%9d\n",
+ share->rec_reflength,share->base.key_reflength);
+ printf("Datafile length: %13s Keyfile length: %13s\n",
+ llstr(info->state->data_file_length,llbuff),
+ llstr(info->state->key_file_length,llbuff2));
+
+ if (info->s->base.reloc == 1L && info->s->base.records == 1L)
+ puts("This is a one-record table");
+ else
+ {
+ if (share->base.max_data_file_length != HA_OFFSET_ERROR ||
+ share->base.max_key_file_length != HA_OFFSET_ERROR)
+ printf("Max datafile length: %13s Max keyfile length: %13s\n",
+ llstr(share->base.max_data_file_length-1,llbuff),
+ llstr(share->base.max_key_file_length-1,llbuff2));
+ }
+ }
+
+ printf("Recordlength: %13d\n",(int) share->base.pack_reclength);
+ if (share->state.key_map != (((ulonglong) 1 << share->base.keys) -1))
+ {
+ longlong2str(share->state.key_map,buff,2);
+ printf("Using only keys '%s' of %d possibly keys\n",
+ buff, share->base.keys);
+ }
+ puts("\ntable description:");
+ printf("Key Start Len Index Type");
+ if (param->testflag & T_VERBOSE)
+ printf(" Rec/key Root Blocksize");
+ VOID(putchar('\n'));
+
+ for (key=keyseg_nr=0, keyinfo= &share->keyinfo[0] ;
+ key < share->base.keys;
+ key++,keyinfo++)
+ {
+ keyseg=keyinfo->seg;
+ if (keyinfo->flag & HA_NOSAME) text="unique ";
+ else if (keyinfo->flag & HA_FULLTEXT) text="fulltext ";
+ else text="multip.";
+
+ pos=buff;
+ if (keyseg->flag & HA_REVERSE_SORT)
+ *pos++ = '-';
+ pos=strmov(pos,type_names[keyseg->type]);
+ *pos++ = ' ';
+ *pos=0;
+ if (keyinfo->flag & HA_PACK_KEY)
+ pos=strmov(pos,prefix_packed_txt);
+ if (keyinfo->flag & HA_BINARY_PACK_KEY)
+ pos=strmov(pos,bin_packed_txt);
+ if (keyseg->flag & HA_SPACE_PACK)
+ pos=strmov(pos,diff_txt);
+ if (keyseg->flag & HA_BLOB_PART)
+ pos=strmov(pos,blob_txt);
+ if (keyseg->flag & HA_NULL_PART)
+ pos=strmov(pos,null_txt);
+ *pos=0;
+
+ printf("%-4d%-6ld%-3d %-8s%-21s",
+ key+1,(long) keyseg->start+1,keyseg->length,text,buff);
+ if (share->state.key_root[key] != HA_OFFSET_ERROR)
+ llstr(share->state.key_root[key],buff);
+ else
+ buff[0]=0;
+ if (param->testflag & T_VERBOSE)
+ printf("%11lu %12s %10d",
+ share->state.rec_per_key_part[keyseg_nr++],
+ buff,keyinfo->block_length);
+ VOID(putchar('\n'));
+ while ((++keyseg)->type != HA_KEYTYPE_END)
+ {
+ pos=buff;
+ if (keyseg->flag & HA_REVERSE_SORT)
+ *pos++ = '-';
+ pos=strmov(pos,type_names[keyseg->type]);
+ *pos++= ' ';
+ if (keyseg->flag & HA_SPACE_PACK)
+ pos=strmov(pos,diff_txt);
+ if (keyseg->flag & HA_BLOB_PART)
+ pos=strmov(pos,blob_txt);
+ if (keyseg->flag & HA_NULL_PART)
+ pos=strmov(pos,null_txt);
+ *pos=0;
+ printf(" %-6ld%-3d %-21s",
+ (long) keyseg->start+1,keyseg->length,buff);
+ if (param->testflag & T_VERBOSE)
+ printf("%11lu", share->state.rec_per_key_part[keyseg_nr++]);
+ VOID(putchar('\n'));
+ }
+ keyseg++;
+ }
+ if (share->state.header.uniques)
+ {
+ MI_UNIQUEDEF *uniqueinfo;
+ puts("\nUnique Key Start Len Nullpos Nullbit Type");
+ for (key=0,uniqueinfo= &share->uniqueinfo[0] ;
+ key < share->state.header.uniques; key++, uniqueinfo++)
+ {
+ my_bool new_row=0;
+ char null_bit[8],null_pos[8];
+ printf("%-8d%-5d",key+1,uniqueinfo->key+1);
+ for (keyseg=uniqueinfo->seg ; keyseg->type != HA_KEYTYPE_END ; keyseg++)
+ {
+ if (new_row)
+ fputs(" ",stdout);
+ null_bit[0]=null_pos[0]=0;
+ if (keyseg->null_bit)
+ {
+ sprintf(null_bit,"%d",keyseg->null_bit);
+ sprintf(null_pos,"%ld",(long) keyseg->null_pos+1);
+ }
+ printf("%-7ld%-5d%-9s%-10s%-30s\n",
+ (long) keyseg->start+1,keyseg->length,
+ null_pos,null_bit,
+ type_names[keyseg->type]);
+ new_row=1;
+ }
+ }
+ }
+ if (param->verbose > 1)
+ {
+ char null_bit[8],null_pos[8];
+ printf("\nField Start Length Nullpos Nullbit Type");
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ printf(" Huff tree Bits");
+ VOID(putchar('\n'));
+ start=1;
+ for (field=0 ; field < share->base.fields ; field++)
+ {
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ type=share->rec[field].base_type;
+ else
+ type=(enum en_fieldtype) share->rec[field].type;
+ end=strmov(buff,field_pack[type]);
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ {
+ if (share->rec[field].pack_type & PACK_TYPE_SELECTED)
+ end=strmov(end,", not_always");
+ if (share->rec[field].pack_type & PACK_TYPE_SPACE_FIELDS)
+ end=strmov(end,", no empty");
+ if (share->rec[field].pack_type & PACK_TYPE_ZERO_FILL)
+ {
+ sprintf(end,", zerofill(%d)",share->rec[field].space_length_bits);
+ end=strend(end);
+ }
+ }
+ if (buff[0] == ',')
+ strmov(buff,buff+2);
+ int10_to_str((long) share->rec[field].length,length,10);
+ null_bit[0]=null_pos[0]=0;
+ if (share->rec[field].null_bit)
+ {
+ sprintf(null_bit,"%d",share->rec[field].null_bit);
+ sprintf(null_pos,"%d",share->rec[field].null_pos+1);
+ }
+ printf("%-6d%-6d%-7s%-8s%-8s%-35s",field+1,start,length,
+ null_pos, null_bit, buff);
+ if (share->options & HA_OPTION_COMPRESS_RECORD)
+ {
+ if (share->rec[field].huff_tree)
+ printf("%3d %2d",
+ (uint) (share->rec[field].huff_tree-share->decode_trees)+1,
+ share->rec[field].huff_tree->quick_table_bits);
+ }
+ VOID(putchar('\n'));
+ start+=share->rec[field].length;
+ }
+ }
+ DBUG_VOID_RETURN;
+} /* describe */
+
+
+ /* Sort records according to one key */
+
+static int mi_sort_records(MI_CHECK *param,
+ register MI_INFO *info, my_string name,
+ uint sort_key,
+ my_bool write_info,
+ my_bool update_index)
+{
+ int got_error;
+ uint key;
+ MI_KEYDEF *keyinfo;
+ File new_file;
+ uchar *temp_buff;
+ ha_rows old_record_count;
+ MYISAM_SHARE *share=info->s;
+ char llbuff[22],llbuff2[22];
+ SORT_INFO sort_info;
+ MI_SORT_PARAM sort_param;
+ DBUG_ENTER("sort_records");
+
+ bzero((char*)&sort_info,sizeof(sort_info));
+ bzero((char*)&sort_param,sizeof(sort_param));
+ sort_param.sort_info=&sort_info;
+ sort_info.param=param;
+ keyinfo= &share->keyinfo[sort_key];
+ got_error=1;
+ temp_buff=0;
+ new_file= -1;
+
+ if (!(((ulonglong) 1 << sort_key) & share->state.key_map))
+ {
+ mi_check_print_warning(param,
+ "Can't sort table '%s' on key %d; No such key",
+ name,sort_key+1);
+ param->error_printed=0;
+ DBUG_RETURN(0); /* Nothing to do */
+ }
+ if (keyinfo->flag & HA_FULLTEXT)
+ {
+ mi_check_print_warning(param,"Can't sort table '%s' on FULLTEXT key %d",
+ name,sort_key+1);
+ param->error_printed=0;
+ DBUG_RETURN(0); /* Nothing to do */
+ }
+ if (share->data_file_type == COMPRESSED_RECORD)
+ {
+ mi_check_print_warning(param,"Can't sort read-only table '%s'", name);
+ param->error_printed=0;
+ DBUG_RETURN(0); /* Nothing to do */
+ }
+ if (!(param->testflag & T_SILENT))
+ {
+ printf("- Sorting records for MyISAM-table '%s'\n",name);
+ if (write_info)
+ printf("Data records: %9s Deleted: %9s\n",
+ llstr(info->state->records,llbuff),
+ llstr(info->state->del,llbuff2));
+ }
+ if (share->state.key_root[sort_key] == HA_OFFSET_ERROR)
+ DBUG_RETURN(0); /* Nothing to do */
+
+ init_key_cache(dflt_key_cache, opt_key_cache_block_size, param->use_buffers,
+ 0, 0);
+ if (init_io_cache(&info->rec_cache,-1,(uint) param->write_buffer_length,
+ WRITE_CACHE,share->pack.header_length,1,
+ MYF(MY_WME | MY_WAIT_IF_FULL)))
+ goto err;
+ info->opt_flag|=WRITE_CACHE_USED;
+
+ if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
+ {
+ mi_check_print_error(param,"Not enough memory for key block");
+ goto err;
+ }
+ if (!(sort_param.record=(byte*) my_malloc((uint) share->base.pack_reclength,
+ MYF(0))))
+ {
+ mi_check_print_error(param,"Not enough memory for record");
+ goto err;
+ }
+ fn_format(param->temp_filename,name,"", MI_NAME_DEXT,2+4+32);
+ new_file=my_raid_create(fn_format(param->temp_filename,
+ param->temp_filename,"",
+ DATA_TMP_EXT,2+4),
+ 0,param->tmpfile_createflag,
+ share->base.raid_type,
+ share->base.raid_chunks,
+ share->base.raid_chunksize,
+ MYF(0));
+ if (new_file < 0)
+ {
+ mi_check_print_error(param,"Can't create new tempfile: '%s'",
+ param->temp_filename);
+ goto err;
+ }
+ if (share->pack.header_length)
+ if (filecopy(param,new_file,info->dfile,0L,share->pack.header_length,
+ "datafile-header"))
+ goto err;
+ info->rec_cache.file=new_file; /* Use this file for cacheing*/
+
+ lock_memory(param);
+ for (key=0 ; key < share->base.keys ; key++)
+ share->keyinfo[key].flag|= HA_SORT_ALLOWS_SAME;
+
+ if (my_pread(share->kfile,(byte*) temp_buff,
+ (uint) keyinfo->block_length,
+ share->state.key_root[sort_key],
+ MYF(MY_NABP+MY_WME)))
+ {
+ mi_check_print_error(param,"Can't read indexpage from filepos: %s",
+ (ulong) share->state.key_root[sort_key]);
+ goto err;
+ }
+
+ /* Setup param for sort_write_record */
+ sort_info.info=info;
+ sort_info.new_data_file_type=share->data_file_type;
+ sort_param.fix_datafile=1;
+ sort_param.master=1;
+ sort_param.filepos=share->pack.header_length;
+ old_record_count=info->state->records;
+ info->state->records=0;
+ if (sort_info.new_data_file_type != COMPRESSED_RECORD)
+ share->state.checksum=0;
+
+ if (sort_record_index(&sort_param,info,keyinfo,share->state.key_root[sort_key],
+ temp_buff, sort_key,new_file,update_index) ||
+ write_data_suffix(&sort_info,1) ||
+ flush_io_cache(&info->rec_cache))
+ goto err;
+
+ if (info->state->records != old_record_count)
+ {
+ mi_check_print_error(param,"found %s of %s records",
+ llstr(info->state->records,llbuff),
+ llstr(old_record_count,llbuff2));
+ goto err;
+ }
+
+ VOID(my_close(info->dfile,MYF(MY_WME)));
+ param->out_flag|=O_NEW_DATA; /* Data in new file */
+ info->dfile=new_file; /* Use new datafile */
+ info->state->del=0;
+ info->state->empty=0;
+ share->state.dellink= HA_OFFSET_ERROR;
+ info->state->data_file_length=sort_param.filepos;
+ share->state.split=info->state->records; /* Only hole records */
+ share->state.version=(ulong) time((time_t*) 0);
+
+ info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
+
+ if (param->testflag & T_WRITE_LOOP)
+ {
+ VOID(fputs(" \r",stdout)); VOID(fflush(stdout));
+ }
+ got_error=0;
+
+err:
+ if (got_error && new_file >= 0)
+ {
+ VOID(end_io_cache(&info->rec_cache));
+ (void) my_close(new_file,MYF(MY_WME));
+ (void) my_raid_delete(param->temp_filename, share->base.raid_chunks,
+ MYF(MY_WME));
+ }
+ if (temp_buff)
+ {
+ my_afree((gptr) temp_buff);
+ }
+ my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR));
+ info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
+ VOID(end_io_cache(&info->rec_cache));
+ my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR));
+ sort_info.buff=0;
+ share->state.sortkey=sort_key;
+ DBUG_RETURN(flush_blocks(param, share->key_cache, share->kfile) |
+ got_error);
+} /* sort_records */
+
+
+ /* Sort records recursive using one index */
+
+static int sort_record_index(MI_SORT_PARAM *sort_param,MI_INFO *info,
+ MI_KEYDEF *keyinfo,
+ my_off_t page, uchar *buff, uint sort_key,
+ File new_file,my_bool update_index)
+{
+ uint nod_flag,used_length,key_length;
+ uchar *temp_buff,*keypos,*endpos;
+ my_off_t next_page,rec_pos;
+ uchar lastkey[MI_MAX_KEY_BUFF];
+ char llbuff[22];
+ SORT_INFO *sort_info= sort_param->sort_info;
+ MI_CHECK *param=sort_info->param;
+ DBUG_ENTER("sort_record_index");
+
+ nod_flag=mi_test_if_nod(buff);
+ temp_buff=0;
+
+ if (nod_flag)
+ {
+ if (!(temp_buff=(uchar*) my_alloca((uint) keyinfo->block_length)))
+ {
+ mi_check_print_error(param,"Not Enough memory");
+ DBUG_RETURN(-1);
+ }
+ }
+ used_length=mi_getint(buff);
+ keypos=buff+2+nod_flag;
+ endpos=buff+used_length;
+ for ( ;; )
+ {
+ _sanity(__FILE__,__LINE__);
+ if (nod_flag)
+ {
+ next_page=_mi_kpos(nod_flag,keypos);
+ if (my_pread(info->s->kfile,(byte*) temp_buff,
+ (uint) keyinfo->block_length, next_page,
+ MYF(MY_NABP+MY_WME)))
+ {
+ mi_check_print_error(param,"Can't read keys from filepos: %s",
+ llstr(next_page,llbuff));
+ goto err;
+ }
+ if (sort_record_index(sort_param, info,keyinfo,next_page,temp_buff,sort_key,
+ new_file, update_index))
+ goto err;
+ }
+ _sanity(__FILE__,__LINE__);
+ if (keypos >= endpos ||
+ (key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,lastkey))
+ == 0)
+ break;
+ rec_pos= _mi_dpos(info,0,lastkey+key_length);
+
+ if ((*info->s->read_rnd)(info,sort_param->record,rec_pos,0))
+ {
+ mi_check_print_error(param,"%d when reading datafile",my_errno);
+ goto err;
+ }
+ if (rec_pos != sort_param->filepos && update_index)
+ {
+ _mi_dpointer(info,keypos-nod_flag-info->s->rec_reflength,
+ sort_param->filepos);
+ if (movepoint(info,sort_param->record,rec_pos,sort_param->filepos,
+ sort_key))
+ {
+ mi_check_print_error(param,"%d when updating key-pointers",my_errno);
+ goto err;
+ }
+ }
+ if (sort_write_record(sort_param))
+ goto err;
+ }
+ /* Clear end of block to get better compression if the table is backuped */
+ bzero((byte*) buff+used_length,keyinfo->block_length-used_length);
+ if (my_pwrite(info->s->kfile,(byte*) buff,(uint) keyinfo->block_length,
+ page,param->myf_rw))
+ {
+ mi_check_print_error(param,"%d when updating keyblock",my_errno);
+ goto err;
+ }
+ if (temp_buff)
+ my_afree((gptr) temp_buff);
+ DBUG_RETURN(0);
+err:
+ if (temp_buff)
+ my_afree((gptr) temp_buff);
+ DBUG_RETURN(1);
+} /* sort_record_index */
+
+
+
+/*
+ Check if myisamchk was killed by a signal
+ This is overloaded by other programs that want to be able to abort
+ sorting
+*/
+
+static int not_killed= 0;
+
+volatile int *killed_ptr(MI_CHECK *param __attribute__((unused)))
+{
+ return &not_killed; /* always NULL */
+}
+
+ /* print warnings and errors */
+ /* VARARGS */
+
+void mi_check_print_info(MI_CHECK *param __attribute__((unused)),
+ const char *fmt,...)
+{
+ va_list args;
+
+ va_start(args,fmt);
+ VOID(vfprintf(stdout, fmt, args));
+ VOID(fputc('\n',stdout));
+ va_end(args);
+}
+
+/* VARARGS */
+
+void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
+{
+ va_list args;
+ DBUG_ENTER("mi_check_print_warning");
+
+ fflush(stdout);
+ if (!param->warning_printed && !param->error_printed)
+ {
+ if (param->testflag & T_SILENT)
+ fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,
+ param->isam_file_name);
+ param->out_flag|= O_DATA_LOST;
+ }
+ param->warning_printed=1;
+ va_start(args,fmt);
+ fprintf(stderr,"%s: warning: ",my_progname_short);
+ VOID(vfprintf(stderr, fmt, args));
+ VOID(fputc('\n',stderr));
+ fflush(stderr);
+ va_end(args);
+ DBUG_VOID_RETURN;
+}
+
+/* VARARGS */
+
+void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
+{
+ va_list args;
+ DBUG_ENTER("mi_check_print_error");
+ DBUG_PRINT("enter",("format: %s",fmt));
+
+ fflush(stdout);
+ if (!param->warning_printed && !param->error_printed)
+ {
+ if (param->testflag & T_SILENT)
+ fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,param->isam_file_name);
+ param->out_flag|= O_DATA_LOST;
+ }
+ param->error_printed|=1;
+ va_start(args,fmt);
+ fprintf(stderr,"%s: error: ",my_progname_short);
+ VOID(vfprintf(stderr, fmt, args));
+ VOID(fputc('\n',stderr));
+ fflush(stderr);
+ va_end(args);
+ DBUG_VOID_RETURN;
+}
diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h
new file mode 100644
index 00000000000..b66d7e71a05
--- /dev/null
+++ b/storage/myisam/myisamdef.h
@@ -0,0 +1,736 @@
+/* Copyright (C) 2000,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 */
+
+/* This file is included by all internal myisam files */
+
+#include "myisam.h" /* Structs & some defines */
+#include "myisampack.h" /* packing of keys */
+#include <my_tree.h>
+#ifdef THREAD
+#include <my_pthread.h>
+#include <thr_lock.h>
+#else
+#include <my_no_pthread.h>
+#endif
+
+#if defined(my_write) && !defined(MAP_TO_USE_RAID)
+#undef my_write /* undef map from my_nosys; We need test-if-disk full */
+#endif
+
+typedef struct st_mi_status_info
+{
+ ha_rows records; /* Rows in table */
+ ha_rows del; /* Removed rows */
+ my_off_t empty; /* lost space in datafile */
+ my_off_t key_empty; /* lost space in indexfile */
+ my_off_t key_file_length;
+ my_off_t data_file_length;
+} MI_STATUS_INFO;
+
+typedef struct st_mi_state_info
+{
+ struct { /* Fileheader */
+ uchar file_version[4];
+ uchar options[2];
+ uchar header_length[2];
+ uchar state_info_length[2];
+ uchar base_info_length[2];
+ uchar base_pos[2];
+ uchar key_parts[2]; /* Key parts */
+ uchar unique_key_parts[2]; /* Key parts + unique parts */
+ uchar keys; /* number of keys in file */
+ uchar uniques; /* number of UNIQUE definitions */
+ uchar language; /* Language for indexes */
+ uchar max_block_size; /* max keyblock size */
+ uchar fulltext_keys;
+ uchar not_used; /* To align to 8 */
+ } header;
+
+ MI_STATUS_INFO state;
+ ha_rows split; /* number of split blocks */
+ my_off_t dellink; /* Link to next removed block */
+ ulonglong auto_increment;
+ ulong process; /* process that updated table last */
+ ulong unique; /* Unique number for this process */
+ ulong update_count; /* Updated for each write lock */
+ ulong status;
+ ulong *rec_per_key_part;
+ my_off_t *key_root; /* Start of key trees */
+ my_off_t *key_del; /* delete links for trees */
+ my_off_t rec_per_key_rows; /* Rows when calculating rec_per_key */
+
+ ulong sec_index_changed; /* Updated when new sec_index */
+ ulong sec_index_used; /* which extra index are in use */
+ ulonglong key_map; /* Which keys are in use */
+ ha_checksum checksum;
+ ulong version; /* timestamp of create */
+ time_t create_time; /* Time when created database */
+ time_t recover_time; /* Time for last recover */
+ time_t check_time; /* Time for last check */
+ uint sortkey; /* sorted by this key (not used) */
+ uint open_count;
+ uint8 changed; /* Changed since myisamchk */
+
+ /* the following isn't saved on disk */
+ uint state_diff_length; /* Should be 0 */
+ uint state_length; /* Length of state header in file */
+ ulong *key_info;
+} MI_STATE_INFO;
+
+#define MI_STATE_INFO_SIZE (24+14*8+7*4+2*2+8)
+#define MI_STATE_KEY_SIZE 8
+#define MI_STATE_KEYBLOCK_SIZE 8
+#define MI_STATE_KEYSEG_SIZE 4
+#define MI_STATE_EXTRA_SIZE ((MI_MAX_KEY+MI_MAX_KEY_BLOCK_SIZE)*MI_STATE_KEY_SIZE + MI_MAX_KEY*MI_MAX_KEY_SEG*MI_STATE_KEYSEG_SIZE)
+#define MI_KEYDEF_SIZE (2+ 5*2)
+#define MI_UNIQUEDEF_SIZE (2+1+1)
+#define HA_KEYSEG_SIZE (6+ 2*2 + 4*2)
+#define MI_COLUMNDEF_SIZE (2*3+1)
+#define MI_BASE_INFO_SIZE (5*8 + 8*4 + 4 + 4*2 + 16)
+#define MI_INDEX_BLOCK_MARGIN 16 /* Safety margin for .MYI tables */
+
+typedef struct st_mi_base_info
+{
+ my_off_t keystart; /* Start of keys */
+ my_off_t max_data_file_length;
+ my_off_t max_key_file_length;
+ my_off_t margin_key_file_length;
+ ha_rows records,reloc; /* Create information */
+ ulong mean_row_length; /* Create information */
+ ulong reclength; /* length of unpacked record */
+ ulong pack_reclength; /* Length of full packed rec. */
+ ulong min_pack_length;
+ ulong max_pack_length; /* Max possibly length of packed rec.*/
+ ulong min_block_length;
+ ulong fields, /* fields in table */
+ pack_fields; /* packed fields in table */
+ uint rec_reflength; /* = 2-8 */
+ uint key_reflength; /* = 2-8 */
+ uint keys; /* same as in state.header */
+ uint auto_key; /* Which key-1 is a auto key */
+ uint blobs; /* Number of blobs */
+ uint pack_bits; /* Length of packed bits */
+ uint max_key_block_length; /* Max block length */
+ uint max_key_length; /* Max key length */
+ /* Extra allocation when using dynamic record format */
+ uint extra_alloc_bytes;
+ uint extra_alloc_procent;
+ /* Info about raid */
+ uint raid_type,raid_chunks;
+ ulong raid_chunksize;
+ /* The following are from the header */
+ uint key_parts,all_key_parts;
+} MI_BASE_INFO;
+
+
+ /* Structs used intern in database */
+
+typedef struct st_mi_blob /* Info of record */
+{
+ ulong offset; /* Offset to blob in record */
+ uint pack_length; /* Type of packed length */
+ ulong length; /* Calc:ed for each record */
+} MI_BLOB;
+
+
+typedef struct st_mi_isam_pack {
+ ulong header_length;
+ uint ref_length;
+} MI_PACK;
+
+
+typedef struct st_mi_isam_share { /* Shared between opens */
+ MI_STATE_INFO state;
+ MI_BASE_INFO base;
+ MI_KEYDEF ft2_keyinfo; /* Second-level ft-key definition */
+ MI_KEYDEF *keyinfo; /* Key definitions */
+ MI_UNIQUEDEF *uniqueinfo; /* unique definitions */
+ HA_KEYSEG *keyparts; /* key part info */
+ MI_COLUMNDEF *rec; /* Pointer to field information */
+ MI_PACK pack; /* Data about packed records */
+ MI_BLOB *blobs; /* Pointer to blobs */
+ char *unique_file_name; /* realpath() of index file */
+ char *data_file_name, /* Resolved path names from symlinks */
+ *index_file_name;
+ byte *file_map; /* mem-map of file if possible */
+ KEY_CACHE *key_cache; /* ref to the current key cache */
+ MI_DECODE_TREE *decode_trees;
+ uint16 *decode_tables;
+ int (*read_record)(struct st_myisam_info*, my_off_t, byte*);
+ int (*write_record)(struct st_myisam_info*, const byte*);
+ int (*update_record)(struct st_myisam_info*, my_off_t, const byte*);
+ int (*delete_record)(struct st_myisam_info*);
+ int (*read_rnd)(struct st_myisam_info*, byte*, my_off_t, my_bool);
+ int (*compare_record)(struct st_myisam_info*, const byte *);
+ ha_checksum (*calc_checksum)(struct st_myisam_info*, const byte *);
+ int (*compare_unique)(struct st_myisam_info*, MI_UNIQUEDEF *,
+ const byte *record, my_off_t pos);
+ invalidator_by_filename invalidator; /* query cache invalidator */
+ ulong this_process; /* processid */
+ ulong last_process; /* For table-change-check */
+ ulong last_version; /* Version on start */
+ ulong options; /* Options used */
+ ulong min_pack_length; /* Theese are used by packed data */
+ ulong max_pack_length;
+ ulong state_diff_length;
+ uint rec_reflength; /* rec_reflength in use now */
+ uint unique_name_length;
+ File kfile; /* Shared keyfile */
+ File data_file; /* Shared data file */
+ int mode; /* mode of file on open */
+ uint reopen; /* How many times reopened */
+ uint w_locks,r_locks,tot_locks; /* Number of read/write locks */
+ uint blocksize; /* blocksize of keyfile */
+ myf write_flag;
+ enum data_file_type data_file_type;
+ my_bool changed, /* If changed since lock */
+ global_changed, /* If changed since open */
+ not_flushed,
+ temporary,delay_key_write,
+ concurrent_insert;
+#ifdef THREAD
+ THR_LOCK lock;
+ pthread_mutex_t intern_lock; /* Locking for use with _locking */
+ rw_lock_t *key_root_lock;
+#endif
+} MYISAM_SHARE;
+
+
+typedef uint mi_bit_type;
+
+typedef struct st_mi_bit_buff { /* Used for packing of record */
+ mi_bit_type current_byte;
+ uint bits;
+ uchar *pos,*end,*blob_pos,*blob_end;
+ uint error;
+} MI_BIT_BUFF;
+
+struct st_myisam_info {
+ MYISAM_SHARE *s; /* Shared between open:s */
+ MI_STATUS_INFO *state,save_state;
+ MI_BLOB *blobs; /* Pointer to blobs */
+ MI_BIT_BUFF bit_buff;
+ /* accumulate indexfile changes between write's */
+ TREE *bulk_insert;
+ DYNAMIC_ARRAY *ft1_to_ft2; /* used only in ft1->ft2 conversion */
+ char *filename; /* parameter to open filename */
+ uchar *buff, /* Temp area for key */
+ *lastkey,*lastkey2; /* Last used search key */
+ uchar *first_mbr_key; /* Searhed spatial key */
+ byte *rec_buff; /* Tempbuff for recordpack */
+ uchar *int_keypos, /* Save position for next/previous */
+ *int_maxpos; /* -""- */
+ uint int_nod_flag; /* -""- */
+ uint32 int_keytree_version; /* -""- */
+ int (*read_record)(struct st_myisam_info*, my_off_t, byte*);
+ invalidator_by_filename invalidator; /* query cache invalidator */
+ ulong this_unique; /* uniq filenumber or thread */
+ ulong last_unique; /* last unique number */
+ ulong this_loop; /* counter for this open */
+ ulong last_loop; /* last used counter */
+ my_off_t lastpos, /* Last record position */
+ nextpos; /* Position to next record */
+ my_off_t save_lastpos;
+ my_off_t pos; /* Intern variable */
+ my_off_t last_keypage; /* Last key page read */
+ my_off_t last_search_keypage; /* Last keypage when searching */
+ my_off_t dupp_key_pos;
+ ha_checksum checksum;
+ /* QQ: the folloing two xxx_length fields should be removed,
+ as they are not compatible with parallel repair */
+ ulong packed_length,blob_length; /* Length of found, packed record */
+ int dfile; /* The datafile */
+ uint opt_flag; /* Optim. for space/speed */
+ uint update; /* If file changed since open */
+ int lastinx; /* Last used index */
+ uint lastkey_length; /* Length of key in lastkey */
+ uint last_rkey_length; /* Last length in mi_rkey() */
+ enum ha_rkey_function last_key_func; /* CONTAIN, OVERLAP, etc */
+ uint save_lastkey_length;
+ int errkey; /* Got last error on this key */
+ int lock_type; /* How database was locked */
+ int tmp_lock_type; /* When locked by readinfo */
+ uint data_changed; /* Somebody has changed data */
+ uint save_update; /* When using KEY_READ */
+ int save_lastinx;
+ LIST open_list;
+ IO_CACHE rec_cache; /* When cacheing records */
+ uint preload_buff_size; /* When preloading indexes */
+ myf lock_wait; /* is 0 or MY_DONT_WAIT */
+ my_bool was_locked; /* Was locked in panic */
+ my_bool quick_mode;
+ my_bool page_changed; /* If info->buff can't be used for rnext */
+ my_bool buff_used; /* If info->buff has to be reread for rnext */
+ my_bool once_flags; /* For MYISAMMRG */
+#ifdef THREAD
+ THR_LOCK_DATA lock;
+#endif
+ uchar *rtree_recursion_state; /* For RTREE */
+ int rtree_recursion_depth;
+};
+
+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;
+
+typedef struct st_mi_sort_param
+{
+ pthread_t thr;
+ IO_CACHE read_cache, tempfile, tempfile_for_exceptions;
+ DYNAMIC_ARRAY buffpek;
+ ulonglong unique[MI_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;
+ MI_KEYDEF *keyinfo;
+ HA_KEYSEG *seg;
+ SORT_INFO *sort_info;
+ uchar **sort_keys;
+ byte *rec_buff;
+ void *wordlist, *wordptr;
+ char *record;
+ MY_TMPDIR *tmpdir;
+ 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)(MI_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;
+ /* Some defines used by isam-funktions */
+
+#define USE_WHOLE_KEY MI_MAX_KEY_BUFF*2 /* Use whole key in _mi_search() */
+#define F_EXTRA_LCK -1
+
+ /* bits in opt_flag */
+#define MEMMAP_USED 32
+#define REMEMBER_OLD_POS 64
+
+#define WRITEINFO_UPDATE_KEYFILE 1
+#define WRITEINFO_NO_UNLOCK 2
+
+ /* once_flags */
+#define USE_PACKED_KEYS 1
+#define RRND_PRESERVE_LASTINX 2
+
+ /* bits in state.changed */
+
+#define STATE_CHANGED 1
+#define STATE_CRASHED 2
+#define STATE_CRASHED_ON_REPAIR 4
+#define STATE_NOT_ANALYZED 8
+#define STATE_NOT_OPTIMIZED_KEYS 16
+#define STATE_NOT_SORTED_PAGES 32
+
+ /* options to mi_read_cache */
+
+#define READING_NEXT 1
+#define READING_HEADER 2
+
+#define mi_getint(x) ((uint) mi_uint2korr(x) & 32767)
+#define mi_putint(x,y,nod) { uint16 boh=(nod ? (uint16) 32768 : 0) + (uint16) (y);\
+ mi_int2store(x,boh); }
+#define mi_test_if_nod(x) (x[0] & 128 ? info->s->base.key_reflength : 0)
+#define mi_mark_crashed(x) (x)->s->state.changed|=STATE_CRASHED
+#define mi_mark_crashed_on_repair(x) { (x)->s->state.changed|=STATE_CRASHED|STATE_CRASHED_ON_REPAIR ; (x)->update|= HA_STATE_CHANGED; }
+#define mi_is_crashed(x) ((x)->s->state.changed & STATE_CRASHED)
+#define mi_is_crashed_on_repair(x) ((x)->s->state.changed & STATE_CRASHED_ON_REPAIR)
+#define mi_print_error(SHARE, ERRNO) \
+ mi_report_error((ERRNO), (SHARE)->index_file_name)
+
+/* Functions to store length of space packed keys, VARCHAR or BLOB keys */
+
+#define store_key_length_inc(key,length) \
+{ if ((length) < 255) \
+ { *(key)++=(length); } \
+ else \
+ { *(key)=255; mi_int2store((key)+1,(length)); (key)+=3; } \
+}
+
+#define store_key_length(key,length) \
+{ if ((length) < 255) \
+ { *(key)=(length); } \
+ else \
+ { *(key)=255; mi_int2store((key)+1,(length)); } \
+}
+
+#define get_key_full_length(length,key) \
+{ if ((uchar) *(key) != 255) \
+ length= ((uint) (uchar) *((key)++))+1; \
+ else \
+ { length=mi_uint2korr((key)+1)+3; (key)+=3; } \
+}
+
+#define get_key_full_length_rdonly(length,key) \
+{ if ((uchar) *(key) != 255) \
+ length= ((uint) (uchar) *((key)))+1; \
+ else \
+ { length=mi_uint2korr((key)+1)+3; } \
+}
+
+#define get_pack_length(length) ((length) >= 255 ? 3 : 1)
+
+#define MI_MIN_BLOCK_LENGTH 20 /* Because of delete-link */
+#define MI_EXTEND_BLOCK_LENGTH 20 /* Don't use to small record-blocks */
+#define MI_SPLIT_LENGTH ((MI_EXTEND_BLOCK_LENGTH+4)*2)
+#define MI_MAX_DYN_BLOCK_HEADER 20 /* Max prefix of record-block */
+#define MI_BLOCK_INFO_HEADER_LENGTH 20
+#define MI_DYN_DELETE_BLOCK_HEADER 20 /* length of delete-block-header */
+#define MI_DYN_MAX_BLOCK_LENGTH ((1L << 24)-4L)
+#define MI_DYN_MAX_ROW_LENGTH (MI_DYN_MAX_BLOCK_LENGTH - MI_SPLIT_LENGTH)
+#define MI_DYN_ALIGN_SIZE 4 /* Align blocks on this */
+#define MI_MAX_DYN_HEADER_BYTE 13 /* max header byte for dynamic rows */
+#define MI_MAX_BLOCK_LENGTH ((((ulong) 1 << 24)-1) & (~ (ulong) (MI_DYN_ALIGN_SIZE-1)))
+#define MI_REC_BUFF_OFFSET ALIGN_SIZE(MI_DYN_DELETE_BLOCK_HEADER+sizeof(uint32))
+
+#define MEMMAP_EXTRA_MARGIN 7 /* Write this as a suffix for file */
+
+#define PACK_TYPE_SELECTED 1 /* Bits in field->pack_type */
+#define PACK_TYPE_SPACE_FIELDS 2
+#define PACK_TYPE_ZERO_FILL 4
+#define MI_FOUND_WRONG_KEY 32738 /* Impossible value from ha_key_cmp */
+
+#define MI_MAX_KEY_BLOCK_SIZE (MI_MAX_KEY_BLOCK_LENGTH/MI_MIN_KEY_BLOCK_LENGTH)
+#define MI_BLOCK_SIZE(key_length,data_pointer,key_pointer) (((((key_length)+(data_pointer)+(key_pointer))*4+(key_pointer)+2)/myisam_block_size+1)*myisam_block_size)
+#define MI_MAX_KEYPTR_SIZE 5 /* For calculating block lengths */
+#define MI_MIN_KEYBLOCK_LENGTH 50 /* When to split delete blocks */
+
+#define MI_MIN_SIZE_BULK_INSERT_TREE 16384 /* this is per key */
+#define MI_MIN_ROWS_TO_USE_BULK_INSERT 100
+#define MI_MIN_ROWS_TO_DISABLE_INDEXES 100
+#define MI_MIN_ROWS_TO_USE_WRITE_CACHE 10
+
+/* The UNIQUE check is done with a hashed long key */
+
+#define MI_UNIQUE_HASH_TYPE HA_KEYTYPE_ULONG_INT
+#define mi_unique_store(A,B) mi_int4store((A),(B))
+
+#ifdef THREAD
+extern pthread_mutex_t THR_LOCK_myisam;
+#endif
+#if !defined(THREAD) || defined(DONT_USE_RW_LOCKS)
+#define rw_wrlock(A) {}
+#define rw_rdlock(A) {}
+#define rw_unlock(A) {}
+#endif
+
+ /* Some extern variables */
+
+extern LIST *myisam_open_list;
+extern uchar NEAR myisam_file_magic[],NEAR myisam_pack_file_magic[];
+extern uint NEAR myisam_read_vec[],NEAR myisam_readnext_vec[];
+extern uint myisam_quick_table_bits;
+extern File myisam_log_file;
+extern ulong myisam_pid;
+
+ /* This is used by _mi_calc_xxx_key_length och _mi_store_key */
+
+typedef struct st_mi_s_param
+{
+ uint ref_length,key_length,
+ n_ref_length,
+ n_length,
+ totlength,
+ part_of_prev_key,prev_length,pack_marker;
+ uchar *key, *prev_key,*next_key_pos;
+ bool store_not_null;
+} MI_KEY_PARAM;
+
+ /* Prototypes for intern functions */
+
+extern int _mi_read_dynamic_record(MI_INFO *info,my_off_t filepos,byte *buf);
+extern int _mi_write_dynamic_record(MI_INFO*, const byte*);
+extern int _mi_update_dynamic_record(MI_INFO*, my_off_t, const byte*);
+extern int _mi_delete_dynamic_record(MI_INFO *info);
+extern int _mi_cmp_dynamic_record(MI_INFO *info,const byte *record);
+extern int _mi_read_rnd_dynamic_record(MI_INFO *, byte *,my_off_t, my_bool);
+extern int _mi_write_blob_record(MI_INFO*, const byte*);
+extern int _mi_update_blob_record(MI_INFO*, my_off_t, const byte*);
+extern int _mi_read_static_record(MI_INFO *info, my_off_t filepos,byte *buf);
+extern int _mi_write_static_record(MI_INFO*, const byte*);
+extern int _mi_update_static_record(MI_INFO*, my_off_t, const byte*);
+extern int _mi_delete_static_record(MI_INFO *info);
+extern int _mi_cmp_static_record(MI_INFO *info,const byte *record);
+extern int _mi_read_rnd_static_record(MI_INFO*, byte *,my_off_t, my_bool);
+extern int _mi_ck_write(MI_INFO *info,uint keynr,uchar *key,uint length);
+extern int _mi_ck_real_write_btree(MI_INFO *info, MI_KEYDEF *keyinfo,
+ uchar *key, uint key_length,
+ my_off_t *root, uint comp_flag);
+extern int _mi_enlarge_root(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key, my_off_t *root);
+extern int _mi_insert(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
+ uchar *anc_buff,uchar *key_pos,uchar *key_buff,
+ uchar *father_buff, uchar *father_keypos,
+ my_off_t father_page, my_bool insert_last);
+extern int _mi_split_page(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
+ uchar *buff,uchar *key_buff, my_bool insert_last);
+extern uchar *_mi_find_half_pos(uint nod_flag,MI_KEYDEF *keyinfo,uchar *page,
+ uchar *key,uint *return_key_length,
+ uchar **after_key);
+extern int _mi_calc_static_key_length(MI_KEYDEF *keyinfo,uint nod_flag,
+ uchar *key_pos, uchar *org_key,
+ uchar *key_buff,
+ uchar *key, MI_KEY_PARAM *s_temp);
+extern int _mi_calc_var_key_length(MI_KEYDEF *keyinfo,uint nod_flag,
+ uchar *key_pos, uchar *org_key,
+ uchar *key_buff,
+ uchar *key, MI_KEY_PARAM *s_temp);
+extern int _mi_calc_var_pack_key_length(MI_KEYDEF *keyinfo,uint nod_flag,
+ uchar *key_pos, uchar *org_key,
+ uchar *prev_key,
+ uchar *key, MI_KEY_PARAM *s_temp);
+extern int _mi_calc_bin_pack_key_length(MI_KEYDEF *keyinfo,uint nod_flag,
+ uchar *key_pos,uchar *org_key,
+ uchar *prev_key,
+ uchar *key, MI_KEY_PARAM *s_temp);
+void _mi_store_static_key(MI_KEYDEF *keyinfo, uchar *key_pos,
+ MI_KEY_PARAM *s_temp);
+void _mi_store_var_pack_key(MI_KEYDEF *keyinfo, uchar *key_pos,
+ MI_KEY_PARAM *s_temp);
+#ifdef NOT_USED
+void _mi_store_pack_key(MI_KEYDEF *keyinfo, uchar *key_pos,
+ MI_KEY_PARAM *s_temp);
+#endif
+void _mi_store_bin_pack_key(MI_KEYDEF *keyinfo, uchar *key_pos,
+ MI_KEY_PARAM *s_temp);
+
+extern int _mi_ck_delete(MI_INFO *info,uint keynr,uchar *key,uint key_length);
+extern int _mi_readinfo(MI_INFO *info,int lock_flag,int check_keybuffer);
+extern int _mi_writeinfo(MI_INFO *info,uint options);
+extern int _mi_test_if_changed(MI_INFO *info);
+extern int _mi_mark_file_changed(MI_INFO *info);
+extern int _mi_decrement_open_count(MI_INFO *info);
+extern int _mi_check_index(MI_INFO *info,int inx);
+extern int _mi_search(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,uint key_len,
+ uint nextflag,my_off_t pos);
+extern int _mi_bin_search(struct st_myisam_info *info,MI_KEYDEF *keyinfo,
+ uchar *page,uchar *key,uint key_len,uint comp_flag,
+ uchar * *ret_pos,uchar *buff, my_bool *was_last_key);
+extern int _mi_seq_search(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *page,
+ uchar *key,uint key_len,uint comp_flag,
+ uchar **ret_pos,uchar *buff, my_bool *was_last_key);
+extern int _mi_prefix_search(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *page,
+ uchar *key,uint key_len,uint comp_flag,
+ uchar **ret_pos,uchar *buff, my_bool *was_last_key);
+extern my_off_t _mi_kpos(uint nod_flag,uchar *after_key);
+extern void _mi_kpointer(MI_INFO *info,uchar *buff,my_off_t pos);
+extern my_off_t _mi_dpos(MI_INFO *info, uint nod_flag,uchar *after_key);
+extern my_off_t _mi_rec_pos(MYISAM_SHARE *info, uchar *ptr);
+extern void _mi_dpointer(MI_INFO *info, uchar *buff,my_off_t pos);
+extern int ha_key_cmp(HA_KEYSEG *keyseg, uchar *a,uchar *b,
+ uint key_length,uint nextflag,uint *diff_length);
+extern uint _mi_get_static_key(MI_KEYDEF *keyinfo,uint nod_flag,uchar * *page,
+ uchar *key);
+extern uint _mi_get_pack_key(MI_KEYDEF *keyinfo,uint nod_flag,uchar * *page,
+ uchar *key);
+extern uint _mi_get_binary_pack_key(MI_KEYDEF *keyinfo, uint nod_flag,
+ uchar **page_pos, uchar *key);
+extern uchar *_mi_get_last_key(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *keypos,
+ uchar *lastkey,uchar *endpos,
+ uint *return_key_length);
+extern uchar *_mi_get_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page,
+ uchar *key, uchar *keypos, uint *return_key_length);
+extern uint _mi_keylength(MI_KEYDEF *keyinfo,uchar *key);
+extern uint _mi_keylength_part(MI_KEYDEF *keyinfo, register uchar *key,
+ HA_KEYSEG *end);
+extern uchar *_mi_move_key(MI_KEYDEF *keyinfo,uchar *to,uchar *from);
+extern int _mi_search_next(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
+ uint key_length,uint nextflag,my_off_t pos);
+extern int _mi_search_first(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t pos);
+extern int _mi_search_last(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t pos);
+extern uchar *_mi_fetch_keypage(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t page,
+ int level,uchar *buff,int return_buffer);
+extern int _mi_write_keypage(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t page,
+ int level, uchar *buff);
+extern int _mi_dispose(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t pos,
+ int level);
+extern my_off_t _mi_new(MI_INFO *info,MI_KEYDEF *keyinfo,int level);
+extern uint _mi_make_key(MI_INFO *info,uint keynr,uchar *key,
+ const byte *record,my_off_t filepos);
+extern uint _mi_pack_key(MI_INFO *info,uint keynr,uchar *key,uchar *old,
+ uint key_length, HA_KEYSEG **last_used_keyseg);
+extern int _mi_read_key_record(MI_INFO *info,my_off_t filepos,byte *buf);
+extern int _mi_read_cache(IO_CACHE *info,byte *buff,my_off_t pos,
+ uint length,int re_read_if_possibly);
+extern void update_auto_increment(MI_INFO *info,const byte *record);
+
+extern byte *mi_alloc_rec_buff(MI_INFO *,ulong, byte**);
+#define mi_get_rec_buff_ptr(info,buf) \
+ ((((info)->s->options & HA_OPTION_PACK_RECORD) && (buf)) ? \
+ (buf) - MI_REC_BUFF_OFFSET : (buf))
+#define mi_get_rec_buff_len(info,buf) \
+ (*((uint32 *)(mi_get_rec_buff_ptr(info,buf))))
+
+extern ulong _mi_rec_unpack(MI_INFO *info,byte *to,byte *from,
+ ulong reclength);
+extern my_bool _mi_rec_check(MI_INFO *info,const char *record, byte *packpos,
+ ulong reclength);
+extern int _mi_write_part_record(MI_INFO *info,my_off_t filepos,ulong length,
+ my_off_t next_filepos,byte **record,
+ ulong *reclength,int *flag);
+extern void _mi_print_key(FILE *stream,HA_KEYSEG *keyseg,const uchar *key,
+ uint length);
+extern my_bool _mi_read_pack_info(MI_INFO *info,pbool fix_keys);
+extern int _mi_read_pack_record(MI_INFO *info,my_off_t filepos,byte *buf);
+extern int _mi_read_rnd_pack_record(MI_INFO*, byte *,my_off_t, my_bool);
+extern int _mi_pack_rec_unpack(MI_INFO *info,byte *to,byte *from,
+ ulong reclength);
+extern ulonglong mi_safe_mul(ulonglong a,ulonglong b);
+extern int _mi_ft_update(MI_INFO *info, uint keynr, byte *keybuf,
+ const byte *oldrec, const byte *newrec, my_off_t pos);
+
+struct st_sort_info;
+
+
+typedef struct st_mi_block_info { /* Parameter to _mi_get_block_info */
+ uchar header[MI_BLOCK_INFO_HEADER_LENGTH];
+ ulong rec_len;
+ ulong data_len;
+ ulong block_len;
+ ulong blob_len;
+ my_off_t filepos;
+ my_off_t next_filepos;
+ my_off_t prev_filepos;
+ uint second_read;
+ uint offset;
+} MI_BLOCK_INFO;
+
+ /* bits in return from _mi_get_block_info */
+
+#define BLOCK_FIRST 1
+#define BLOCK_LAST 2
+#define BLOCK_DELETED 4
+#define BLOCK_ERROR 8 /* Wrong data */
+#define BLOCK_SYNC_ERROR 16 /* Right data at wrong place */
+#define BLOCK_FATAL_ERROR 32 /* hardware-error */
+
+#define NEED_MEM ((uint) 10*4*(IO_SIZE+32)+32) /* Nead for recursion */
+#define MAXERR 20
+#define BUFFERS_WHEN_SORTING 16 /* Alloc for sort-key-tree */
+#define WRITE_COUNT MY_HOW_OFTEN_TO_WRITE
+#define INDEX_TMP_EXT ".TMM"
+#define DATA_TMP_EXT ".TMD"
+
+#define UPDATE_TIME 1
+#define UPDATE_STAT 2
+#define UPDATE_SORT 4
+#define UPDATE_AUTO_INC 8
+#define UPDATE_OPEN_COUNT 16
+
+#define USE_BUFFER_INIT (((1024L*512L-MALLOC_OVERHEAD)/IO_SIZE)*IO_SIZE)
+#define READ_BUFFER_INIT (1024L*256L-MALLOC_OVERHEAD)
+#define SORT_BUFFER_INIT (2048L*1024L-MALLOC_OVERHEAD)
+#define MIN_SORT_BUFFER (4096-MALLOC_OVERHEAD)
+
+enum myisam_log_commands {
+ MI_LOG_OPEN,MI_LOG_WRITE,MI_LOG_UPDATE,MI_LOG_DELETE,MI_LOG_CLOSE,MI_LOG_EXTRA,MI_LOG_LOCK,MI_LOG_DELETE_ALL
+};
+
+#define myisam_log(a,b,c,d) if (myisam_log_file >= 0) _myisam_log(a,b,c,d)
+#define myisam_log_command(a,b,c,d,e) if (myisam_log_file >= 0) _myisam_log_command(a,b,c,d,e)
+#define myisam_log_record(a,b,c,d,e) if (myisam_log_file >= 0) _myisam_log_record(a,b,c,d,e)
+
+#define fast_mi_writeinfo(INFO) if (!(INFO)->s->tot_locks) (void) _mi_writeinfo((INFO),0)
+#define fast_mi_readinfo(INFO) ((INFO)->lock_type == F_UNLCK) && _mi_readinfo((INFO),F_RDLCK,1)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint _mi_get_block_info(MI_BLOCK_INFO *,File, my_off_t);
+extern uint _mi_rec_pack(MI_INFO *info,byte *to,const byte *from);
+extern uint _mi_pack_get_block_info(MI_INFO *, MI_BLOCK_INFO *, File, my_off_t);
+extern void _my_store_blob_length(byte *pos,uint pack_length,uint length);
+extern void _myisam_log(enum myisam_log_commands command,MI_INFO *info,
+ const byte *buffert,uint length);
+extern void _myisam_log_command(enum myisam_log_commands command,
+ MI_INFO *info, const byte *buffert,
+ uint length, int result);
+extern void _myisam_log_record(enum myisam_log_commands command,MI_INFO *info,
+ const byte *record,my_off_t filepos,
+ int result);
+extern void mi_report_error(int errcode, const char *file_name);
+extern my_bool _mi_memmap_file(MI_INFO *info);
+extern void _mi_unmap_file(MI_INFO *info);
+extern uint save_pack_length(byte *block_buff,ulong length);
+
+uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite);
+char *mi_state_info_read(char *ptr, MI_STATE_INFO *state);
+uint mi_state_info_read_dsk(File file, MI_STATE_INFO *state, my_bool pRead);
+uint mi_base_info_write(File file, MI_BASE_INFO *base);
+char *my_n_base_info_read(char *ptr, MI_BASE_INFO *base);
+int mi_keyseg_write(File file, const HA_KEYSEG *keyseg);
+char *mi_keyseg_read(char *ptr, HA_KEYSEG *keyseg);
+uint mi_keydef_write(File file, MI_KEYDEF *keydef);
+char *mi_keydef_read(char *ptr, MI_KEYDEF *keydef);
+uint mi_uniquedef_write(File file, MI_UNIQUEDEF *keydef);
+char *mi_uniquedef_read(char *ptr, MI_UNIQUEDEF *keydef);
+uint mi_recinfo_write(File file, MI_COLUMNDEF *recinfo);
+char *mi_recinfo_read(char *ptr, MI_COLUMNDEF *recinfo);
+extern int mi_disable_indexes(MI_INFO *info);
+extern int mi_enable_indexes(MI_INFO *info);
+extern int mi_indexes_are_disabled(MI_INFO *info);
+ulong _my_calc_total_blob_length(MI_INFO *info, const byte *record);
+ha_checksum mi_checksum(MI_INFO *info, const byte *buf);
+ha_checksum mi_static_checksum(MI_INFO *info, const byte *buf);
+my_bool mi_check_unique(MI_INFO *info, MI_UNIQUEDEF *def, byte *record,
+ ha_checksum unique_hash, my_off_t pos);
+ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const byte *buf);
+int _mi_cmp_static_unique(MI_INFO *info, MI_UNIQUEDEF *def,
+ const byte *record, my_off_t pos);
+int _mi_cmp_dynamic_unique(MI_INFO *info, MI_UNIQUEDEF *def,
+ const byte *record, my_off_t pos);
+int mi_unique_comp(MI_UNIQUEDEF *def, const byte *a, const byte *b,
+ my_bool null_are_equal);
+void mi_get_status(void* param);
+void mi_update_status(void* param);
+void mi_copy_status(void* to,void *from);
+my_bool mi_check_status(void* param);
+void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);
+
+extern MI_INFO *test_if_reopen(char *filename);
+my_bool check_table_is_closed(const char *name, const char *where);
+int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, File file_to_dup);
+int mi_open_keyfile(MYISAM_SHARE *share);
+void mi_setup_functions(register MYISAM_SHARE *share);
+
+ /* Functions needed by mi_check */
+volatile int *killed_ptr(MI_CHECK *param);
+void mi_check_print_error _VARARGS((MI_CHECK *param, const char *fmt,...));
+void mi_check_print_warning _VARARGS((MI_CHECK *param, const char *fmt,...));
+void mi_check_print_info _VARARGS((MI_CHECK *param, const char *fmt,...));
+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);
+#ifdef THREAD
+pthread_handler_decl(thr_find_all_keys,arg);
+#endif
+int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file);
+
+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
+
diff --git a/storage/myisam/myisamlog.c b/storage/myisam/myisamlog.c
new file mode 100644
index 00000000000..dc98d813266
--- /dev/null
+++ b/storage/myisam/myisamlog.c
@@ -0,0 +1,845 @@
+/* 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 */
+
+/* write whats in isam.log */
+
+#ifndef USE_MY_FUNC
+#define USE_MY_FUNC
+#endif
+
+#include "myisamdef.h"
+#include <my_tree.h>
+#include <stdarg.h>
+#ifdef HAVE_GETRUSAGE
+#include <sys/resource.h>
+#endif
+
+#define FILENAME(A) (A ? A->show_name : "Unknown")
+
+struct file_info {
+ long process;
+ int filenr,id;
+ uint rnd;
+ my_string name,show_name,record;
+ MI_INFO *isam;
+ bool closed,used;
+ ulong accessed;
+};
+
+struct test_if_open_param {
+ my_string name;
+ int max_id;
+};
+
+struct st_access_param
+{
+ ulong min_accessed;
+ struct file_info *found;
+};
+
+#define NO_FILEPOS (ulong) ~0L
+
+extern int main(int argc,char * *argv);
+static void get_options(int *argc,char ***argv);
+static int examine_log(my_string file_name,char **table_names);
+static int read_string(IO_CACHE *file,gptr *to,uint length);
+static int file_info_compare(void *cmp_arg, void *a,void *b);
+static int test_if_open(struct file_info *key,element_count count,
+ struct test_if_open_param *param);
+static void fix_blob_pointers(MI_INFO *isam,byte *record);
+static int test_when_accessed(struct file_info *key,element_count count,
+ struct st_access_param *access_param);
+static void file_info_free(struct file_info *info);
+static int close_some_file(TREE *tree);
+static int reopen_closed_file(TREE *tree,struct file_info *file_info);
+static int find_record_with_key(struct file_info *file_info,byte *record);
+static void printf_log(const char *str,...);
+static bool cmp_filename(struct file_info *file_info,my_string name);
+
+static uint verbose=0,update=0,test_info=0,max_files=0,re_open_count=0,
+ recover=0,prefix_remove=0,opt_processes=0;
+static my_string log_filename=0,filepath=0,write_filename=0,record_pos_file=0;
+static ulong com_count[10][3],number_of_commands=(ulong) ~0L,
+ isamlog_process;
+static my_off_t isamlog_filepos,start_offset=0,record_pos= HA_OFFSET_ERROR;
+static const char *command_name[]=
+{"open","write","update","delete","close","extra","lock","re-open",
+ "delete-all", NullS};
+
+
+int main(int argc, char **argv)
+{
+ int error,i,first;
+ ulong total_count,total_error,total_recover;
+ MY_INIT(argv[0]);
+
+ log_filename=myisam_log_filename;
+ get_options(&argc,&argv);
+ /* Number of MyISAM files we can have open at one time */
+ max_files= (my_set_max_open_files(min(max_files,8))-6)/2;
+ if (update)
+ printf("Trying to %s MyISAM files according to log '%s'\n",
+ (recover ? "recover" : "update"),log_filename);
+ error= examine_log(log_filename,argv);
+ if (update && ! error)
+ puts("Tables updated successfully");
+ total_count=total_error=total_recover=0;
+ for (i=first=0 ; command_name[i] ; i++)
+ {
+ if (com_count[i][0])
+ {
+ if (!first++)
+ {
+ if (verbose || update)
+ puts("");
+ puts("Commands Used count Errors Recover errors");
+ }
+ printf("%-12s%9ld%10ld%17ld\n",command_name[i],com_count[i][0],
+ com_count[i][1],com_count[i][2]);
+ total_count+=com_count[i][0];
+ total_error+=com_count[i][1];
+ total_recover+=com_count[i][2];
+ }
+ }
+ if (total_count)
+ printf("%-12s%9ld%10ld%17ld\n","Total",total_count,total_error,
+ total_recover);
+ if (re_open_count)
+ printf("Had to do %d re-open because of too few possibly open files\n",
+ re_open_count);
+ VOID(mi_panic(HA_PANIC_CLOSE));
+ my_free_open_file_info();
+ my_end(test_info ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
+ exit(error);
+ return 0; /* No compiler warning */
+} /* main */
+
+
+static void get_options(register int *argc, register char ***argv)
+{
+ int help,version;
+ const char *pos,*usage;
+ char option;
+
+ help=0;
+ usage="Usage: %s [-?iruvDIV] [-c #] [-f #] [-F filepath/] [-o #] [-R file recordpos] [-w write_file] [log-filename [table ...]] \n";
+ pos="";
+
+ while (--*argc > 0 && *(pos = *(++*argv)) == '-' ) {
+ while (*++pos)
+ {
+ version=0;
+ switch((option=*pos)) {
+ case '#':
+ DBUG_PUSH (++pos);
+ pos=" "; /* Skip rest of arg */
+ break;
+ case 'c':
+ if (! *++pos)
+ {
+ if (!--*argc)
+ goto err;
+ else
+ pos= *(++*argv);
+ }
+ number_of_commands=(ulong) atol(pos);
+ pos=" ";
+ break;
+ case 'u':
+ update=1;
+ break;
+ case 'f':
+ if (! *++pos)
+ {
+ if (!--*argc)
+ goto err;
+ else
+ pos= *(++*argv);
+ }
+ max_files=(uint) atoi(pos);
+ pos=" ";
+ break;
+ case 'i':
+ test_info=1;
+ break;
+ case 'o':
+ if (! *++pos)
+ {
+ if (!--*argc)
+ goto err;
+ else
+ pos= *(++*argv);
+ }
+ start_offset=(my_off_t) strtoll(pos,NULL,10);
+ pos=" ";
+ break;
+ case 'p':
+ if (! *++pos)
+ {
+ if (!--*argc)
+ goto err;
+ else
+ pos= *(++*argv);
+ }
+ prefix_remove=atoi(pos);
+ break;
+ case 'r':
+ update=1;
+ recover++;
+ break;
+ case 'P':
+ opt_processes=1;
+ break;
+ case 'R':
+ if (! *++pos)
+ {
+ if (!--*argc)
+ goto err;
+ else
+ pos= *(++*argv);
+ }
+ record_pos_file=(char*) pos;
+ if (!--*argc)
+ goto err;
+ record_pos=(my_off_t) strtoll(*(++*argv),NULL,10);
+ pos=" ";
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'w':
+ if (! *++pos)
+ {
+ if (!--*argc)
+ goto err;
+ else
+ pos= *(++*argv);
+ }
+ write_filename=(char*) pos;
+ pos=" ";
+ break;
+ case 'F':
+ if (! *++pos)
+ {
+ if (!--*argc)
+ goto err;
+ else
+ pos= *(++*argv);
+ }
+ filepath= (char*) pos;
+ pos=" ";
+ break;
+ case 'V':
+ version=1;
+ /* Fall through */
+ case 'I':
+ case '?':
+#include <help_start.h>
+ printf("%s Ver 1.4 for %s at %s\n",my_progname,SYSTEM_TYPE,
+ MACHINE_TYPE);
+ puts("By Monty, for your professional use\n");
+ if (version)
+ break;
+ puts("Write info about whats in a MyISAM log file.");
+ printf("If no file name is given %s is used\n",log_filename);
+ puts("");
+ printf(usage,my_progname);
+ puts("");
+ puts("Options: -? or -I \"Info\" -V \"version\" -c \"do only # commands\"");
+ puts(" -f \"max open files\" -F \"filepath\" -i \"extra info\"");
+ puts(" -o \"offset\" -p # \"remove # components from path\"");
+ puts(" -r \"recover\" -R \"file recordposition\"");
+ puts(" -u \"update\" -v \"verbose\" -w \"write file\"");
+ puts(" -D \"myisam compiled with DBUG\" -P \"processes\"");
+ puts("\nOne can give a second and a third '-v' for more verbose.");
+ puts("Normaly one does a update (-u).");
+ puts("If a recover is done all writes and all possibly updates and deletes is done\nand errors are only counted.");
+ puts("If one gives table names as arguments only these tables will be updated\n");
+ help=1;
+#include <help_end.h>
+ break;
+ default:
+ printf("illegal option: \"-%c\"\n",*pos);
+ break;
+ }
+ }
+ }
+ if (! *argc)
+ {
+ if (help)
+ exit(0);
+ (*argv)++;
+ }
+ if (*argc >= 1)
+ {
+ log_filename=(char*) pos;
+ (*argc)--;
+ (*argv)++;
+ }
+ return;
+ err:
+ VOID(fprintf(stderr,"option \"%c\" used without or with wrong argument\n",
+ option));
+ exit(1);
+}
+
+
+static int examine_log(my_string file_name, char **table_names)
+{
+ uint command,result,files_open;
+ ulong access_time,length;
+ my_off_t filepos;
+ int lock_command,mi_result;
+ char isam_file_name[FN_REFLEN],llbuff[21],llbuff2[21];
+ uchar head[20];
+ gptr buff;
+ struct test_if_open_param open_param;
+ IO_CACHE cache;
+ File file;
+ FILE *write_file;
+ enum ha_extra_function extra_command;
+ TREE tree;
+ struct file_info file_info,*curr_file_info;
+ DBUG_ENTER("examine_log");
+
+ if ((file=my_open(file_name,O_RDONLY,MYF(MY_WME))) < 0)
+ DBUG_RETURN(1);
+ write_file=0;
+ if (write_filename)
+ {
+ if (!(write_file=my_fopen(write_filename,O_WRONLY,MYF(MY_WME))))
+ {
+ my_close(file,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+
+ init_io_cache(&cache,file,0,READ_CACHE,start_offset,0,MYF(0));
+ bzero((gptr) com_count,sizeof(com_count));
+ init_tree(&tree,0,0,sizeof(file_info),(qsort_cmp2) file_info_compare,1,
+ (tree_element_free) file_info_free, NULL);
+ VOID(init_key_cache(dflt_key_cache,KEY_CACHE_BLOCK_SIZE,KEY_CACHE_SIZE,
+ 0, 0));
+
+ files_open=0; access_time=0;
+ while (access_time++ != number_of_commands &&
+ !my_b_read(&cache,(byte*) head,9))
+ {
+ isamlog_filepos=my_b_tell(&cache)-9L;
+ file_info.filenr= mi_uint2korr(head+1);
+ isamlog_process=file_info.process=(long) mi_uint4korr(head+3);
+ if (!opt_processes)
+ file_info.process=0;
+ result= mi_uint2korr(head+7);
+ if ((curr_file_info=(struct file_info*) tree_search(&tree, &file_info,
+ tree.custom_arg)))
+ {
+ curr_file_info->accessed=access_time;
+ if (update && curr_file_info->used && curr_file_info->closed)
+ {
+ if (reopen_closed_file(&tree,curr_file_info))
+ {
+ command=sizeof(com_count)/sizeof(com_count[0][0])/3;
+ result=0;
+ goto com_err;
+ }
+ }
+ }
+ command=(uint) head[0];
+ if (command < sizeof(com_count)/sizeof(com_count[0][0])/3 &&
+ (!table_names[0] || (curr_file_info && curr_file_info->used)))
+ {
+ com_count[command][0]++;
+ if (result)
+ com_count[command][1]++;
+ }
+ switch ((enum myisam_log_commands) command) {
+ case MI_LOG_OPEN:
+ if (!table_names[0])
+ {
+ com_count[command][0]--; /* Must be counted explicite */
+ if (result)
+ com_count[command][1]--;
+ }
+
+ if (curr_file_info)
+ printf("\nWarning: %s is opened with same process and filenumber\nMaybe you should use the -P option ?\n",
+ curr_file_info->show_name);
+ if (my_b_read(&cache,(byte*) head,2))
+ goto err;
+ file_info.name=0;
+ file_info.show_name=0;
+ file_info.record=0;
+ if (read_string(&cache,(gptr*) &file_info.name,
+ (uint) mi_uint2korr(head)))
+ goto err;
+ {
+ uint i;
+ char *pos,*to;
+
+ /* Fix if old DOS files to new format */
+ for (pos=file_info.name; (pos=strchr(pos,'\\')) ; pos++)
+ *pos= '/';
+
+ pos=file_info.name;
+ for (i=0 ; i < prefix_remove ; i++)
+ {
+ char *next;
+ if (!(next=strchr(pos,'/')))
+ break;
+ pos=next+1;
+ }
+ to=isam_file_name;
+ if (filepath)
+ to=convert_dirname(isam_file_name,filepath,NullS);
+ strmov(to,pos);
+ fn_ext(isam_file_name)[0]=0; /* Remove extension */
+ }
+ open_param.name=file_info.name;
+ open_param.max_id=0;
+ VOID(tree_walk(&tree,(tree_walk_action) test_if_open,(void*) &open_param,
+ left_root_right));
+ file_info.id=open_param.max_id+1;
+ /*
+ * In the line below +10 is added to accomodate '<' and '>' chars
+ * plus '\0' at the end, so that there is place for 7 digits.
+ * It is improbable that same table can have that many entries in
+ * the table cache.
+ * The additional space is needed for the sprintf commands two lines
+ * below.
+ */
+ file_info.show_name=my_memdup(isam_file_name,
+ (uint) strlen(isam_file_name)+10,
+ MYF(MY_WME));
+ if (file_info.id > 1)
+ sprintf(strend(file_info.show_name),"<%d>",file_info.id);
+ file_info.closed=1;
+ file_info.accessed=access_time;
+ file_info.used=1;
+ if (table_names[0])
+ {
+ char **name;
+ file_info.used=0;
+ for (name=table_names ; *name ; name++)
+ {
+ if (!strcmp(*name,isam_file_name))
+ file_info.used=1; /* Update/log only this */
+ }
+ }
+ if (update && file_info.used)
+ {
+ if (files_open >= max_files)
+ {
+ if (close_some_file(&tree))
+ goto com_err;
+ files_open--;
+ }
+ if (!(file_info.isam= mi_open(isam_file_name,O_RDWR,
+ HA_OPEN_WAIT_IF_LOCKED)))
+ goto com_err;
+ if (!(file_info.record=my_malloc(file_info.isam->s->base.reclength,
+ MYF(MY_WME))))
+ goto end;
+ files_open++;
+ file_info.closed=0;
+ }
+ VOID(tree_insert(&tree, (gptr) &file_info, 0, tree.custom_arg));
+ if (file_info.used)
+ {
+ if (verbose && !record_pos_file)
+ printf_log("%s: open -> %d",file_info.show_name, file_info.filenr);
+ com_count[command][0]++;
+ if (result)
+ com_count[command][1]++;
+ }
+ break;
+ case MI_LOG_CLOSE:
+ if (verbose && !record_pos_file &&
+ (!table_names[0] || (curr_file_info && curr_file_info->used)))
+ printf_log("%s: %s -> %d",FILENAME(curr_file_info),
+ command_name[command],result);
+ if (curr_file_info)
+ {
+ if (!curr_file_info->closed)
+ files_open--;
+ VOID(tree_delete(&tree, (gptr) curr_file_info, tree.custom_arg));
+ }
+ break;
+ case MI_LOG_EXTRA:
+ if (my_b_read(&cache,(byte*) head,1))
+ goto err;
+ extra_command=(enum ha_extra_function) head[0];
+ if (verbose && !record_pos_file &&
+ (!table_names[0] || (curr_file_info && curr_file_info->used)))
+ printf_log("%s: %s(%d) -> %d",FILENAME(curr_file_info),
+ command_name[command], (int) extra_command,result);
+ if (update && curr_file_info && !curr_file_info->closed)
+ {
+ if (mi_extra(curr_file_info->isam, extra_command, 0) != (int) result)
+ {
+ fflush(stdout);
+ VOID(fprintf(stderr,
+ "Warning: error %d, expected %d on command %s at %s\n",
+ my_errno,result,command_name[command],
+ llstr(isamlog_filepos,llbuff)));
+ fflush(stderr);
+ }
+ }
+ break;
+ case MI_LOG_DELETE:
+ if (my_b_read(&cache,(byte*) head,8))
+ goto err;
+ filepos=mi_sizekorr(head);
+ if (verbose && (!record_pos_file ||
+ ((record_pos == filepos || record_pos == NO_FILEPOS) &&
+ !cmp_filename(curr_file_info,record_pos_file))) &&
+ (!table_names[0] || (curr_file_info && curr_file_info->used)))
+ printf_log("%s: %s at %ld -> %d",FILENAME(curr_file_info),
+ command_name[command],(long) filepos,result);
+ if (update && curr_file_info && !curr_file_info->closed)
+ {
+ if (mi_rrnd(curr_file_info->isam,curr_file_info->record,filepos))
+ {
+ if (!recover)
+ goto com_err;
+ if (verbose)
+ printf_log("error: Didn't find row to delete with mi_rrnd");
+ com_count[command][2]++; /* Mark error */
+ }
+ mi_result=mi_delete(curr_file_info->isam,curr_file_info->record);
+ if ((mi_result == 0 && result) ||
+ (mi_result && (uint) my_errno != result))
+ {
+ if (!recover)
+ goto com_err;
+ if (mi_result)
+ com_count[command][2]++; /* Mark error */
+ if (verbose)
+ printf_log("error: Got result %d from mi_delete instead of %d",
+ mi_result, result);
+ }
+ }
+ break;
+ case MI_LOG_WRITE:
+ case MI_LOG_UPDATE:
+ if (my_b_read(&cache,(byte*) head,12))
+ goto err;
+ filepos=mi_sizekorr(head);
+ length=mi_uint4korr(head+8);
+ buff=0;
+ if (read_string(&cache,&buff,(uint) length))
+ goto err;
+ if ((!record_pos_file ||
+ ((record_pos == filepos || record_pos == NO_FILEPOS) &&
+ !cmp_filename(curr_file_info,record_pos_file))) &&
+ (!table_names[0] || (curr_file_info && curr_file_info->used)))
+ {
+ if (write_file &&
+ (my_fwrite(write_file,buff,length,MYF(MY_WAIT_IF_FULL | MY_NABP))))
+ goto end;
+ if (verbose)
+ printf_log("%s: %s at %ld, length=%ld -> %d",
+ FILENAME(curr_file_info),
+ command_name[command], filepos,length,result);
+ }
+ if (update && curr_file_info && !curr_file_info->closed)
+ {
+ if (curr_file_info->isam->s->base.blobs)
+ fix_blob_pointers(curr_file_info->isam,buff);
+ if ((enum myisam_log_commands) command == MI_LOG_UPDATE)
+ {
+ if (mi_rrnd(curr_file_info->isam,curr_file_info->record,filepos))
+ {
+ if (!recover)
+ {
+ result=0;
+ goto com_err;
+ }
+ if (verbose)
+ printf_log("error: Didn't find row to update with mi_rrnd");
+ if (recover == 1 || result ||
+ find_record_with_key(curr_file_info,buff))
+ {
+ com_count[command][2]++; /* Mark error */
+ break;
+ }
+ }
+ mi_result=mi_update(curr_file_info->isam,curr_file_info->record,
+ buff);
+ if ((mi_result == 0 && result) ||
+ (mi_result && (uint) my_errno != result))
+ {
+ if (!recover)
+ goto com_err;
+ if (verbose)
+ printf_log("error: Got result %d from mi_update instead of %d",
+ mi_result, result);
+ if (mi_result)
+ com_count[command][2]++; /* Mark error */
+ }
+ }
+ else
+ {
+ mi_result=mi_write(curr_file_info->isam,buff);
+ if ((mi_result == 0 && result) ||
+ (mi_result && (uint) my_errno != result))
+ {
+ if (!recover)
+ goto com_err;
+ if (verbose)
+ printf_log("error: Got result %d from mi_write instead of %d",
+ mi_result, result);
+ if (mi_result)
+ com_count[command][2]++; /* Mark error */
+ }
+ if (!recover && filepos != curr_file_info->isam->lastpos)
+ {
+ printf("error: Wrote at position: %s, should have been %s",
+ llstr(curr_file_info->isam->lastpos,llbuff),
+ llstr(filepos,llbuff2));
+ goto end;
+ }
+ }
+ }
+ my_free(buff,MYF(0));
+ break;
+ case MI_LOG_LOCK:
+ if (my_b_read(&cache,(byte*) head,sizeof(lock_command)))
+ goto err;
+ memcpy_fixed(&lock_command,head,sizeof(lock_command));
+ if (verbose && !record_pos_file &&
+ (!table_names[0] || (curr_file_info && curr_file_info->used)))
+ printf_log("%s: %s(%d) -> %d\n",FILENAME(curr_file_info),
+ command_name[command],lock_command,result);
+ if (update && curr_file_info && !curr_file_info->closed)
+ {
+ if (mi_lock_database(curr_file_info->isam,lock_command) !=
+ (int) result)
+ goto com_err;
+ }
+ break;
+ case MI_LOG_DELETE_ALL:
+ if (verbose && !record_pos_file &&
+ (!table_names[0] || (curr_file_info && curr_file_info->used)))
+ printf_log("%s: %s -> %d\n",FILENAME(curr_file_info),
+ command_name[command],result);
+ break;
+ default:
+ fflush(stdout);
+ VOID(fprintf(stderr,
+ "Error: found unknown command %d in logfile, aborted\n",
+ command));
+ fflush(stderr);
+ goto end;
+ }
+ }
+ end_key_cache(dflt_key_cache,1);
+ delete_tree(&tree);
+ VOID(end_io_cache(&cache));
+ VOID(my_close(file,MYF(0)));
+ if (write_file && my_fclose(write_file,MYF(MY_WME)))
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+
+ err:
+ fflush(stdout);
+ VOID(fprintf(stderr,"Got error %d when reading from logfile\n",my_errno));
+ fflush(stderr);
+ goto end;
+ com_err:
+ fflush(stdout);
+ VOID(fprintf(stderr,"Got error %d, expected %d on command %s at %s\n",
+ my_errno,result,command_name[command],
+ llstr(isamlog_filepos,llbuff)));
+ fflush(stderr);
+ end:
+ end_key_cache(dflt_key_cache, 1);
+ delete_tree(&tree);
+ VOID(end_io_cache(&cache));
+ VOID(my_close(file,MYF(0)));
+ if (write_file)
+ VOID(my_fclose(write_file,MYF(MY_WME)));
+ DBUG_RETURN(1);
+}
+
+
+static int read_string(IO_CACHE *file, register gptr *to, register uint length)
+{
+ DBUG_ENTER("read_string");
+
+ if (*to)
+ my_free((gptr) *to,MYF(0));
+ if (!(*to= (gptr) my_malloc(length+1,MYF(MY_WME))) ||
+ my_b_read(file,(byte*) *to,length))
+ {
+ if (*to)
+ my_free(*to,MYF(0));
+ *to= 0;
+ DBUG_RETURN(1);
+ }
+ *((char*) *to+length)= '\0';
+ DBUG_RETURN (0);
+} /* read_string */
+
+
+static int file_info_compare(void* cmp_arg __attribute__((unused)),
+ void *a, void *b)
+{
+ long lint;
+
+ if ((lint=((struct file_info*) a)->process -
+ ((struct file_info*) b)->process))
+ return lint < 0L ? -1 : 1;
+ return ((struct file_info*) a)->filenr - ((struct file_info*) b)->filenr;
+}
+
+ /* ARGSUSED */
+
+static int test_if_open (struct file_info *key,
+ element_count count __attribute__((unused)),
+ struct test_if_open_param *param)
+{
+ if (!strcmp(key->name,param->name) && key->id > param->max_id)
+ param->max_id=key->id;
+ return 0;
+}
+
+
+static void fix_blob_pointers(MI_INFO *info, byte *record)
+{
+ byte *pos;
+ MI_BLOB *blob,*end;
+
+ pos=record+info->s->base.reclength;
+ for (end=info->blobs+info->s->base.blobs, blob= info->blobs;
+ blob != end ;
+ blob++)
+ {
+ memcpy_fixed(record+blob->offset+blob->pack_length,&pos,sizeof(char*));
+ pos+=_mi_calc_blob_length(blob->pack_length,record+blob->offset);
+ }
+}
+
+ /* close the file with hasn't been accessed for the longest time */
+ /* ARGSUSED */
+
+static int test_when_accessed (struct file_info *key,
+ element_count count __attribute__((unused)),
+ struct st_access_param *access_param)
+{
+ if (key->accessed < access_param->min_accessed && ! key->closed)
+ {
+ access_param->min_accessed=key->accessed;
+ access_param->found=key;
+ }
+ return 0;
+}
+
+
+static void file_info_free(struct file_info *fileinfo)
+{
+ DBUG_ENTER("file_info_free");
+ if (update)
+ {
+ if (!fileinfo->closed)
+ VOID(mi_close(fileinfo->isam));
+ if (fileinfo->record)
+ my_free(fileinfo->record,MYF(0));
+ }
+ my_free(fileinfo->name,MYF(0));
+ my_free(fileinfo->show_name,MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+
+static int close_some_file(TREE *tree)
+{
+ struct st_access_param access_param;
+
+ access_param.min_accessed=LONG_MAX;
+ access_param.found=0;
+
+ VOID(tree_walk(tree,(tree_walk_action) test_when_accessed,
+ (void*) &access_param,left_root_right));
+ if (!access_param.found)
+ return 1; /* No open file that is possibly to close */
+ if (mi_close(access_param.found->isam))
+ return 1;
+ access_param.found->closed=1;
+ return 0;
+}
+
+
+static int reopen_closed_file(TREE *tree, struct file_info *fileinfo)
+{
+ char name[FN_REFLEN];
+ if (close_some_file(tree))
+ return 1; /* No file to close */
+ strmov(name,fileinfo->show_name);
+ if (fileinfo->id > 1)
+ *strrchr(name,'<')='\0'; /* Remove "<id>" */
+
+ if (!(fileinfo->isam= mi_open(name,O_RDWR,HA_OPEN_WAIT_IF_LOCKED)))
+ return 1;
+ fileinfo->closed=0;
+ re_open_count++;
+ return 0;
+}
+
+ /* Try to find record with uniq key */
+
+static int find_record_with_key(struct file_info *file_info, byte *record)
+{
+ uint key;
+ MI_INFO *info=file_info->isam;
+ uchar tmp_key[MI_MAX_KEY_BUFF];
+
+ for (key=0 ; key < info->s->base.keys ; key++)
+ {
+ if ((((ulonglong) 1 << key) & info->s->state.key_map) &&
+ info->s->keyinfo[key].flag & HA_NOSAME)
+ {
+ VOID(_mi_make_key(info,key,tmp_key,record,0L));
+ return mi_rkey(info,file_info->record,(int) key,(char*) tmp_key,0,
+ HA_READ_KEY_EXACT);
+ }
+ }
+ return 1;
+}
+
+
+static void printf_log(const char *format,...)
+{
+ char llbuff[21];
+ va_list args;
+ va_start(args,format);
+ if (verbose > 2)
+ printf("%9s:",llstr(isamlog_filepos,llbuff));
+ if (verbose > 1)
+ printf("%5ld ",isamlog_process); /* Write process number */
+ (void) vprintf((char*) format,args);
+ putchar('\n');
+ va_end(args);
+}
+
+
+static bool cmp_filename(struct file_info *file_info, my_string name)
+{
+ if (!file_info)
+ return 1;
+ return strcmp(file_info->name,name) ? 1 : 0;
+}
diff --git a/storage/myisam/myisampack.c b/storage/myisam/myisampack.c
new file mode 100644
index 00000000000..bda620a594a
--- /dev/null
+++ b/storage/myisam/myisampack.c
@@ -0,0 +1,2180 @@
+/* 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 */
+
+/* Pack MyISAM file */
+
+#ifndef USE_MY_FUNC
+#define USE_MY_FUNC /* We need at least my_malloc */
+#endif
+
+#include "myisamdef.h"
+#include <queues.h>
+#include <my_tree.h>
+#include "mysys_err.h"
+#ifdef MSDOS
+#include <io.h>
+#endif
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__ /* Skip warnings in getopt.h */
+#endif
+#include <my_getopt.h>
+
+#if INT_MAX > 32767
+#define BITS_SAVED 32
+#else
+#define BITS_SAVED 16
+#endif
+
+#define IS_OFFSET ((uint) 32768) /* Bit if offset or char in tree */
+#define HEAD_LENGTH 32
+#define ALLOWED_JOIN_DIFF 256 /* Diff allowed to join trees */
+
+#define DATA_TMP_EXT ".TMD"
+#define OLD_EXT ".OLD"
+#define WRITE_COUNT MY_HOW_OFTEN_TO_WRITE
+
+struct st_file_buffer {
+ File file;
+ char *buffer,*pos,*end;
+ my_off_t pos_in_file;
+ int bits;
+ uint current_byte;
+};
+
+struct st_huff_tree;
+struct st_huff_element;
+
+typedef struct st_huff_counts {
+ uint field_length,max_zero_fill;
+ uint pack_type;
+ uint max_end_space,max_pre_space,length_bits,min_space;
+ ulong max_length;
+ enum en_fieldtype field_type;
+ struct st_huff_tree *tree; /* Tree for field */
+ my_off_t counts[256];
+ my_off_t end_space[8];
+ my_off_t pre_space[8];
+ my_off_t tot_end_space,tot_pre_space,zero_fields,empty_fields,bytes_packed;
+ TREE int_tree;
+ byte *tree_buff;
+ byte *tree_pos;
+} HUFF_COUNTS;
+
+typedef struct st_huff_element HUFF_ELEMENT;
+
+struct st_huff_element {
+ my_off_t count;
+ union un_element {
+ struct st_nod {
+ HUFF_ELEMENT *left,*right;
+ } nod;
+ struct st_leaf {
+ HUFF_ELEMENT *null;
+ uint element_nr; /* Number of element */
+ } leaf;
+ } a;
+};
+
+
+typedef struct st_huff_tree {
+ HUFF_ELEMENT *root,*element_buffer;
+ HUFF_COUNTS *counts;
+ uint tree_number;
+ uint elements;
+ my_off_t bytes_packed;
+ uint tree_pack_length;
+ uint min_chr,max_chr,char_bits,offset_bits,max_offset,height;
+ ulong *code;
+ uchar *code_len;
+} HUFF_TREE;
+
+
+typedef struct st_isam_mrg {
+ MI_INFO **file,**current,**end;
+ uint free_file;
+ uint count;
+ uint min_pack_length; /* Theese is used by packed data */
+ uint max_pack_length;
+ uint ref_length;
+ uint max_blob_length;
+ my_off_t records;
+ /* true if at least one source file has at least one disabled index */
+ my_bool src_file_has_indexes_disabled;
+} PACK_MRG_INFO;
+
+
+extern int main(int argc,char * *argv);
+static void get_options(int *argc,char ***argv);
+static MI_INFO *open_isam_file(char *name,int mode);
+static bool open_isam_files(PACK_MRG_INFO *mrg,char **names,uint count);
+static int compress(PACK_MRG_INFO *file,char *join_name);
+static HUFF_COUNTS *init_huff_count(MI_INFO *info,my_off_t records);
+static void free_counts_and_tree_and_queue(HUFF_TREE *huff_trees,
+ uint trees,
+ HUFF_COUNTS *huff_counts,
+ uint fields);
+static int compare_tree(void* cmp_arg __attribute__((unused)),
+ const uchar *s,const uchar *t);
+static int get_statistic(PACK_MRG_INFO *mrg,HUFF_COUNTS *huff_counts);
+static void check_counts(HUFF_COUNTS *huff_counts,uint trees,
+ my_off_t records);
+static int test_space_compress(HUFF_COUNTS *huff_counts,my_off_t records,
+ uint max_space_length,my_off_t *space_counts,
+ my_off_t tot_space_count,
+ enum en_fieldtype field_type);
+static HUFF_TREE* make_huff_trees(HUFF_COUNTS *huff_counts,uint trees);
+static int make_huff_tree(HUFF_TREE *tree,HUFF_COUNTS *huff_counts);
+static int compare_huff_elements(void *not_used, byte *a,byte *b);
+static int save_counts_in_queue(byte *key,element_count count,
+ HUFF_TREE *tree);
+static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,uint flag);
+static uint join_same_trees(HUFF_COUNTS *huff_counts,uint trees);
+static int make_huff_decode_table(HUFF_TREE *huff_tree,uint trees);
+static void make_traverse_code_tree(HUFF_TREE *huff_tree,
+ HUFF_ELEMENT *element,uint size,
+ ulong code);
+static int write_header(PACK_MRG_INFO *isam_file, uint header_length,uint trees,
+ my_off_t tot_elements,my_off_t filelength);
+static void write_field_info(HUFF_COUNTS *counts, uint fields,uint trees);
+static my_off_t write_huff_tree(HUFF_TREE *huff_tree,uint trees);
+static uint *make_offset_code_tree(HUFF_TREE *huff_tree,
+ HUFF_ELEMENT *element,
+ uint *offset);
+static uint max_bit(uint value);
+static int compress_isam_file(PACK_MRG_INFO *file,HUFF_COUNTS *huff_counts);
+static char *make_new_name(char *new_name,char *old_name);
+static char *make_old_name(char *new_name,char *old_name);
+static void init_file_buffer(File file,pbool read_buffer);
+static int flush_buffer(ulong neaded_length);
+static void end_file_buffer(void);
+static void write_bits(ulong value,uint bits);
+static void flush_bits(void);
+static int save_state(MI_INFO *isam_file,PACK_MRG_INFO *mrg,my_off_t new_length,
+ ha_checksum crc);
+static int save_state_mrg(File file,PACK_MRG_INFO *isam_file,my_off_t new_length,
+ ha_checksum crc);
+static int mrg_close(PACK_MRG_INFO *mrg);
+static int mrg_rrnd(PACK_MRG_INFO *info,byte *buf);
+static void mrg_reset(PACK_MRG_INFO *mrg);
+
+
+static int error_on_write=0,test_only=0,verbose=0,silent=0,
+ write_loop=0,force_pack=0, isamchk_neaded=0;
+static int tmpfile_createflag=O_RDWR | O_TRUNC | O_EXCL;
+static my_bool backup, opt_wait;
+static uint tree_buff_length=8196-MALLOC_OVERHEAD;
+static char tmp_dir[FN_REFLEN]={0},*join_table;
+static my_off_t intervall_length;
+static ha_checksum glob_crc;
+static struct st_file_buffer file_buffer;
+static QUEUE queue;
+static HUFF_COUNTS *global_count;
+static char zero_string[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+static const char *load_default_groups[]= { "myisampack",0 };
+
+ /* The main program */
+
+int main(int argc, char **argv)
+{
+ int error,ok;
+ PACK_MRG_INFO merge;
+ char **default_argv;
+ MY_INIT(argv[0]);
+
+ load_defaults("my",load_default_groups,&argc,&argv);
+ default_argv= argv;
+ get_options(&argc,&argv);
+
+ error=ok=isamchk_neaded=0;
+ if (join_table)
+ { /* Join files into one */
+ if (open_isam_files(&merge,argv,(uint) argc) ||
+ compress(&merge,join_table))
+ error=1;
+ }
+ else while (argc--)
+ {
+ MI_INFO *isam_file;
+ if (!(isam_file=open_isam_file(*argv++,O_RDWR)))
+ error=1;
+ else
+ {
+ merge.file= &isam_file;
+ merge.current=0;
+ merge.free_file=0;
+ merge.count=1;
+ if (compress(&merge,0))
+ error=1;
+ else
+ ok=1;
+ }
+ }
+ if (ok && isamchk_neaded && !silent)
+ puts("Remember to run myisamchk -rq on compressed tables");
+ VOID(fflush(stdout)); VOID(fflush(stderr));
+ free_defaults(default_argv);
+ my_end(verbose ? MY_CHECK_ERROR | MY_GIVE_INFO : MY_CHECK_ERROR);
+ exit(error ? 2 : 0);
+#ifndef _lint
+ return 0; /* No compiler warning */
+#endif
+}
+
+enum options_mp {OPT_CHARSETS_DIR_MP=256};
+
+static struct my_option my_long_options[] =
+{
+ {"backup", 'b', "Make a backup of the table as table_name.OLD.",
+ (gptr*) &backup, (gptr*) &backup, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"character-sets-dir", OPT_CHARSETS_DIR_MP,
+ "Directory where character sets are.", (gptr*) &charsets_dir,
+ (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"force", 'f',
+ "Force packing of table even if it gets bigger or if tempfile exists.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"join", 'j',
+ "Join all given tables into 'new_table_name'. All tables MUST have identical layouts.",
+ (gptr*) &join_table, (gptr*) &join_table, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
+ 0, 0, 0},
+ {"help", '?', "Display this help and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"silent", 's', "Be more silent.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"tmpdir", 'T', "Use temporary directory to store temporary table.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"test", 't', "Don't pack table, only test packing it.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose", 'v', "Write info about progress and packing result.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V', "Output version information and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"wait", 'w', "Wait and retry if table is in use.", (gptr*) &opt_wait,
+ (gptr*) &opt_wait, 0, GET_BOOL, 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}
+};
+
+#include <help_start.h>
+
+static void print_version(void)
+{
+ printf("%s Ver 1.22 for %s on %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
+ NETWARE_SET_SCREEN_MODE(1);
+}
+
+
+static void usage(void)
+{
+ print_version();
+ puts("Copyright (C) 2002 MySQL AB");
+ puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,");
+ puts("and you are welcome to modify and redistribute it under the GPL license\n");
+
+ puts("Pack a MyISAM-table to take much less space.");
+ puts("Keys are not updated, you must run myisamchk -rq on the datafile");
+ puts("afterwards to update the keys.");
+ puts("You should give the .MYI file as the filename argument.");
+
+ printf("\nUsage: %s [OPTIONS] filename...\n", my_progname);
+ my_print_help(my_long_options);
+ print_defaults("my", load_default_groups);
+ my_print_variables(my_long_options);
+}
+
+#include <help_end.h>
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ uint length;
+
+ switch(optid) {
+ case 'f':
+ force_pack= 1;
+ tmpfile_createflag= O_RDWR | O_TRUNC;
+ break;
+ case 's':
+ write_loop= verbose= 0;
+ silent= 1;
+ break;
+ case 't':
+ test_only= verbose= 1;
+ break;
+ case 'T':
+ length= (uint) (strmov(tmp_dir, argument) - tmp_dir);
+ if (length != dirname_length(tmp_dir))
+ {
+ tmp_dir[length]=FN_LIBCHAR;
+ tmp_dir[length+1]=0;
+ }
+ break;
+ case 'v':
+ verbose= 1;
+ silent= 0;
+ break;
+ case '#':
+ DBUG_PUSH(argument ? argument : "d:t:o");
+ break;
+ case 'V':
+ print_version();
+ exit(0);
+ case 'I':
+ case '?':
+ usage();
+ exit(0);
+ }
+ return 0;
+}
+
+ /* reads options */
+ /* Initiates DEBUG - but no debugging here ! */
+
+static void get_options(int *argc,char ***argv)
+{
+ int ho_error;
+
+ my_progname= argv[0][0];
+ if (isatty(fileno(stdout)))
+ write_loop=1;
+
+ if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
+ exit(ho_error);
+
+ if (!*argc)
+ {
+ usage();
+ exit(1);
+ }
+ if (join_table)
+ {
+ backup=0; /* Not needed */
+ tmp_dir[0]=0;
+ }
+ return;
+}
+
+
+static MI_INFO *open_isam_file(char *name,int mode)
+{
+ MI_INFO *isam_file;
+ MYISAM_SHARE *share;
+ DBUG_ENTER("open_isam_file");
+
+ if (!(isam_file=mi_open(name,mode,
+ (opt_wait ? HA_OPEN_WAIT_IF_LOCKED :
+ HA_OPEN_ABORT_IF_LOCKED))))
+ {
+ VOID(fprintf(stderr,"%s gave error %d on open\n",name,my_errno));
+ DBUG_RETURN(0);
+ }
+ share=isam_file->s;
+ if (share->options & HA_OPTION_COMPRESS_RECORD && !join_table)
+ {
+ if (!force_pack)
+ {
+ VOID(fprintf(stderr,"%s is already compressed\n",name));
+ VOID(mi_close(isam_file));
+ DBUG_RETURN(0);
+ }
+ if (verbose)
+ puts("Recompressing already compressed table");
+ share->options&= ~HA_OPTION_READ_ONLY_DATA; /* We are modifing it */
+ }
+ if (! force_pack && share->state.state.records != 0 &&
+ (share->state.state.records <= 1 ||
+ share->state.state.data_file_length < 1024))
+ {
+ VOID(fprintf(stderr,"%s is too small to compress\n",name));
+ VOID(mi_close(isam_file));
+ DBUG_RETURN(0);
+ }
+ VOID(mi_lock_database(isam_file,F_WRLCK));
+ DBUG_RETURN(isam_file);
+}
+
+
+static bool open_isam_files(PACK_MRG_INFO *mrg,char **names,uint count)
+{
+ uint i,j;
+ mrg->count=0;
+ mrg->current=0;
+ mrg->file=(MI_INFO**) my_malloc(sizeof(MI_INFO*)*count,MYF(MY_FAE));
+ mrg->free_file=1;
+ mrg->src_file_has_indexes_disabled= 0;
+ for (i=0; i < count ; i++)
+ {
+ if (!(mrg->file[i]=open_isam_file(names[i],O_RDONLY)))
+ goto error;
+
+ mrg->src_file_has_indexes_disabled|= ((mrg->file[i]->s->state.key_map !=
+ (((ulonglong) 1) <<
+ mrg->file[i]->s->base. keys) - 1));
+ }
+ /* Check that files are identical */
+ for (j=0 ; j < count-1 ; j++)
+ {
+ MI_COLUMNDEF *m1,*m2,*end;
+ if (mrg->file[j]->s->base.reclength != mrg->file[j+1]->s->base.reclength ||
+ mrg->file[j]->s->base.fields != mrg->file[j+1]->s->base.fields)
+ goto diff_file;
+ m1=mrg->file[j]->s->rec;
+ end=m1+mrg->file[j]->s->base.fields;
+ m2=mrg->file[j+1]->s->rec;
+ for ( ; m1 != end ; m1++,m2++)
+ {
+ if (m1->type != m2->type || m1->length != m2->length)
+ goto diff_file;
+ }
+ }
+ mrg->count=count;
+ return 0;
+
+ diff_file:
+ fprintf(stderr,"%s: Tables '%s' and '%s' are not identical\n",
+ my_progname,names[j],names[j+1]);
+ error:
+ while (i--)
+ mi_close(mrg->file[i]);
+ my_free((gptr) mrg->file,MYF(0));
+ return 1;
+}
+
+
+static int compress(PACK_MRG_INFO *mrg,char *result_table)
+{
+ int error;
+ File new_file,join_isam_file;
+ MI_INFO *isam_file;
+ MYISAM_SHARE *share;
+ char org_name[FN_REFLEN],new_name[FN_REFLEN],temp_name[FN_REFLEN];
+ uint i,header_length,fields,trees,used_trees;
+ my_off_t old_length,new_length,tot_elements;
+ HUFF_COUNTS *huff_counts;
+ HUFF_TREE *huff_trees;
+ DBUG_ENTER("compress");
+
+ isam_file=mrg->file[0]; /* Take this as an example */
+ share=isam_file->s;
+ new_file=join_isam_file= -1;
+ trees=fields=0;
+ huff_trees=0;
+ huff_counts=0;
+
+ /* Create temporary or join file */
+
+ if (backup)
+ VOID(fn_format(org_name,isam_file->filename,"",MI_NAME_DEXT,2));
+ else
+ VOID(fn_format(org_name,isam_file->filename,"",MI_NAME_DEXT,2+4+16));
+ if (!test_only && result_table)
+ {
+ /* Make a new indexfile based on first file in list */
+ uint length;
+ char *buff;
+ strmov(org_name,result_table); /* Fix error messages */
+ VOID(fn_format(new_name,result_table,"",MI_NAME_IEXT,2));
+ if ((join_isam_file=my_create(new_name,0,tmpfile_createflag,MYF(MY_WME)))
+ < 0)
+ goto err;
+ length=(uint) share->base.keystart;
+ if (!(buff=my_malloc(length,MYF(MY_WME))))
+ goto err;
+ if (my_pread(share->kfile,buff,length,0L,MYF(MY_WME | MY_NABP)) ||
+ my_write(join_isam_file,buff,length,
+ MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
+ {
+ my_free(buff,MYF(0));
+ goto err;
+ }
+ my_free(buff,MYF(0));
+ VOID(fn_format(new_name,result_table,"",MI_NAME_DEXT,2));
+ }
+ else if (!tmp_dir[0])
+ VOID(make_new_name(new_name,org_name));
+ else
+ VOID(fn_format(new_name,org_name,tmp_dir,DATA_TMP_EXT,1+2+4));
+ if (!test_only &&
+ (new_file=my_create(new_name,0,tmpfile_createflag,MYF(MY_WME))) < 0)
+ goto err;
+
+ /* Start calculating statistics */
+
+ mrg->records=0;
+ for (i=0 ; i < mrg->count ; i++)
+ mrg->records+=mrg->file[i]->s->state.state.records;
+ if (write_loop || verbose)
+ {
+ printf("Compressing %s: (%lu records)\n",
+ result_table ? new_name : org_name,(ulong) mrg->records);
+ }
+ trees=fields=share->base.fields;
+ huff_counts=init_huff_count(isam_file,mrg->records);
+ QUICK_SAFEMALLOC;
+ if (write_loop || verbose)
+ printf("- Calculating statistics\n");
+ if (get_statistic(mrg,huff_counts))
+ goto err;
+ NORMAL_SAFEMALLOC;
+ old_length=0;
+ for (i=0; i < mrg->count ; i++)
+ old_length+= (mrg->file[i]->s->state.state.data_file_length -
+ mrg->file[i]->s->state.state.empty);
+
+ if (init_queue(&queue,256,0,0,compare_huff_elements,0))
+ goto err;
+ check_counts(huff_counts,fields,mrg->records);
+ huff_trees=make_huff_trees(huff_counts,trees);
+ if ((int) (used_trees=join_same_trees(huff_counts,trees)) < 0)
+ goto err;
+ if (make_huff_decode_table(huff_trees,fields))
+ goto err;
+
+ init_file_buffer(new_file,0);
+ file_buffer.pos_in_file=HEAD_LENGTH;
+ if (! test_only)
+ VOID(my_seek(new_file,file_buffer.pos_in_file,MY_SEEK_SET,MYF(0)));
+
+ write_field_info(huff_counts,fields,used_trees);
+ if (!(tot_elements=write_huff_tree(huff_trees,trees)))
+ goto err;
+ header_length=(uint) file_buffer.pos_in_file+
+ (uint) (file_buffer.pos-file_buffer.buffer);
+
+ /* Compress file */
+ if (write_loop || verbose)
+ printf("- Compressing file\n");
+ error=compress_isam_file(mrg,huff_counts);
+ new_length=file_buffer.pos_in_file;
+ if (!error && !test_only)
+ {
+ char buff[MEMMAP_EXTRA_MARGIN]; /* End marginal for memmap */
+ bzero(buff,sizeof(buff));
+ error=my_write(file_buffer.file,buff,sizeof(buff),
+ MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)) != 0;
+ }
+ if (!error)
+ error=write_header(mrg,header_length,used_trees,tot_elements,
+ new_length);
+ end_file_buffer();
+
+ if (verbose && mrg->records)
+ printf("Min record length: %6d Max length: %6d Mean total length: %6ld\n",
+ mrg->min_pack_length,mrg->max_pack_length,
+ (ulong) (new_length/mrg->records));
+
+ if (!test_only)
+ {
+ error|=my_close(new_file,MYF(MY_WME));
+ if (!result_table)
+ {
+ error|=my_close(isam_file->dfile,MYF(MY_WME));
+ isam_file->dfile= -1; /* Tell mi_close file is closed */
+ }
+ }
+
+ free_counts_and_tree_and_queue(huff_trees,trees,huff_counts,fields);
+ if (! test_only && ! error)
+ {
+ if (result_table)
+ {
+ error=save_state_mrg(join_isam_file,mrg,new_length,glob_crc);
+ }
+ else
+ {
+ if (backup)
+ {
+ if (my_rename(org_name,make_old_name(temp_name,isam_file->filename),
+ MYF(MY_WME)))
+ error=1;
+ else
+ {
+ if (tmp_dir[0])
+ error=my_copy(new_name,org_name,MYF(MY_WME));
+ else
+ error=my_rename(new_name,org_name,MYF(MY_WME));
+ if (!error)
+ VOID(my_copystat(temp_name,org_name,MYF(MY_COPYTIME)));
+ }
+ }
+ else
+ {
+ if (tmp_dir[0])
+ error=my_copy(new_name,org_name,
+ MYF(MY_WME | MY_HOLD_ORIGINAL_MODES | MY_COPYTIME));
+ else
+ error=my_redel(org_name,new_name,MYF(MY_WME | MY_COPYTIME));
+ }
+ if (! error)
+ error=save_state(isam_file,mrg,new_length,glob_crc);
+ }
+ }
+ error|=mrg_close(mrg);
+ if (join_isam_file >= 0)
+ error|=my_close(join_isam_file,MYF(MY_WME));
+ if (error)
+ {
+ VOID(fprintf(stderr,"Aborting: %s is not compressed\n",org_name));
+ VOID(my_delete(new_name,MYF(MY_WME)));
+ DBUG_RETURN(-1);
+ }
+ if (write_loop || verbose)
+ {
+ if (old_length)
+ printf("%.4g%% \n", (((longlong) (old_length -new_length))*100.0/
+ (longlong) old_length));
+ else
+ puts("Empty file saved in compressed format");
+ }
+ DBUG_RETURN(0);
+
+ err:
+ free_counts_and_tree_and_queue(huff_trees,trees,huff_counts,fields);
+ if (new_file >= 0)
+ VOID(my_close(new_file,MYF(0)));
+ if (join_isam_file >= 0)
+ VOID(my_close(join_isam_file,MYF(0)));
+ mrg_close(mrg);
+ VOID(fprintf(stderr,"Aborted: %s is not compressed\n",org_name));
+ DBUG_RETURN(-1);
+}
+
+ /* Init a huff_count-struct for each field and init it */
+
+static HUFF_COUNTS *init_huff_count(MI_INFO *info,my_off_t records)
+{
+ reg2 uint i;
+ reg1 HUFF_COUNTS *count;
+ if ((count = (HUFF_COUNTS*) my_malloc(info->s->base.fields*
+ sizeof(HUFF_COUNTS),
+ MYF(MY_ZEROFILL | MY_WME))))
+ {
+ for (i=0 ; i < info->s->base.fields ; i++)
+ {
+ enum en_fieldtype type;
+ count[i].field_length=info->s->rec[i].length;
+ type= count[i].field_type= (enum en_fieldtype) info->s->rec[i].type;
+ if (type == FIELD_INTERVALL ||
+ type == FIELD_CONSTANT ||
+ type == FIELD_ZERO)
+ type = FIELD_NORMAL;
+ if (count[i].field_length <= 8 &&
+ (type == FIELD_NORMAL ||
+ type == FIELD_SKIP_ZERO))
+ count[i].max_zero_fill= count[i].field_length;
+ init_tree(&count[i].int_tree,0,0,-1,(qsort_cmp2) compare_tree,0, NULL,
+ NULL);
+ if (records && type != FIELD_BLOB && type != FIELD_VARCHAR)
+ count[i].tree_pos=count[i].tree_buff =
+ my_malloc(count[i].field_length > 1 ? tree_buff_length : 2,
+ MYF(MY_WME));
+ }
+ }
+ return count;
+}
+
+
+ /* Free memory used by counts and trees */
+
+static void free_counts_and_tree_and_queue(HUFF_TREE *huff_trees, uint trees,
+ HUFF_COUNTS *huff_counts,
+ uint fields)
+{
+ register uint i;
+
+ if (huff_trees)
+ {
+ for (i=0 ; i < trees ; i++)
+ {
+ if (huff_trees[i].element_buffer)
+ my_free((gptr) huff_trees[i].element_buffer,MYF(0));
+ if (huff_trees[i].code)
+ my_free((gptr) huff_trees[i].code,MYF(0));
+ }
+ my_free((gptr) huff_trees,MYF(0));
+ }
+ if (huff_counts)
+ {
+ for (i=0 ; i < fields ; i++)
+ {
+ if (huff_counts[i].tree_buff)
+ {
+ my_free((gptr) huff_counts[i].tree_buff,MYF(0));
+ delete_tree(&huff_counts[i].int_tree);
+ }
+ }
+ my_free((gptr) huff_counts,MYF(0));
+ }
+ delete_queue(&queue); /* This is safe to free */
+ return;
+}
+
+ /* Read through old file and gather some statistics */
+
+static int get_statistic(PACK_MRG_INFO *mrg,HUFF_COUNTS *huff_counts)
+{
+ int error;
+ uint length;
+ ulong reclength,max_blob_length;
+ byte *record,*pos,*next_pos,*end_pos,*start_pos;
+ ha_rows record_count;
+ my_bool static_row_size;
+ HUFF_COUNTS *count,*end_count;
+ TREE_ELEMENT *element;
+ DBUG_ENTER("get_statistic");
+
+ reclength=mrg->file[0]->s->base.reclength;
+ record=(byte*) my_alloca(reclength);
+ end_count=huff_counts+mrg->file[0]->s->base.fields;
+ record_count=0; glob_crc=0;
+ max_blob_length=0;
+
+ /* Check how to calculate checksum */
+ static_row_size=1;
+ for (count=huff_counts ; count < end_count ; count++)
+ {
+ if (count->field_type == FIELD_BLOB ||
+ count->field_type == FIELD_VARCHAR)
+ {
+ static_row_size=0;
+ break;
+ }
+ }
+
+ mrg_reset(mrg);
+ while ((error=mrg_rrnd(mrg,record)) != HA_ERR_END_OF_FILE)
+ {
+ ulong tot_blob_length=0;
+ if (! error)
+ {
+ if (static_row_size)
+ glob_crc+=mi_static_checksum(mrg->file[0],record);
+ else
+ glob_crc+=mi_checksum(mrg->file[0],record);
+ for (pos=record,count=huff_counts ;
+ count < end_count ;
+ count++,
+ pos=next_pos)
+ {
+ next_pos=end_pos=(start_pos=pos)+count->field_length;
+
+ /* Put value in tree if there is room for it */
+ if (count->tree_buff)
+ {
+ global_count=count;
+ if (!(element=tree_insert(&count->int_tree,pos, 0,
+ count->int_tree.custom_arg)) ||
+ (element->count == 1 &&
+ count->tree_buff + tree_buff_length <
+ count->tree_pos + count->field_length) ||
+ (count->field_length == 1 &&
+ count->int_tree.elements_in_tree > 1))
+ {
+ delete_tree(&count->int_tree);
+ my_free(count->tree_buff,MYF(0));
+ count->tree_buff=0;
+ }
+ else
+ {
+ if (element->count == 1)
+ { /* New element */
+ memcpy(count->tree_pos,pos,(size_t) count->field_length);
+ tree_set_pointer(element,count->tree_pos);
+ count->tree_pos+=count->field_length;
+ }
+ }
+ }
+
+ /* Save character counters and space-counts and zero-field-counts */
+ if (count->field_type == FIELD_NORMAL ||
+ count->field_type == FIELD_SKIP_ENDSPACE)
+ {
+ for ( ; end_pos > pos ; end_pos--)
+ if (end_pos[-1] != ' ')
+ break;
+ if (end_pos == pos)
+ {
+ count->empty_fields++;
+ count->max_zero_fill=0;
+ continue;
+ }
+ length= (uint) (next_pos-end_pos);
+ count->tot_end_space+=length;
+ if (length < 8)
+ count->end_space[length]++;
+ if (count->max_end_space < length)
+ count->max_end_space = length;
+ }
+ if (count->field_type == FIELD_NORMAL ||
+ count->field_type == FIELD_SKIP_PRESPACE)
+ {
+ for (pos=start_pos; pos < end_pos ; pos++)
+ if (pos[0] != ' ')
+ break;
+ if (end_pos == pos)
+ {
+ count->empty_fields++;
+ count->max_zero_fill=0;
+ continue;
+ }
+ length= (uint) (pos-start_pos);
+ count->tot_pre_space+=length;
+ if (length < 8)
+ count->pre_space[length]++;
+ if (count->max_pre_space < length)
+ count->max_pre_space = length;
+ }
+ if (count->field_type == FIELD_BLOB)
+ {
+ uint field_length=count->field_length -mi_portable_sizeof_char_ptr;
+ ulong blob_length= _mi_calc_blob_length(field_length, start_pos);
+ memcpy_fixed((char*) &pos, start_pos+field_length,sizeof(char*));
+ end_pos=pos+blob_length;
+ tot_blob_length+=blob_length;
+ set_if_bigger(count->max_length,blob_length);
+ }
+ else if (count->field_type == FIELD_VARCHAR)
+ {
+ uint pack_length= HA_VARCHAR_PACKLENGTH(count->field_length-1);
+ length= (pack_length == 1 ? (uint) *(uchar*) start_pos :
+ uint2korr(start_pos));
+ pos= start_pos+pack_length;
+ end_pos= pos+length;
+ set_if_bigger(count->max_length,length);
+ }
+ if (count->field_length <= 8 &&
+ (count->field_type == FIELD_NORMAL ||
+ count->field_type == FIELD_SKIP_ZERO))
+ {
+ uint i;
+ if (!memcmp((byte*) start_pos,zero_string,count->field_length))
+ {
+ count->zero_fields++;
+ continue;
+ }
+ for (i =0 ; i < count->max_zero_fill && ! end_pos[-1 - (int) i] ;
+ i++) ;
+ if (i < count->max_zero_fill)
+ count->max_zero_fill=i;
+ }
+ if (count->field_type == FIELD_ZERO ||
+ count->field_type == FIELD_CHECK)
+ continue;
+ for ( ; pos < end_pos ; pos++)
+ count->counts[(uchar) *pos]++;
+ }
+ if (tot_blob_length > max_blob_length)
+ max_blob_length=tot_blob_length;
+ record_count++;
+ if (write_loop && record_count % WRITE_COUNT == 0)
+ {
+ printf("%lu\r",(ulong) record_count); VOID(fflush(stdout));
+ }
+ }
+ else if (error != HA_ERR_RECORD_DELETED)
+ {
+ fprintf(stderr,"Got error %d while reading rows",error);
+ break;
+ }
+ }
+ if (write_loop)
+ {
+ printf(" \r"); VOID(fflush(stdout));
+ }
+ mrg->records=record_count;
+ mrg->max_blob_length=max_blob_length;
+ my_afree((gptr) record);
+ DBUG_RETURN(error != HA_ERR_END_OF_FILE);
+}
+
+static int compare_huff_elements(void *not_used __attribute__((unused)),
+ byte *a, byte *b)
+{
+ return *((my_off_t*) a) < *((my_off_t*) b) ? -1 :
+ (*((my_off_t*) a) == *((my_off_t*) b) ? 0 : 1);
+}
+
+ /* Check each tree if we should use pre-space-compress, end-space-
+ compress, empty-field-compress or zero-field-compress */
+
+static void check_counts(HUFF_COUNTS *huff_counts, uint trees,
+ my_off_t records)
+{
+ uint space_fields,fill_zero_fields,field_count[(int) FIELD_VARCHAR+1];
+ my_off_t old_length,new_length,length;
+ DBUG_ENTER("check_counts");
+
+ bzero((gptr) field_count,sizeof(field_count));
+ space_fields=fill_zero_fields=0;
+
+ for (; trees-- ; huff_counts++)
+ {
+ if (huff_counts->field_type == FIELD_BLOB)
+ {
+ huff_counts->length_bits=max_bit(huff_counts->max_length);
+ goto found_pack;
+ }
+ else if (huff_counts->field_type == FIELD_VARCHAR)
+ {
+ huff_counts->length_bits=max_bit(huff_counts->max_length);
+ goto found_pack;
+ }
+ else if (huff_counts->field_type == FIELD_CHECK)
+ {
+ huff_counts->bytes_packed=0;
+ huff_counts->counts[0]=0;
+ goto found_pack;
+ }
+
+ huff_counts->field_type=FIELD_NORMAL;
+ huff_counts->pack_type=0;
+
+ if (huff_counts->zero_fields || ! records)
+ {
+ my_off_t old_space_count;
+ if (huff_counts->zero_fields == records)
+ {
+ huff_counts->field_type= FIELD_ZERO;
+ huff_counts->bytes_packed=0;
+ huff_counts->counts[0]=0;
+ goto found_pack;
+ }
+ old_space_count=huff_counts->counts[' '];
+ huff_counts->counts[' ']+=huff_counts->tot_end_space+
+ huff_counts->tot_pre_space +
+ huff_counts->empty_fields * huff_counts->field_length;
+ old_length=calc_packed_length(huff_counts,0)+records/8;
+ length=huff_counts->zero_fields*huff_counts->field_length;
+ huff_counts->counts[0]+=length;
+ new_length=calc_packed_length(huff_counts,0);
+ if (old_length < new_length && huff_counts->field_length > 1)
+ {
+ huff_counts->field_type=FIELD_SKIP_ZERO;
+ huff_counts->counts[0]-=length;
+ huff_counts->bytes_packed=old_length- records/8;
+ goto found_pack;
+ }
+ huff_counts->counts[' ']=old_space_count;
+ }
+ huff_counts->bytes_packed=calc_packed_length(huff_counts,0);
+ if (huff_counts->empty_fields)
+ {
+ if (huff_counts->field_length > 2 &&
+ huff_counts->empty_fields + (records - huff_counts->empty_fields)*
+ (1+max_bit(max(huff_counts->max_pre_space,
+ huff_counts->max_end_space))) <
+ records * max_bit(huff_counts->field_length))
+ {
+ huff_counts->pack_type |= PACK_TYPE_SPACE_FIELDS;
+ }
+ else
+ {
+ length=huff_counts->empty_fields*huff_counts->field_length;
+ if (huff_counts->tot_end_space || ! huff_counts->tot_pre_space)
+ {
+ huff_counts->tot_end_space+=length;
+ huff_counts->max_end_space=huff_counts->field_length;
+ if (huff_counts->field_length < 8)
+ huff_counts->end_space[huff_counts->field_length]+=
+ huff_counts->empty_fields;
+ }
+ if (huff_counts->tot_pre_space)
+ {
+ huff_counts->tot_pre_space+=length;
+ huff_counts->max_pre_space=huff_counts->field_length;
+ if (huff_counts->field_length < 8)
+ huff_counts->pre_space[huff_counts->field_length]+=
+ huff_counts->empty_fields;
+ }
+ }
+ }
+ if (huff_counts->tot_end_space)
+ {
+ huff_counts->counts[' ']+=huff_counts->tot_pre_space;
+ if (test_space_compress(huff_counts,records,huff_counts->max_end_space,
+ huff_counts->end_space,
+ huff_counts->tot_end_space,FIELD_SKIP_ENDSPACE))
+ goto found_pack;
+ huff_counts->counts[' ']-=huff_counts->tot_pre_space;
+ }
+ if (huff_counts->tot_pre_space)
+ {
+ if (test_space_compress(huff_counts,records,huff_counts->max_pre_space,
+ huff_counts->pre_space,
+ huff_counts->tot_pre_space,FIELD_SKIP_PRESPACE))
+ goto found_pack;
+ }
+
+ found_pack: /* Found field-packing */
+
+ /* Test if we can use zero-fill */
+
+ if (huff_counts->max_zero_fill &&
+ (huff_counts->field_type == FIELD_NORMAL ||
+ huff_counts->field_type == FIELD_SKIP_ZERO))
+ {
+ huff_counts->counts[0]-=huff_counts->max_zero_fill*
+ (huff_counts->field_type == FIELD_SKIP_ZERO ?
+ records - huff_counts->zero_fields : records);
+ huff_counts->pack_type|=PACK_TYPE_ZERO_FILL;
+ huff_counts->bytes_packed=calc_packed_length(huff_counts,0);
+ }
+
+ /* Test if intervall-field is better */
+
+ if (huff_counts->tree_buff)
+ {
+ HUFF_TREE tree;
+
+ tree.element_buffer=0;
+ if (!make_huff_tree(&tree,huff_counts) &&
+ tree.bytes_packed+tree.tree_pack_length < huff_counts->bytes_packed)
+ {
+ if (tree.elements == 1)
+ huff_counts->field_type=FIELD_CONSTANT;
+ else
+ huff_counts->field_type=FIELD_INTERVALL;
+ huff_counts->pack_type=0;
+ }
+ else
+ {
+ my_free((gptr) huff_counts->tree_buff,MYF(0));
+ delete_tree(&huff_counts->int_tree);
+ huff_counts->tree_buff=0;
+ }
+ if (tree.element_buffer)
+ my_free((gptr) tree.element_buffer,MYF(0));
+ }
+ if (huff_counts->pack_type & PACK_TYPE_SPACE_FIELDS)
+ space_fields++;
+ if (huff_counts->pack_type & PACK_TYPE_ZERO_FILL)
+ fill_zero_fields++;
+ field_count[huff_counts->field_type]++;
+ }
+ if (verbose)
+ printf("\nnormal: %3d empty-space: %3d empty-zero: %3d empty-fill: %3d\npre-space: %3d end-space: %3d intervall-fields: %3d zero: %3d\n",
+ field_count[FIELD_NORMAL],space_fields,
+ field_count[FIELD_SKIP_ZERO],fill_zero_fields,
+ field_count[FIELD_SKIP_PRESPACE],
+ field_count[FIELD_SKIP_ENDSPACE],
+ field_count[FIELD_INTERVALL],
+ field_count[FIELD_ZERO]);
+ DBUG_VOID_RETURN;
+}
+
+ /* Test if we can use space-compression and empty-field-compression */
+
+static int
+test_space_compress(HUFF_COUNTS *huff_counts, my_off_t records,
+ uint max_space_length, my_off_t *space_counts,
+ my_off_t tot_space_count, enum en_fieldtype field_type)
+{
+ int min_pos;
+ uint length_bits,i;
+ my_off_t space_count,min_space_count,min_pack,new_length,skip;
+
+ length_bits=max_bit(max_space_length);
+
+ /* Default no end_space-packing */
+ space_count=huff_counts->counts[(uint) ' '];
+ min_space_count= (huff_counts->counts[(uint) ' ']+= tot_space_count);
+ min_pack=calc_packed_length(huff_counts,0);
+ min_pos= -2;
+ huff_counts->counts[(uint) ' ']=space_count;
+
+ /* Test with allways space-count */
+ new_length=huff_counts->bytes_packed+length_bits*records/8;
+ if (new_length+1 < min_pack)
+ {
+ min_pos= -1;
+ min_pack=new_length;
+ min_space_count=space_count;
+ }
+ /* Test with length-flag */
+ for (skip=0L, i=0 ; i < 8 ; i++)
+ {
+ if (space_counts[i])
+ {
+ if (i)
+ huff_counts->counts[(uint) ' ']+=space_counts[i];
+ skip+=huff_counts->pre_space[i];
+ new_length=calc_packed_length(huff_counts,0)+
+ (records+(records-skip)*(1+length_bits))/8;
+ if (new_length < min_pack)
+ {
+ min_pos=(int) i;
+ min_pack=new_length;
+ min_space_count=huff_counts->counts[(uint) ' '];
+ }
+ }
+ }
+
+ huff_counts->counts[(uint) ' ']=min_space_count;
+ huff_counts->bytes_packed=min_pack;
+ switch (min_pos) {
+ case -2:
+ return(0); /* No space-compress */
+ case -1: /* Always space-count */
+ huff_counts->field_type=field_type;
+ huff_counts->min_space=0;
+ huff_counts->length_bits=max_bit(max_space_length);
+ break;
+ default:
+ huff_counts->field_type=field_type;
+ huff_counts->min_space=(uint) min_pos;
+ huff_counts->pack_type|=PACK_TYPE_SELECTED;
+ huff_counts->length_bits=max_bit(max_space_length);
+ break;
+ }
+ return(1); /* Using space-compress */
+}
+
+
+ /* Make a huff_tree of each huff_count */
+
+static HUFF_TREE* make_huff_trees(HUFF_COUNTS *huff_counts, uint trees)
+{
+ uint tree;
+ HUFF_TREE *huff_tree;
+ DBUG_ENTER("make_huff_trees");
+
+ if (!(huff_tree=(HUFF_TREE*) my_malloc(trees*sizeof(HUFF_TREE),
+ MYF(MY_WME | MY_ZEROFILL))))
+ DBUG_RETURN(0);
+
+ for (tree=0 ; tree < trees ; tree++)
+ {
+ if (make_huff_tree(huff_tree+tree,huff_counts+tree))
+ {
+ while (tree--)
+ my_free((gptr) huff_tree[tree].element_buffer,MYF(0));
+ my_free((gptr) huff_tree,MYF(0));
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_RETURN(huff_tree);
+}
+
+ /* Update huff_tree according to huff_counts->counts or
+ huff_counts->tree_buff */
+
+static int make_huff_tree(HUFF_TREE *huff_tree, HUFF_COUNTS *huff_counts)
+{
+ uint i,found,bits_packed,first,last;
+ my_off_t bytes_packed;
+ HUFF_ELEMENT *a,*b,*new_huff_el;
+
+ first=last=0;
+ if (huff_counts->tree_buff)
+ {
+ found= (uint) (huff_counts->tree_pos - huff_counts->tree_buff) /
+ huff_counts->field_length;
+ first=0; last=found-1;
+ }
+ else
+ {
+ for (i=found=0 ; i < 256 ; i++)
+ {
+ if (huff_counts->counts[i])
+ {
+ if (! found++)
+ first=i;
+ last=i;
+ }
+ }
+ if (found < 2)
+ found=2;
+ }
+
+ if (queue.max_elements < found)
+ {
+ delete_queue(&queue);
+ if (init_queue(&queue,found,0,0,compare_huff_elements,0))
+ return -1;
+ }
+
+ if (!huff_tree->element_buffer)
+ {
+ if (!(huff_tree->element_buffer=
+ (HUFF_ELEMENT*) my_malloc(found*2*sizeof(HUFF_ELEMENT),MYF(MY_WME))))
+ return 1;
+ }
+ else
+ {
+ HUFF_ELEMENT *temp;
+ if (!(temp=
+ (HUFF_ELEMENT*) my_realloc((gptr) huff_tree->element_buffer,
+ found*2*sizeof(HUFF_ELEMENT),
+ MYF(MY_WME))))
+ return 1;
+ huff_tree->element_buffer=temp;
+ }
+
+ huff_counts->tree=huff_tree;
+ huff_tree->counts=huff_counts;
+ huff_tree->min_chr=first;
+ huff_tree->max_chr=last;
+ huff_tree->char_bits=max_bit(last-first);
+ huff_tree->offset_bits=max_bit(found-1)+1;
+
+ if (huff_counts->tree_buff)
+ {
+ huff_tree->elements=0;
+ tree_walk(&huff_counts->int_tree,
+ (int (*)(void*, element_count,void*)) save_counts_in_queue,
+ (gptr) huff_tree, left_root_right);
+ huff_tree->tree_pack_length=(1+15+16+5+5+
+ (huff_tree->char_bits+1)*found+
+ (huff_tree->offset_bits+1)*
+ (found-2)+7)/8 +
+ (uint) (huff_tree->counts->tree_pos-
+ huff_tree->counts->tree_buff);
+ }
+ else
+ {
+ huff_tree->elements=found;
+ huff_tree->tree_pack_length=(9+9+5+5+
+ (huff_tree->char_bits+1)*found+
+ (huff_tree->offset_bits+1)*
+ (found-2)+7)/8;
+
+ for (i=first, found=0 ; i <= last ; i++)
+ {
+ if (huff_counts->counts[i])
+ {
+ new_huff_el=huff_tree->element_buffer+(found++);
+ new_huff_el->count=huff_counts->counts[i];
+ new_huff_el->a.leaf.null=0;
+ new_huff_el->a.leaf.element_nr=i;
+ queue.root[found]=(byte*) new_huff_el;
+ }
+ }
+ while (found < 2)
+ { /* Our huff_trees request at least 2 elements */
+ new_huff_el=huff_tree->element_buffer+(found++);
+ new_huff_el->count=0;
+ new_huff_el->a.leaf.null=0;
+ if (last)
+ new_huff_el->a.leaf.element_nr=huff_tree->min_chr=last-1;
+ else
+ new_huff_el->a.leaf.element_nr=huff_tree->max_chr=last+1;
+ queue.root[found]=(byte*) new_huff_el;
+ }
+ }
+ queue.elements=found;
+
+ for (i=found/2 ; i > 0 ; i--)
+ _downheap(&queue,i);
+ bytes_packed=0; bits_packed=0;
+ for (i=1 ; i < found ; i++)
+ {
+ a=(HUFF_ELEMENT*) queue_remove(&queue,0);
+ b=(HUFF_ELEMENT*) queue.root[1];
+ new_huff_el=huff_tree->element_buffer+found+i;
+ new_huff_el->count=a->count+b->count;
+ bits_packed+=(uint) (new_huff_el->count & 7);
+ bytes_packed+=new_huff_el->count/8;
+ new_huff_el->a.nod.left=a; /* lesser in left */
+ new_huff_el->a.nod.right=b;
+ queue.root[1]=(byte*) new_huff_el;
+ queue_replaced(&queue);
+ }
+ huff_tree->root=(HUFF_ELEMENT*) queue.root[1];
+ huff_tree->bytes_packed=bytes_packed+(bits_packed+7)/8;
+ return 0;
+}
+
+static int compare_tree(void* cmp_arg __attribute__((unused)),
+ register const uchar *s, register const uchar *t)
+{
+ uint length;
+ for (length=global_count->field_length; length-- ;)
+ if (*s++ != *t++)
+ return (int) s[-1] - (int) t[-1];
+ return 0;
+}
+
+ /* Used by make_huff_tree to save intervall-counts in queue */
+
+static int save_counts_in_queue(byte *key, element_count count,
+ HUFF_TREE *tree)
+{
+ HUFF_ELEMENT *new_huff_el;
+
+ new_huff_el=tree->element_buffer+(tree->elements++);
+ new_huff_el->count=count;
+ new_huff_el->a.leaf.null=0;
+ new_huff_el->a.leaf.element_nr= (uint) (key- tree->counts->tree_buff) /
+ tree->counts->field_length;
+ queue.root[tree->elements]=(byte*) new_huff_el;
+ return 0;
+}
+
+
+ /* Calculate length of file if given counts should be used */
+ /* Its actually a faster version of make_huff_tree */
+
+static my_off_t calc_packed_length(HUFF_COUNTS *huff_counts,
+ uint add_tree_lenght)
+{
+ uint i,found,bits_packed,first,last;
+ my_off_t bytes_packed;
+ HUFF_ELEMENT element_buffer[256];
+ DBUG_ENTER("calc_packed_length");
+
+ first=last=0;
+ for (i=found=0 ; i < 256 ; i++)
+ {
+ if (huff_counts->counts[i])
+ {
+ if (! found++)
+ first=i;
+ last=i;
+ queue.root[found]=(byte*) &huff_counts->counts[i];
+ }
+ }
+ if (!found)
+ DBUG_RETURN(0); /* Empty tree */
+ if (found < 2)
+ queue.root[++found]=(byte*) &huff_counts->counts[last ? 0 : 1];
+
+ queue.elements=found;
+
+ bytes_packed=0; bits_packed=0;
+ if (add_tree_lenght)
+ bytes_packed=(8+9+5+5+(max_bit(last-first)+1)*found+
+ (max_bit(found-1)+1+1)*(found-2) +7)/8;
+ for (i=(found+1)/2 ; i > 0 ; i--)
+ _downheap(&queue,i);
+ for (i=0 ; i < found-1 ; i++)
+ {
+ HUFF_ELEMENT *a,*b,*new_huff_el;
+ a=(HUFF_ELEMENT*) queue_remove(&queue,0);
+ b=(HUFF_ELEMENT*) queue.root[1];
+ new_huff_el=element_buffer+i;
+ new_huff_el->count=a->count+b->count;
+ bits_packed+=(uint) (new_huff_el->count & 7);
+ bytes_packed+=new_huff_el->count/8;
+ queue.root[1]=(byte*) new_huff_el;
+ queue_replaced(&queue);
+ }
+ DBUG_RETURN(bytes_packed+(bits_packed+7)/8);
+}
+
+
+ /* Remove trees that don't give any compression */
+
+static uint join_same_trees(HUFF_COUNTS *huff_counts, uint trees)
+{
+ uint k,tree_number;
+ HUFF_COUNTS count,*i,*j,*last_count;
+
+ last_count=huff_counts+trees;
+ for (tree_number=0, i=huff_counts ; i < last_count ; i++)
+ {
+ if (!i->tree->tree_number)
+ {
+ i->tree->tree_number= ++tree_number;
+ if (i->tree_buff)
+ continue; /* Don't join intervall */
+ for (j=i+1 ; j < last_count ; j++)
+ {
+ if (! j->tree->tree_number && ! j->tree_buff)
+ {
+ for (k=0 ; k < 256 ; k++)
+ count.counts[k]=i->counts[k]+j->counts[k];
+ if (calc_packed_length(&count,1) <=
+ i->tree->bytes_packed + j->tree->bytes_packed+
+ i->tree->tree_pack_length+j->tree->tree_pack_length+
+ ALLOWED_JOIN_DIFF)
+ {
+ memcpy_fixed((byte*) i->counts,(byte*) count.counts,
+ sizeof(count.counts[0])*256);
+ my_free((gptr) j->tree->element_buffer,MYF(0));
+ j->tree->element_buffer=0;
+ j->tree=i->tree;
+ bmove((byte*) i->counts,(byte*) count.counts,
+ sizeof(count.counts[0])*256);
+ if (make_huff_tree(i->tree,i))
+ return (uint) -1;
+ }
+ }
+ }
+ }
+ }
+ if (verbose)
+ printf("Original trees: %d After join: %d\n",trees,tree_number);
+ return tree_number; /* Return trees left */
+}
+
+
+ /* Fill in huff_tree decode tables */
+
+static int make_huff_decode_table(HUFF_TREE *huff_tree, uint trees)
+{
+ uint elements;
+ for ( ; trees-- ; huff_tree++)
+ {
+ if (huff_tree->tree_number > 0)
+ {
+ elements=huff_tree->counts->tree_buff ? huff_tree->elements : 256;
+ if (!(huff_tree->code =
+ (ulong*) my_malloc(elements*
+ (sizeof(ulong)+sizeof(uchar)),
+ MYF(MY_WME | MY_ZEROFILL))))
+ return 1;
+ huff_tree->code_len=(uchar*) (huff_tree->code+elements);
+ make_traverse_code_tree(huff_tree,huff_tree->root,32,0);
+ }
+ }
+ return 0;
+}
+
+
+static void make_traverse_code_tree(HUFF_TREE *huff_tree,
+ HUFF_ELEMENT *element,
+ uint size, ulong code)
+{
+ uint chr;
+ if (!element->a.leaf.null)
+ {
+ chr=element->a.leaf.element_nr;
+ huff_tree->code_len[chr]=(uchar) (32-size);
+ huff_tree->code[chr]= (code >> size);
+ if (huff_tree->height < 32-size)
+ huff_tree->height= 32-size;
+ }
+ else
+ {
+ size--;
+ make_traverse_code_tree(huff_tree,element->a.nod.left,size,code);
+ make_traverse_code_tree(huff_tree,element->a.nod.right,size,
+ code+((ulong) 1L << size));
+ }
+ return;
+}
+
+
+ /* Write header to new packed data file */
+
+static int write_header(PACK_MRG_INFO *mrg,uint head_length,uint trees,
+ my_off_t tot_elements,my_off_t filelength)
+{
+ byte *buff=file_buffer.pos;
+
+ bzero(buff,HEAD_LENGTH);
+ memcpy_fixed(buff,myisam_pack_file_magic,4);
+ int4store(buff+4,head_length);
+ int4store(buff+8, mrg->min_pack_length);
+ int4store(buff+12,mrg->max_pack_length);
+ int4store(buff+16,tot_elements);
+ int4store(buff+20,intervall_length);
+ int2store(buff+24,trees);
+ buff[26]=(char) mrg->ref_length;
+ /* Save record pointer length */
+ buff[27]= (uchar) mi_get_pointer_length((ulonglong) filelength,2);
+ if (test_only)
+ return 0;
+ VOID(my_seek(file_buffer.file,0L,MY_SEEK_SET,MYF(0)));
+ return my_write(file_buffer.file,file_buffer.pos,HEAD_LENGTH,
+ MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)) != 0;
+}
+
+ /* Write fieldinfo to new packed file */
+
+static void write_field_info(HUFF_COUNTS *counts, uint fields, uint trees)
+{
+ reg1 uint i;
+ uint huff_tree_bits;
+ huff_tree_bits=max_bit(trees ? trees-1 : 0);
+
+ for (i=0 ; i++ < fields ; counts++)
+ {
+ write_bits((ulong) (int) counts->field_type,5);
+ write_bits(counts->pack_type,6);
+ if (counts->pack_type & PACK_TYPE_ZERO_FILL)
+ write_bits(counts->max_zero_fill,5);
+ else
+ write_bits(counts->length_bits,5);
+ write_bits((ulong) counts->tree->tree_number-1,huff_tree_bits);
+ }
+ flush_bits();
+ return;
+}
+
+ /* Write all huff_trees to new datafile. Return tot count of
+ elements in all trees
+ Returns 0 on error */
+
+static my_off_t write_huff_tree(HUFF_TREE *huff_tree, uint trees)
+{
+ uint i,int_length;
+ uint *packed_tree,*offset,length;
+ my_off_t elements;
+
+ for (i=length=0 ; i < trees ; i++)
+ if (huff_tree[i].tree_number > 0 && huff_tree[i].elements > length)
+ length=huff_tree[i].elements;
+ if (!(packed_tree=(uint*) my_alloca(sizeof(uint)*length*2)))
+ {
+ my_error(EE_OUTOFMEMORY,MYF(ME_BELL),sizeof(uint)*length*2);
+ return 0;
+ }
+
+ intervall_length=0;
+ for (elements=0; trees-- ; huff_tree++)
+ {
+ if (huff_tree->tree_number == 0)
+ continue; /* Deleted tree */
+ elements+=huff_tree->elements;
+ huff_tree->max_offset=2;
+ if (huff_tree->elements <= 1)
+ offset=packed_tree;
+ else
+ offset=make_offset_code_tree(huff_tree,huff_tree->root,packed_tree);
+ huff_tree->offset_bits=max_bit(huff_tree->max_offset);
+ if (huff_tree->max_offset >= IS_OFFSET)
+ { /* This should be impossible */
+ VOID(fprintf(stderr,"Tree offset got too big: %d, aborted\n",
+ huff_tree->max_offset));
+ my_afree((gptr) packed_tree);
+ return 0;
+ }
+
+#ifdef EXTRA_DBUG
+ printf("pos: %d elements: %d tree-elements: %d char_bits: %d\n",
+ (uint) (file_buffer.pos-file_buffer.buffer),
+ huff_tree->elements, (offset-packed_tree),huff_tree->char_bits);
+#endif
+ if (!huff_tree->counts->tree_buff)
+ {
+ write_bits(0,1);
+ write_bits(huff_tree->min_chr,8);
+ write_bits(huff_tree->elements,9);
+ write_bits(huff_tree->char_bits,5);
+ write_bits(huff_tree->offset_bits,5);
+ int_length=0;
+ }
+ else
+ {
+ int_length=(uint) (huff_tree->counts->tree_pos -
+ huff_tree->counts->tree_buff);
+ write_bits(1,1);
+ write_bits(huff_tree->elements,15);
+ write_bits(int_length,16);
+ write_bits(huff_tree->char_bits,5);
+ write_bits(huff_tree->offset_bits,5);
+ intervall_length+=int_length;
+ }
+ length=(uint) (offset-packed_tree);
+ if (length != huff_tree->elements*2-2)
+ printf("error: Huff-tree-length: %d != calc_length: %d\n",
+ length,huff_tree->elements*2-2);
+
+ for (i=0 ; i < length ; i++)
+ {
+ if (packed_tree[i] & IS_OFFSET)
+ write_bits(packed_tree[i] - IS_OFFSET+ (1 << huff_tree->offset_bits),
+ huff_tree->offset_bits+1);
+ else
+ write_bits(packed_tree[i]-huff_tree->min_chr,huff_tree->char_bits+1);
+ }
+ flush_bits();
+ if (huff_tree->counts->tree_buff)
+ {
+ for (i=0 ; i < int_length ; i++)
+ write_bits((uint) (uchar) huff_tree->counts->tree_buff[i],8);
+ }
+ flush_bits();
+ }
+ my_afree((gptr) packed_tree);
+ return elements;
+}
+
+
+static uint *make_offset_code_tree(HUFF_TREE *huff_tree, HUFF_ELEMENT *element,
+ uint *offset)
+{
+ uint *prev_offset;
+
+ prev_offset= offset;
+ if (!element->a.nod.left->a.leaf.null)
+ {
+ offset[0] =(uint) element->a.nod.left->a.leaf.element_nr;
+ offset+=2;
+ }
+ else
+ {
+ prev_offset[0]= IS_OFFSET+2;
+ offset=make_offset_code_tree(huff_tree,element->a.nod.left,offset+2);
+ }
+ if (!element->a.nod.right->a.leaf.null)
+ {
+ prev_offset[1]=element->a.nod.right->a.leaf.element_nr;
+ return offset;
+ }
+ else
+ {
+ uint temp=(uint) (offset-prev_offset-1);
+ prev_offset[1]= IS_OFFSET+ temp;
+ if (huff_tree->max_offset < temp)
+ huff_tree->max_offset = temp;
+ return make_offset_code_tree(huff_tree,element->a.nod.right,offset);
+ }
+}
+
+ /* Get number of bits neaded to represent value */
+
+static uint max_bit(register uint value)
+{
+ reg2 uint power=1;
+
+ while ((value>>=1))
+ power++;
+ return (power);
+}
+
+
+static int compress_isam_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts)
+{
+ int error;
+ uint i,max_calc_length,pack_ref_length,min_record_length,max_record_length,
+ intervall,field_length,max_pack_length,pack_blob_length;
+ my_off_t record_count;
+ ulong length,pack_length;
+ byte *record,*pos,*end_pos,*record_pos,*start_pos;
+ HUFF_COUNTS *count,*end_count;
+ HUFF_TREE *tree;
+ MI_INFO *isam_file=mrg->file[0];
+ DBUG_ENTER("compress_isam_file");
+
+ if (!(record=(byte*) my_alloca(isam_file->s->base.reclength)))
+ return -1;
+ end_count=huff_counts+isam_file->s->base.fields;
+ min_record_length= (uint) ~0;
+ max_record_length=0;
+
+ for (i=max_calc_length=0 ; i < isam_file->s->base.fields ; i++)
+ {
+ if (!(huff_counts[i].pack_type & PACK_TYPE_ZERO_FILL))
+ huff_counts[i].max_zero_fill=0;
+ if (huff_counts[i].field_type == FIELD_CONSTANT ||
+ huff_counts[i].field_type == FIELD_ZERO ||
+ huff_counts[i].field_type == FIELD_CHECK)
+ continue;
+ if (huff_counts[i].field_type == FIELD_INTERVALL)
+ max_calc_length+=huff_counts[i].tree->height;
+ else if (huff_counts[i].field_type == FIELD_BLOB ||
+ huff_counts[i].field_type == FIELD_VARCHAR)
+ max_calc_length+=huff_counts[i].tree->height*huff_counts[i].max_length + huff_counts[i].length_bits +1;
+ else
+ max_calc_length+=
+ (huff_counts[i].field_length - huff_counts[i].max_zero_fill)*
+ huff_counts[i].tree->height+huff_counts[i].length_bits;
+ }
+ max_calc_length/=8;
+ if (max_calc_length < 254)
+ pack_ref_length=1;
+ else if (max_calc_length <= 65535)
+ pack_ref_length=3;
+ else
+ pack_ref_length=4;
+ record_count=0;
+ pack_blob_length=0;
+ if (isam_file->s->base.blobs)
+ {
+ if (mrg->max_blob_length < 254)
+ pack_blob_length=1;
+ else if (mrg->max_blob_length <= 65535)
+ pack_blob_length=3;
+ else
+ pack_blob_length=4;
+ }
+ max_pack_length=pack_ref_length+pack_blob_length;
+
+ mrg_reset(mrg);
+ while ((error=mrg_rrnd(mrg,record)) != HA_ERR_END_OF_FILE)
+ {
+ ulong tot_blob_length=0;
+ if (! error)
+ {
+ if (flush_buffer(max_calc_length+max_pack_length))
+ break;
+ record_pos=file_buffer.pos;
+ file_buffer.pos+=max_pack_length;
+ for (start_pos=record, count= huff_counts; count < end_count ; count++)
+ {
+ end_pos=start_pos+(field_length=count->field_length);
+ tree=count->tree;
+
+ if (count->pack_type & PACK_TYPE_SPACE_FIELDS)
+ {
+ for (pos=start_pos ; *pos == ' ' && pos < end_pos; pos++) ;
+ if (pos == end_pos)
+ {
+ write_bits(1,1);
+ start_pos=end_pos;
+ continue;
+ }
+ write_bits(0,1);
+ }
+ end_pos-=count->max_zero_fill;
+ field_length-=count->max_zero_fill;
+
+ switch(count->field_type) {
+ case FIELD_SKIP_ZERO:
+ if (!memcmp((byte*) start_pos,zero_string,field_length))
+ {
+ write_bits(1,1);
+ start_pos=end_pos;
+ break;
+ }
+ write_bits(0,1);
+ /* Fall through */
+ case FIELD_NORMAL:
+ for ( ; start_pos < end_pos ; start_pos++)
+ write_bits(tree->code[(uchar) *start_pos],
+ (uint) tree->code_len[(uchar) *start_pos]);
+ break;
+ case FIELD_SKIP_ENDSPACE:
+ for (pos=end_pos ; pos > start_pos && pos[-1] == ' ' ; pos--) ;
+ length=(uint) (end_pos-pos);
+ if (count->pack_type & PACK_TYPE_SELECTED)
+ {
+ if (length > count->min_space)
+ {
+ write_bits(1,1);
+ write_bits(length,count->length_bits);
+ }
+ else
+ {
+ write_bits(0,1);
+ pos=end_pos;
+ }
+ }
+ else
+ write_bits(length,count->length_bits);
+ for ( ; start_pos < pos ; start_pos++)
+ write_bits(tree->code[(uchar) *start_pos],
+ (uint) tree->code_len[(uchar) *start_pos]);
+ start_pos=end_pos;
+ break;
+ case FIELD_SKIP_PRESPACE:
+ for (pos=start_pos ; pos < end_pos && pos[0] == ' ' ; pos++) ;
+ length=(uint) (pos-start_pos);
+ if (count->pack_type & PACK_TYPE_SELECTED)
+ {
+ if (length > count->min_space)
+ {
+ write_bits(1,1);
+ write_bits(length,count->length_bits);
+ }
+ else
+ {
+ pos=start_pos;
+ write_bits(0,1);
+ }
+ }
+ else
+ write_bits(length,count->length_bits);
+ for (start_pos=pos ; start_pos < end_pos ; start_pos++)
+ write_bits(tree->code[(uchar) *start_pos],
+ (uint) tree->code_len[(uchar) *start_pos]);
+ break;
+ case FIELD_CONSTANT:
+ case FIELD_ZERO:
+ case FIELD_CHECK:
+ start_pos=end_pos;
+ break;
+ case FIELD_INTERVALL:
+ global_count=count;
+ pos=(byte*) tree_search(&count->int_tree, start_pos,
+ count->int_tree.custom_arg);
+ intervall=(uint) (pos - count->tree_buff)/field_length;
+ write_bits(tree->code[intervall],(uint) tree->code_len[intervall]);
+ start_pos=end_pos;
+ break;
+ case FIELD_BLOB:
+ {
+ ulong blob_length=_mi_calc_blob_length(field_length-
+ mi_portable_sizeof_char_ptr,
+ start_pos);
+ if (!blob_length)
+ {
+ write_bits(1,1); /* Empty blob */
+ }
+ else
+ {
+ byte *blob,*blob_end;
+ write_bits(0,1);
+ write_bits(blob_length,count->length_bits);
+ memcpy_fixed(&blob,end_pos-mi_portable_sizeof_char_ptr,
+ sizeof(char*));
+ blob_end=blob+blob_length;
+ for ( ; blob < blob_end ; blob++)
+ write_bits(tree->code[(uchar) *blob],
+ (uint) tree->code_len[(uchar) *blob]);
+ tot_blob_length+=blob_length;
+ }
+ start_pos= end_pos;
+ break;
+ }
+ case FIELD_VARCHAR:
+ {
+ uint pack_length= HA_VARCHAR_PACKLENGTH(count->field_length-1);
+ ulong col_length= (pack_length == 1 ? (uint) *(uchar*) start_pos :
+ uint2korr(start_pos));
+ if (!col_length)
+ {
+ write_bits(1,1); /* Empty varchar */
+ }
+ else
+ {
+ byte *end=start_pos+pack_length+col_length;
+ write_bits(0,1);
+ write_bits(col_length,count->length_bits);
+ for (start_pos+=pack_length ; start_pos < end ; start_pos++)
+ write_bits(tree->code[(uchar) *start_pos],
+ (uint) tree->code_len[(uchar) *start_pos]);
+ }
+ start_pos= end_pos;
+ break;
+ }
+ case FIELD_LAST:
+ abort(); /* Impossible */
+ }
+ start_pos+=count->max_zero_fill;
+ }
+ flush_bits();
+ length=(ulong) (file_buffer.pos-record_pos)-max_pack_length;
+ pack_length=save_pack_length(record_pos,length);
+ if (pack_blob_length)
+ pack_length+=save_pack_length(record_pos+pack_length,tot_blob_length);
+
+ /* Correct file buffer if the header was smaller */
+ if (pack_length != max_pack_length)
+ {
+ bmove(record_pos+pack_length,record_pos+max_pack_length,length);
+ file_buffer.pos-= (max_pack_length-pack_length);
+ }
+ if (length < (ulong) min_record_length)
+ min_record_length=(uint) length;
+ if (length > (ulong) max_record_length)
+ max_record_length=(uint) length;
+ if (write_loop && ++record_count % WRITE_COUNT == 0)
+ {
+ printf("%lu\r",(ulong) record_count); VOID(fflush(stdout));
+ }
+ }
+ else if (error != HA_ERR_RECORD_DELETED)
+ break;
+ }
+ if (error == HA_ERR_END_OF_FILE)
+ error=0;
+ else
+ {
+ fprintf(stderr,"%s: Got error %d reading records\n",my_progname,error);
+ }
+
+ my_afree((gptr) record);
+ mrg->ref_length=max_pack_length;
+ mrg->min_pack_length=max_record_length ? min_record_length : 0;
+ mrg->max_pack_length=max_record_length;
+ DBUG_RETURN(error || error_on_write || flush_buffer(~(ulong) 0));
+}
+
+
+static char *make_new_name(char *new_name, char *old_name)
+{
+ return fn_format(new_name,old_name,"",DATA_TMP_EXT,2+4);
+}
+
+static char *make_old_name(char *new_name, char *old_name)
+{
+ return fn_format(new_name,old_name,"",OLD_EXT,2+4);
+}
+
+ /* rutines for bit writing buffer */
+
+static void init_file_buffer(File file, pbool read_buffer)
+{
+ file_buffer.file=file;
+ file_buffer.buffer=my_malloc(ALIGN_SIZE(RECORD_CACHE_SIZE),MYF(MY_WME));
+ file_buffer.end=file_buffer.buffer+ALIGN_SIZE(RECORD_CACHE_SIZE)-8;
+ file_buffer.pos_in_file=0;
+ error_on_write=0;
+ if (read_buffer)
+ {
+
+ file_buffer.pos=file_buffer.end;
+ file_buffer.bits=0;
+ }
+ else
+ {
+ file_buffer.pos=file_buffer.buffer;
+ file_buffer.bits=BITS_SAVED;
+ }
+ file_buffer.current_byte=0;
+}
+
+
+static int flush_buffer(ulong neaded_length)
+{
+ ulong length;
+ if ((ulong) (file_buffer.end - file_buffer.pos) > neaded_length)
+ return 0;
+ length=(ulong) (file_buffer.pos-file_buffer.buffer);
+ file_buffer.pos=file_buffer.buffer;
+ file_buffer.pos_in_file+=length;
+ if (test_only)
+ return 0;
+ if (error_on_write|| my_write(file_buffer.file,file_buffer.buffer,
+ length,
+ MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
+ {
+ error_on_write=1;
+ return 1;
+ }
+
+ if (neaded_length != ~(ulong) 0 &&
+ (ulong) (file_buffer.end-file_buffer.buffer) < neaded_length)
+ {
+ char *tmp;
+ neaded_length+=256; /* some margin */
+ tmp=my_realloc(file_buffer.buffer, neaded_length,MYF(MY_WME));
+ if (!tmp)
+ return 1;
+ file_buffer.pos= tmp + (ulong) (file_buffer.pos - file_buffer.buffer);
+ file_buffer.buffer=tmp;
+ file_buffer.end=tmp+neaded_length-8;
+ }
+ return 0;
+}
+
+
+static void end_file_buffer(void)
+{
+ my_free((gptr) file_buffer.buffer,MYF(0));
+}
+
+ /* output `bits` low bits of `value' */
+
+static void write_bits (register ulong value, register uint bits)
+{
+ if ((file_buffer.bits-=(int) bits) >= 0)
+ {
+ file_buffer.current_byte|=value << file_buffer.bits;
+ }
+ else
+ {
+ reg3 uint byte_buff;
+ bits= (uint) -file_buffer.bits;
+ byte_buff=file_buffer.current_byte | (uint) (value >> bits);
+#if BITS_SAVED == 32
+ *file_buffer.pos++= (byte) (byte_buff >> 24) ;
+ *file_buffer.pos++= (byte) (byte_buff >> 16) ;
+#endif
+ *file_buffer.pos++= (byte) (byte_buff >> 8) ;
+ *file_buffer.pos++= (byte) byte_buff;
+
+ value&=(1 << bits)-1;
+#if BITS_SAVED == 16
+ if (bits >= sizeof(uint))
+ {
+ bits-=8;
+ *file_buffer.pos++= (uchar) (value >> bits);
+ value&= (1 << bits)-1;
+ if (bits >= sizeof(uint))
+ {
+ bits-=8;
+ *file_buffer.pos++= (uchar) (value >> bits);
+ value&= (1 << bits)-1;
+ }
+ }
+#endif
+ if (file_buffer.pos >= file_buffer.end)
+ VOID(flush_buffer((uint) ~0));
+ file_buffer.bits=(int) (BITS_SAVED - bits);
+ file_buffer.current_byte=(uint) (value << (BITS_SAVED - bits));
+ }
+ return;
+}
+
+ /* Flush bits in bit_buffer to buffer */
+
+static void flush_bits (void)
+{
+ uint bits,byte_buff;
+
+ bits=(file_buffer.bits) & ~7;
+ byte_buff = file_buffer.current_byte >> bits;
+ bits=BITS_SAVED - bits;
+ while (bits > 0)
+ {
+ bits-=8;
+ *file_buffer.pos++= (byte) (uchar) (byte_buff >> bits) ;
+ }
+ file_buffer.bits=BITS_SAVED;
+ file_buffer.current_byte=0;
+ return;
+}
+
+
+/****************************************************************************
+** functions to handle the joined files
+****************************************************************************/
+
+static int save_state(MI_INFO *isam_file,PACK_MRG_INFO *mrg,my_off_t new_length,
+ ha_checksum crc)
+{
+ MYISAM_SHARE *share=isam_file->s;
+ uint options=mi_uint2korr(share->state.header.options);
+ uint key;
+ DBUG_ENTER("save_state");
+
+ options|= HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA;
+ mi_int2store(share->state.header.options,options);
+
+ share->state.state.data_file_length=new_length;
+ share->state.state.del=0;
+ share->state.state.empty=0;
+ share->state.dellink= HA_OFFSET_ERROR;
+ share->state.split=(ha_rows) mrg->records;
+ share->state.version=(ulong) time((time_t*) 0);
+ if (share->state.key_map != (ULL(1) << share->base.keys) - 1)
+ {
+ /*
+ Some indexes are disabled, cannot use current key_file_length value
+ as an estimate of upper bound of index file size. Use packed data file
+ size instead.
+ */
+ share->state.state.key_file_length= new_length;
+ }
+ /*
+ If there are no disabled indexes, keep key_file_length value from
+ original file so "myisamchk -rq" can use this value (this is necessary
+ because index size cannot be easily calculated for fulltext keys)
+ */
+ share->state.key_map=0;
+ for (key=0 ; key < share->base.keys ; key++)
+ share->state.key_root[key]= HA_OFFSET_ERROR;
+ for (key=0 ; key < share->state.header.max_block_size ; key++)
+ share->state.key_del[key]= HA_OFFSET_ERROR;
+ share->state.checksum=crc; /* Save crc here */
+ share->changed=1; /* Force write of header */
+ share->state.open_count=0;
+ share->global_changed=0;
+ VOID(my_chsize(share->kfile, share->base.keystart, 0, MYF(0)));
+ if (share->base.keys)
+ isamchk_neaded=1;
+ DBUG_RETURN(mi_state_info_write(share->kfile,&share->state,1+2));
+}
+
+
+static int save_state_mrg(File file,PACK_MRG_INFO *mrg,my_off_t new_length,
+ ha_checksum crc)
+{
+ MI_STATE_INFO state;
+ MI_INFO *isam_file=mrg->file[0];
+ uint options;
+ DBUG_ENTER("save_state_mrg");
+
+ state= isam_file->s->state;
+ options= (mi_uint2korr(state.header.options) | HA_OPTION_COMPRESS_RECORD |
+ HA_OPTION_READ_ONLY_DATA);
+ mi_int2store(state.header.options,options);
+ state.state.data_file_length=new_length;
+ state.state.del=0;
+ state.state.empty=0;
+ state.state.records=state.split=(ha_rows) mrg->records;
+ /* See comment above in save_state about key_file_length handling. */
+ if (mrg->src_file_has_indexes_disabled)
+ {
+ isam_file->s->state.state.key_file_length=
+ max(isam_file->s->state.state.key_file_length, new_length);
+ }
+ state.dellink= HA_OFFSET_ERROR;
+ state.version=(ulong) time((time_t*) 0);
+ state.key_map=0;
+ state.checksum=crc;
+ if (isam_file->s->base.keys)
+ isamchk_neaded=1;
+ state.changed=STATE_CHANGED | STATE_NOT_ANALYZED; /* Force check of table */
+ DBUG_RETURN (mi_state_info_write(file,&state,1+2));
+}
+
+
+/* reset for mrg_rrnd */
+
+static void mrg_reset(PACK_MRG_INFO *mrg)
+{
+ if (mrg->current)
+ {
+ mi_extra(*mrg->current, HA_EXTRA_NO_CACHE, 0);
+ mrg->current=0;
+ }
+}
+
+static int mrg_rrnd(PACK_MRG_INFO *info,byte *buf)
+{
+ int error;
+ MI_INFO *isam_info;
+ my_off_t filepos;
+
+ if (!info->current)
+ {
+ isam_info= *(info->current=info->file);
+ info->end=info->current+info->count;
+ mi_extra(isam_info, HA_EXTRA_RESET, 0);
+ mi_extra(isam_info, HA_EXTRA_CACHE, 0);
+ filepos=isam_info->s->pack.header_length;
+ }
+ else
+ {
+ isam_info= *info->current;
+ filepos= isam_info->nextpos;
+ }
+
+ for (;;)
+ {
+ isam_info->update&= HA_STATE_CHANGED;
+ if (!(error=(*isam_info->s->read_rnd)(isam_info,(byte*) buf,
+ filepos, 1)) ||
+ error != HA_ERR_END_OF_FILE)
+ return (error);
+ mi_extra(isam_info,HA_EXTRA_NO_CACHE, 0);
+ if (info->current+1 == info->end)
+ return(HA_ERR_END_OF_FILE);
+ info->current++;
+ isam_info= *info->current;
+ filepos=isam_info->s->pack.header_length;
+ mi_extra(isam_info,HA_EXTRA_RESET, 0);
+ mi_extra(isam_info,HA_EXTRA_CACHE, 0);
+ }
+}
+
+
+static int mrg_close(PACK_MRG_INFO *mrg)
+{
+ uint i;
+ int error=0;
+ for (i=0 ; i < mrg->count ; i++)
+ error|=mi_close(mrg->file[i]);
+ if (mrg->free_file)
+ my_free((gptr) mrg->file,MYF(0));
+ return error;
+}
diff --git a/storage/myisam/rt_index.c b/storage/myisam/rt_index.c
new file mode 100644
index 00000000000..97554dca4e6
--- /dev/null
+++ b/storage/myisam/rt_index.c
@@ -0,0 +1,1082 @@
+/* Copyright (C) 2000 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 "myisamdef.h"
+
+#ifdef HAVE_RTREE_KEYS
+
+#include "rt_index.h"
+#include "rt_key.h"
+#include "rt_mbr.h"
+
+#define REINSERT_BUFFER_INC 10
+#define PICK_BY_AREA
+/*#define PICK_BY_PERIMETER*/
+
+typedef struct st_page_level
+{
+ uint level;
+ my_off_t offs;
+} stPageLevel;
+
+typedef struct st_page_list
+{
+ ulong n_pages;
+ ulong m_pages;
+ stPageLevel *pages;
+} stPageList;
+
+
+/*
+ Find next key in r-tree according to search_flag recursively
+
+ NOTES
+ Used in rtree_find_first() and rtree_find_next()
+
+ RETURN
+ -1 Error
+ 0 Found
+ 1 Not found
+*/
+
+static int rtree_find_req(MI_INFO *info, MI_KEYDEF *keyinfo, uint search_flag,
+ uint nod_cmp_flag, my_off_t page, int level)
+{
+ uchar *k;
+ uchar *last;
+ uint nod_flag;
+ int res;
+ uchar *page_buf;
+ int k_len;
+ uint *saved_key = (uint*) (info->rtree_recursion_state) + level;
+
+ if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
+ {
+ my_errno = HA_ERR_OUT_OF_MEM;
+ return -1;
+ }
+ if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
+ goto err1;
+ nod_flag = mi_test_if_nod(page_buf);
+
+ k_len = keyinfo->keylength - info->s->base.rec_reflength;
+
+ if(info->rtree_recursion_depth >= level)
+ {
+ k = page_buf + *saved_key;
+ }
+ else
+ {
+ k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
+ }
+ last = rt_PAGE_END(page_buf);
+
+ for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag))
+ {
+ if (nod_flag)
+ {
+ /* this is an internal node in the tree */
+ if (!(res = rtree_key_cmp(keyinfo->seg, info->first_mbr_key, k,
+ info->last_rkey_length, nod_cmp_flag)))
+ {
+ switch ((res = rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag,
+ _mi_kpos(nod_flag, k), level + 1)))
+ {
+ case 0: /* found - exit from recursion */
+ *saved_key = k - page_buf;
+ goto ok;
+ case 1: /* not found - continue searching */
+ info->rtree_recursion_depth = level;
+ break;
+ default: /* error */
+ case -1:
+ goto err1;
+ }
+ }
+ }
+ else
+ {
+ /* this is a leaf */
+ if (!rtree_key_cmp(keyinfo->seg, info->first_mbr_key, k,
+ info->last_rkey_length, search_flag))
+ {
+ uchar *after_key = rt_PAGE_NEXT_KEY(k, k_len, nod_flag);
+ info->lastpos = _mi_dpos(info, 0, after_key);
+ info->lastkey_length = k_len + info->s->base.rec_reflength;
+ memcpy(info->lastkey, k, info->lastkey_length);
+ info->rtree_recursion_depth = level;
+ *saved_key = last - page_buf;
+
+ if (after_key < last)
+ {
+ info->int_keypos = info->buff;
+ info->int_maxpos = info->buff + (last - after_key);
+ memcpy(info->buff, after_key, last - after_key);
+ info->buff_used = 0;
+ }
+ else
+ {
+ info->buff_used = 1;
+ }
+
+ res = 0;
+ goto ok;
+ }
+ }
+ }
+ info->lastpos = HA_OFFSET_ERROR;
+ my_errno = HA_ERR_KEY_NOT_FOUND;
+ res = 1;
+
+ok:
+ my_afree((byte*)page_buf);
+ return res;
+
+err1:
+ my_afree((byte*)page_buf);
+ info->lastpos = HA_OFFSET_ERROR;
+ return -1;
+}
+
+
+/*
+ Find first key in r-tree according to search_flag condition
+
+ SYNOPSIS
+ rtree_find_first()
+ info Handler to MyISAM file
+ uint keynr Key number to use
+ key Key to search for
+ key_length Length of 'key'
+ search_flag Bitmap of flags how to do the search
+
+ RETURN
+ -1 Error
+ 0 Found
+ 1 Not found
+*/
+
+int rtree_find_first(MI_INFO *info, uint keynr, uchar *key, uint key_length,
+ uint search_flag)
+{
+ my_off_t root;
+ uint nod_cmp_flag;
+ MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
+
+ if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ {
+ my_errno= HA_ERR_END_OF_FILE;
+ return -1;
+ }
+
+ /* Save searched key */
+ memcpy(info->first_mbr_key, key, keyinfo->keylength -
+ info->s->base.rec_reflength);
+ info->last_rkey_length = key_length;
+
+ info->rtree_recursion_depth = -1;
+ info->buff_used = 1;
+
+ nod_cmp_flag = ((search_flag & (MBR_EQUAL | MBR_WITHIN)) ?
+ MBR_WITHIN : MBR_INTERSECT);
+ return rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag, root, 0);
+}
+
+
+/*
+ Find next key in r-tree according to search_flag condition
+
+ SYNOPSIS
+ rtree_find_next()
+ info Handler to MyISAM file
+ uint keynr Key number to use
+ search_flag Bitmap of flags how to do the search
+
+ RETURN
+ -1 Error
+ 0 Found
+ 1 Not found
+*/
+
+int rtree_find_next(MI_INFO *info, uint keynr, uint search_flag)
+{
+ my_off_t root;
+ uint nod_cmp_flag;
+ MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
+
+ if (info->update & HA_STATE_DELETED)
+ return rtree_find_first(info, keynr, info->lastkey, info->lastkey_length,
+ search_flag);
+
+ if (!info->buff_used)
+ {
+ uchar *key= info->int_keypos;
+
+ while (key < info->int_maxpos)
+ {
+ if (!rtree_key_cmp(keyinfo->seg, info->first_mbr_key, key,
+ info->last_rkey_length, search_flag))
+ {
+ uchar *after_key = key + keyinfo->keylength;
+
+ info->lastpos= _mi_dpos(info, 0, after_key);
+ memcpy(info->lastkey, key, info->lastkey_length);
+
+ if (after_key < info->int_maxpos)
+ info->int_keypos= after_key;
+ else
+ info->buff_used= 1;
+ return 0;
+ }
+ key+= keyinfo->keylength;
+ }
+ }
+ if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ {
+ my_errno= HA_ERR_END_OF_FILE;
+ return -1;
+ }
+
+ nod_cmp_flag = ((search_flag & (MBR_EQUAL | MBR_WITHIN)) ?
+ MBR_WITHIN : MBR_INTERSECT);
+ return rtree_find_req(info, keyinfo, search_flag, nod_cmp_flag, root, 0);
+}
+
+
+/*
+ Get next key in r-tree recursively
+
+ NOTES
+ Used in rtree_get_first() and rtree_get_next()
+
+ RETURN
+ -1 Error
+ 0 Found
+ 1 Not found
+*/
+
+static int rtree_get_req(MI_INFO *info, MI_KEYDEF *keyinfo, uint key_length,
+ my_off_t page, int level)
+{
+ uchar *k;
+ uchar *last;
+ uint nod_flag;
+ int res;
+ uchar *page_buf;
+ uint k_len;
+ uint *saved_key = (uint*) (info->rtree_recursion_state) + level;
+
+ if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
+ return -1;
+ if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
+ goto err1;
+ nod_flag = mi_test_if_nod(page_buf);
+
+ k_len = keyinfo->keylength - info->s->base.rec_reflength;
+
+ if(info->rtree_recursion_depth >= level)
+ {
+ k = page_buf + *saved_key;
+ if (!nod_flag)
+ {
+ /* Only leaf pages contain data references. */
+ /* Need to check next key with data reference. */
+ k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag);
+ }
+ }
+ else
+ {
+ k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
+ }
+ last = rt_PAGE_END(page_buf);
+
+ for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag))
+ {
+ if (nod_flag)
+ {
+ /* this is an internal node in the tree */
+ switch ((res = rtree_get_req(info, keyinfo, key_length,
+ _mi_kpos(nod_flag, k), level + 1)))
+ {
+ case 0: /* found - exit from recursion */
+ *saved_key = k - page_buf;
+ goto ok;
+ case 1: /* not found - continue searching */
+ info->rtree_recursion_depth = level;
+ break;
+ default:
+ case -1: /* error */
+ goto err1;
+ }
+ }
+ else
+ {
+ /* this is a leaf */
+ uchar *after_key = rt_PAGE_NEXT_KEY(k, k_len, nod_flag);
+ info->lastpos = _mi_dpos(info, 0, after_key);
+ info->lastkey_length = k_len + info->s->base.rec_reflength;
+ memcpy(info->lastkey, k, info->lastkey_length);
+
+ info->rtree_recursion_depth = level;
+ *saved_key = k - page_buf;
+
+ if (after_key < last)
+ {
+ info->int_keypos = (uchar*)saved_key;
+ memcpy(info->buff, page_buf, keyinfo->block_length);
+ info->int_maxpos = rt_PAGE_END(info->buff);
+ info->buff_used = 0;
+ }
+ else
+ {
+ info->buff_used = 1;
+ }
+
+ res = 0;
+ goto ok;
+ }
+ }
+ info->lastpos = HA_OFFSET_ERROR;
+ my_errno = HA_ERR_KEY_NOT_FOUND;
+ res = 1;
+
+ok:
+ my_afree((byte*)page_buf);
+ return res;
+
+err1:
+ my_afree((byte*)page_buf);
+ info->lastpos = HA_OFFSET_ERROR;
+ return -1;
+}
+
+
+/*
+ Get first key in r-tree
+
+ RETURN
+ -1 Error
+ 0 Found
+ 1 Not found
+*/
+
+int rtree_get_first(MI_INFO *info, uint keynr, uint key_length)
+{
+ my_off_t root;
+ MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
+
+ if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ {
+ my_errno= HA_ERR_END_OF_FILE;
+ return -1;
+ }
+
+ info->rtree_recursion_depth = -1;
+ info->buff_used = 1;
+
+ return rtree_get_req(info, &keyinfo[keynr], key_length, root, 0);
+}
+
+
+/*
+ Get next key in r-tree
+
+ RETURN
+ -1 Error
+ 0 Found
+ 1 Not found
+*/
+
+int rtree_get_next(MI_INFO *info, uint keynr, uint key_length)
+{
+ my_off_t root;
+ MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
+
+ if (!info->buff_used)
+ {
+ uint k_len = keyinfo->keylength - info->s->base.rec_reflength;
+ /* rt_PAGE_NEXT_KEY(info->int_keypos) */
+ uchar *key = info->buff + *(int*)info->int_keypos + k_len +
+ info->s->base.rec_reflength;
+ /* rt_PAGE_NEXT_KEY(key) */
+ uchar *after_key = key + k_len + info->s->base.rec_reflength;
+
+ info->lastpos = _mi_dpos(info, 0, after_key);
+ info->lastkey_length = k_len + info->s->base.rec_reflength;
+ memcpy(info->lastkey, key, k_len + info->s->base.rec_reflength);
+
+ *(int*)info->int_keypos = key - info->buff;
+ if (after_key >= info->int_maxpos)
+ {
+ info->buff_used = 1;
+ }
+
+ return 0;
+ }
+ else
+ {
+ if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ {
+ my_errno= HA_ERR_END_OF_FILE;
+ return -1;
+ }
+
+ return rtree_get_req(info, &keyinfo[keynr], key_length, root, 0);
+ }
+}
+
+
+/*
+ Choose non-leaf better key for insertion
+*/
+
+#ifdef PICK_BY_PERIMETER
+static uchar *rtree_pick_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, uchar *page_buf, uint nod_flag)
+{
+ double increase;
+ double best_incr = DBL_MAX;
+ double perimeter;
+ double best_perimeter;
+ uchar *best_key;
+ uchar *k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
+ uchar *last = rt_PAGE_END(page_buf);
+
+ LINT_INIT(best_perimeter);
+ LINT_INIT(best_key);
+
+ for (; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag))
+ {
+ if ((increase = rtree_perimeter_increase(keyinfo->seg, k, key, key_length,
+ &perimeter)) == -1)
+ return NULL;
+ if ((increase < best_incr)||
+ (increase == best_incr && perimeter < best_perimeter))
+ {
+ best_key = k;
+ best_perimeter= perimeter;
+ best_incr = increase;
+ }
+ }
+ return best_key;
+}
+
+#endif /*PICK_BY_PERIMETER*/
+
+#ifdef PICK_BY_AREA
+static uchar *rtree_pick_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, uchar *page_buf, uint nod_flag)
+{
+ double increase;
+ double best_incr = DBL_MAX;
+ double area;
+ double best_area;
+ uchar *best_key;
+ uchar *k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
+ uchar *last = rt_PAGE_END(page_buf);
+
+ LINT_INIT(best_area);
+ LINT_INIT(best_key);
+
+ for (; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag))
+ {
+ /* The following is safe as -1.0 is an exact number */
+ if ((increase = rtree_area_increase(keyinfo->seg, k, key, key_length,
+ &area)) == -1.0)
+ return NULL;
+ /* The following should be safe, even if we compare doubles */
+ if (increase < best_incr)
+ {
+ best_key = k;
+ best_area = area;
+ best_incr = increase;
+ }
+ else
+ {
+ /* The following should be safe, even if we compare doubles */
+ if ((increase == best_incr) && (area < best_area))
+ {
+ best_key = k;
+ best_area = area;
+ best_incr = increase;
+ }
+ }
+ }
+ return best_key;
+}
+
+#endif /*PICK_BY_AREA*/
+
+/*
+ Go down and insert key into tree
+
+ RETURN
+ -1 Error
+ 0 Child was not split
+ 1 Child was split
+*/
+
+static int rtree_insert_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, my_off_t page, my_off_t *new_page,
+ int ins_level, int level)
+{
+ uchar *k;
+ uint nod_flag;
+ uchar *page_buf;
+ int res;
+
+ if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length +
+ MI_MAX_KEY_BUFF)))
+ {
+ my_errno = HA_ERR_OUT_OF_MEM;
+ return -1;
+ }
+ if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
+ goto err1;
+ nod_flag = mi_test_if_nod(page_buf);
+
+ if ((ins_level == -1 && nod_flag) || /* key: go down to leaf */
+ (ins_level > -1 && ins_level > level)) /* branch: go down to ins_level */
+ {
+ if ((k = rtree_pick_key(info, keyinfo, key, key_length, page_buf,
+ nod_flag)) == NULL)
+ goto err1;
+ switch ((res = rtree_insert_req(info, keyinfo, key, key_length,
+ _mi_kpos(nod_flag, k), new_page, ins_level, level + 1)))
+ {
+ case 0: /* child was not split */
+ {
+ rtree_combine_rect(keyinfo->seg, k, key, k, key_length);
+ if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
+ goto err1;
+ goto ok;
+ }
+ case 1: /* child was split */
+ {
+ uchar *new_key = page_buf + keyinfo->block_length + nod_flag;
+ /* set proper MBR for key */
+ if (rtree_set_key_mbr(info, keyinfo, k, key_length,
+ _mi_kpos(nod_flag, k)))
+ goto err1;
+ /* add new key for new page */
+ _mi_kpointer(info, new_key - nod_flag, *new_page);
+ if (rtree_set_key_mbr(info, keyinfo, new_key, key_length, *new_page))
+ goto err1;
+ res = rtree_add_key(info, keyinfo, new_key, key_length,
+ page_buf, new_page);
+ if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
+ goto err1;
+ goto ok;
+ }
+ default:
+ case -1: /* error */
+ {
+ goto err1;
+ }
+ }
+ }
+ else
+ {
+ res = rtree_add_key(info, keyinfo, key, key_length, page_buf, new_page);
+ if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
+ goto err1;
+ goto ok;
+ }
+
+ok:
+ my_afree((byte*)page_buf);
+ return res;
+
+err1:
+ my_afree((byte*)page_buf);
+ return -1;
+}
+
+
+/*
+ Insert key into the tree
+
+ RETURN
+ -1 Error
+ 0 Root was not split
+ 1 Root was split
+*/
+
+static int rtree_insert_level(MI_INFO *info, uint keynr, uchar *key,
+ uint key_length, int ins_level)
+{
+ my_off_t old_root;
+ MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
+ int res;
+ my_off_t new_page;
+
+ if ((old_root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ {
+ int res;
+
+ if ((old_root = _mi_new(info, keyinfo, DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
+ return -1;
+ info->buff_used = 1;
+ mi_putint(info->buff, 2, 0);
+ res = rtree_add_key(info, keyinfo, key, key_length, info->buff, NULL);
+ if (_mi_write_keypage(info, keyinfo, old_root, DFLT_INIT_HITS, info->buff))
+ return 1;
+ info->s->state.key_root[keynr] = old_root;
+ return res;
+ }
+
+ switch ((res = rtree_insert_req(info, keyinfo, key, key_length,
+ old_root, &new_page, ins_level, 0)))
+ {
+ case 0: /* root was not split */
+ {
+ break;
+ }
+ case 1: /* root was split, grow a new root */
+ {
+ uchar *new_root_buf;
+ my_off_t new_root;
+ uchar *new_key;
+ uint nod_flag = info->s->base.key_reflength;
+
+ if (!(new_root_buf = (uchar*)my_alloca((uint)keyinfo->block_length +
+ MI_MAX_KEY_BUFF)))
+ {
+ my_errno = HA_ERR_OUT_OF_MEM;
+ return -1;
+ }
+
+ mi_putint(new_root_buf, 2, nod_flag);
+ if ((new_root = _mi_new(info, keyinfo, DFLT_INIT_HITS)) ==
+ HA_OFFSET_ERROR)
+ goto err1;
+
+ new_key = new_root_buf + keyinfo->block_length + nod_flag;
+
+ _mi_kpointer(info, new_key - nod_flag, old_root);
+ if (rtree_set_key_mbr(info, keyinfo, new_key, key_length, old_root))
+ goto err1;
+ if (rtree_add_key(info, keyinfo, new_key, key_length, new_root_buf, NULL)
+ == -1)
+ goto err1;
+ _mi_kpointer(info, new_key - nod_flag, new_page);
+ if (rtree_set_key_mbr(info, keyinfo, new_key, key_length, new_page))
+ goto err1;
+ if (rtree_add_key(info, keyinfo, new_key, key_length, new_root_buf, NULL)
+ == -1)
+ goto err1;
+ if (_mi_write_keypage(info, keyinfo, new_root,
+ DFLT_INIT_HITS, new_root_buf))
+ goto err1;
+ info->s->state.key_root[keynr] = new_root;
+
+ my_afree((byte*)new_root_buf);
+ break;
+err1:
+ my_afree((byte*)new_root_buf);
+ return -1;
+ }
+ default:
+ case -1: /* error */
+ {
+ break;
+ }
+ }
+ return res;
+}
+
+
+/*
+ Insert key into the tree - interface function
+
+ RETURN
+ -1 Error
+ 0 OK
+*/
+
+int rtree_insert(MI_INFO *info, uint keynr, uchar *key, uint key_length)
+{
+ return (!key_length ||
+ (rtree_insert_level(info, keynr, key, key_length, -1) == -1)) ? -1 : 0;
+}
+
+
+/*
+ Fill reinsert page buffer
+
+ RETURN
+ -1 Error
+ 0 OK
+*/
+
+static int rtree_fill_reinsert_list(stPageList *ReinsertList, my_off_t page,
+ int level)
+{
+ if (ReinsertList->n_pages == ReinsertList->m_pages)
+ {
+ ReinsertList->m_pages += REINSERT_BUFFER_INC;
+ if (!(ReinsertList->pages = (stPageLevel*)my_realloc((gptr)ReinsertList->pages,
+ ReinsertList->m_pages * sizeof(stPageLevel), MYF(MY_ALLOW_ZERO_PTR))))
+ goto err1;
+ }
+ /* save page to ReinsertList */
+ ReinsertList->pages[ReinsertList->n_pages].offs = page;
+ ReinsertList->pages[ReinsertList->n_pages].level = level;
+ ReinsertList->n_pages++;
+ return 0;
+
+err1:
+ return -1;
+}
+
+
+/*
+ Go down and delete key from the tree
+
+ RETURN
+ -1 Error
+ 0 Deleted
+ 1 Not found
+ 2 Empty leaf
+*/
+
+static int rtree_delete_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, my_off_t page, uint *page_size,
+ stPageList *ReinsertList, int level)
+{
+ uchar *k;
+ uchar *last;
+ ulong i;
+ uint nod_flag;
+ uchar *page_buf;
+ int res;
+
+ if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
+ {
+ my_errno = HA_ERR_OUT_OF_MEM;
+ return -1;
+ }
+ if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
+ goto err1;
+ nod_flag = mi_test_if_nod(page_buf);
+
+ k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
+ last = rt_PAGE_END(page_buf);
+
+ for (i = 0; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag), ++i)
+ {
+ if (nod_flag)
+ {
+ /* not leaf */
+ if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_WITHIN))
+ {
+ switch ((res = rtree_delete_req(info, keyinfo, key, key_length,
+ _mi_kpos(nod_flag, k), page_size, ReinsertList, level + 1)))
+ {
+ case 0: /* deleted */
+ {
+ /* test page filling */
+ if (*page_size + key_length >= rt_PAGE_MIN_SIZE(keyinfo->block_length))
+ {
+ /* OK */
+ if (rtree_set_key_mbr(info, keyinfo, k, key_length,
+ _mi_kpos(nod_flag, k)))
+ goto err1;
+ if (_mi_write_keypage(info, keyinfo, page,
+ DFLT_INIT_HITS, page_buf))
+ goto err1;
+ }
+ else
+ {
+ /* too small: delete key & add it descendant to reinsert list */
+ if (rtree_fill_reinsert_list(ReinsertList, _mi_kpos(nod_flag, k),
+ level + 1))
+ goto err1;
+ rtree_delete_key(info, page_buf, k, key_length, nod_flag);
+ if (_mi_write_keypage(info, keyinfo, page,
+ DFLT_INIT_HITS, page_buf))
+ goto err1;
+ *page_size = mi_getint(page_buf);
+ }
+
+ goto ok;
+ }
+ case 1: /* not found - continue searching */
+ {
+ break;
+ }
+ case 2: /* vacuous case: last key in the leaf */
+ {
+ rtree_delete_key(info, page_buf, k, key_length, nod_flag);
+ if (_mi_write_keypage(info, keyinfo, page,
+ DFLT_INIT_HITS, page_buf))
+ goto err1;
+ *page_size = mi_getint(page_buf);
+ res = 0;
+ goto ok;
+ }
+ default: /* error */
+ case -1:
+ {
+ goto err1;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* leaf */
+ if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_EQUAL | MBR_DATA))
+ {
+ rtree_delete_key(info, page_buf, k, key_length, nod_flag);
+ *page_size = mi_getint(page_buf);
+ if (*page_size == 2)
+ {
+ /* last key in the leaf */
+ res = 2;
+ if (_mi_dispose(info, keyinfo, page, DFLT_INIT_HITS))
+ goto err1;
+ }
+ else
+ {
+ res = 0;
+ if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
+ goto err1;
+ }
+ goto ok;
+ }
+ }
+ }
+ res = 1;
+
+ok:
+ my_afree((byte*)page_buf);
+ return res;
+
+err1:
+ my_afree((byte*)page_buf);
+ return -1;
+}
+
+
+/*
+ Delete key - interface function
+
+ RETURN
+ -1 Error
+ 0 Deleted
+*/
+
+int rtree_delete(MI_INFO *info, uint keynr, uchar *key, uint key_length)
+{
+ uint page_size;
+ stPageList ReinsertList;
+ my_off_t old_root;
+ MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
+
+ if ((old_root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ {
+ my_errno= HA_ERR_END_OF_FILE;
+ return -1;
+ }
+
+ ReinsertList.pages = NULL;
+ ReinsertList.n_pages = 0;
+ ReinsertList.m_pages = 0;
+
+ switch (rtree_delete_req(info, keyinfo, key, key_length, old_root,
+ &page_size, &ReinsertList, 0))
+ {
+ case 2:
+ {
+ info->s->state.key_root[keynr] = HA_OFFSET_ERROR;
+ return 0;
+ }
+ case 0:
+ {
+ uint nod_flag;
+ ulong i;
+ for (i = 0; i < ReinsertList.n_pages; ++i)
+ {
+ uchar *page_buf;
+ uint nod_flag;
+ uchar *k;
+ uchar *last;
+
+ if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
+ {
+ my_errno = HA_ERR_OUT_OF_MEM;
+ goto err1;
+ }
+ if (!_mi_fetch_keypage(info, keyinfo, ReinsertList.pages[i].offs,
+ DFLT_INIT_HITS, page_buf, 0))
+ goto err1;
+ nod_flag = mi_test_if_nod(page_buf);
+ k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
+ last = rt_PAGE_END(page_buf);
+ for (; k < last; k = rt_PAGE_NEXT_KEY(k, key_length, nod_flag))
+ {
+ if (rtree_insert_level(info, keynr, k, key_length,
+ ReinsertList.pages[i].level) == -1)
+ {
+ my_afree((byte*)page_buf);
+ goto err1;
+ }
+ }
+ my_afree((byte*)page_buf);
+ if (_mi_dispose(info, keyinfo, ReinsertList.pages[i].offs,
+ DFLT_INIT_HITS))
+ goto err1;
+ }
+ if (ReinsertList.pages)
+ my_free((byte*) ReinsertList.pages, MYF(0));
+
+ /* check for redundant root (not leaf, 1 child) and eliminate */
+ if ((old_root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ goto err1;
+ if (!_mi_fetch_keypage(info, keyinfo, old_root, DFLT_INIT_HITS,
+ info->buff, 0))
+ goto err1;
+ nod_flag = mi_test_if_nod(info->buff);
+ page_size = mi_getint(info->buff);
+ if (nod_flag && (page_size == 2 + key_length + nod_flag))
+ {
+ my_off_t new_root = _mi_kpos(nod_flag,
+ rt_PAGE_FIRST_KEY(info->buff, nod_flag));
+ if (_mi_dispose(info, keyinfo, old_root, DFLT_INIT_HITS))
+ goto err1;
+ info->s->state.key_root[keynr] = new_root;
+ }
+ info->update= HA_STATE_DELETED;
+ return 0;
+
+err1:
+ return -1;
+ }
+ case 1: /* not found */
+ {
+ my_errno = HA_ERR_KEY_NOT_FOUND;
+ return -1;
+ }
+ default:
+ case -1: /* error */
+ {
+ return -1;
+ }
+ }
+}
+
+
+/*
+ Estimate number of suitable keys in the tree
+
+ RETURN
+ estimated value
+*/
+
+ha_rows rtree_estimate(MI_INFO *info, uint keynr, uchar *key,
+ uint key_length, uint flag)
+{
+ MI_KEYDEF *keyinfo = info->s->keyinfo + keynr;
+ my_off_t root;
+ uint i = 0;
+ uchar *k;
+ uchar *last;
+ uint nod_flag;
+ uchar *page_buf;
+ uint k_len;
+ double area = 0;
+ ha_rows res = 0;
+
+ if (flag & MBR_DISJOINT)
+ return info->state->records;
+
+ if ((root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
+ return HA_POS_ERROR;
+ if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
+ return HA_POS_ERROR;
+ if (!_mi_fetch_keypage(info, keyinfo, root, DFLT_INIT_HITS, page_buf, 0))
+ goto err1;
+ nod_flag = mi_test_if_nod(page_buf);
+
+ k_len = keyinfo->keylength - info->s->base.rec_reflength;
+
+ k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
+ last = rt_PAGE_END(page_buf);
+
+ for (; k < last; k = rt_PAGE_NEXT_KEY(k, k_len, nod_flag), ++i)
+ {
+ if (nod_flag)
+ {
+ double k_area = rtree_rect_volume(keyinfo->seg, k, key_length);
+
+ /* The following should be safe, even if we compare doubles */
+ if (k_area == 0)
+ {
+ if (flag & (MBR_CONTAIN | MBR_INTERSECT))
+ {
+ area += 1;
+ }
+ else if (flag & (MBR_WITHIN | MBR_EQUAL))
+ {
+ if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_WITHIN))
+ area += 1;
+ }
+ else
+ goto err1;
+ }
+ else
+ {
+ if (flag & (MBR_CONTAIN | MBR_INTERSECT))
+ {
+ area += rtree_overlapping_area(keyinfo->seg, key, k, key_length) /
+ k_area;
+ }
+ else if (flag & (MBR_WITHIN | MBR_EQUAL))
+ {
+ if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, MBR_WITHIN))
+ area += rtree_rect_volume(keyinfo->seg, key, key_length) /
+ k_area;
+ }
+ else
+ goto err1;
+ }
+ }
+ else
+ {
+ if (!rtree_key_cmp(keyinfo->seg, key, k, key_length, flag))
+ ++res;
+ }
+ }
+ if (nod_flag)
+ {
+ if (i)
+ res = (ha_rows) (area / i * info->state->records);
+ else
+ res = HA_POS_ERROR;
+ }
+
+ my_afree((byte*)page_buf);
+ return res;
+
+err1:
+ my_afree((byte*)page_buf);
+ return HA_POS_ERROR;
+}
+
+#endif /*HAVE_RTREE_KEYS*/
+
diff --git a/storage/myisam/rt_index.h b/storage/myisam/rt_index.h
new file mode 100644
index 00000000000..d3fcd934719
--- /dev/null
+++ b/storage/myisam/rt_index.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2000 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 + mi_getint(page))
+
+#define rt_PAGE_MIN_SIZE(block_length) ((uint)(block_length) / 3)
+
+int rtree_insert(MI_INFO *info, uint keynr, uchar *key, uint key_length);
+int rtree_delete(MI_INFO *info, uint keynr, uchar *key, uint key_length);
+
+int rtree_find_first(MI_INFO *info, uint keynr, uchar *key, uint key_length,
+ uint search_flag);
+int rtree_find_next(MI_INFO *info, uint keynr, uint search_flag);
+
+int rtree_get_first(MI_INFO *info, uint keynr, uint key_length);
+int rtree_get_next(MI_INFO *info, uint keynr, uint key_length);
+
+ha_rows rtree_estimate(MI_INFO *info, uint keynr, uchar *key,
+ uint key_length, uint flag);
+
+int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key,
+ uint key_length, my_off_t *new_page_offs);
+
+#endif /*HAVE_RTREE_KEYS*/
+#endif /* _rt_index_h */
diff --git a/storage/myisam/rt_key.c b/storage/myisam/rt_key.c
new file mode 100644
index 00000000000..e2a402fbefd
--- /dev/null
+++ b/storage/myisam/rt_key.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2000 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 "myisamdef.h"
+
+#ifdef HAVE_RTREE_KEYS
+#include "rt_index.h"
+#include "rt_key.h"
+#include "rt_mbr.h"
+
+/*
+ Add key to the page
+
+ RESULT VALUES
+ -1 Error
+ 0 Not split
+ 1 Split
+*/
+
+int rtree_add_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, uchar *page_buf, my_off_t *new_page)
+{
+ uint page_size = mi_getint(page_buf);
+ uint nod_flag = mi_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;
+ }
+ mi_putint(page_buf, page_size, nod_flag);
+ return 0;
+ }
+
+ return (rtree_split_page(info, keyinfo, page_buf, key, key_length,
+ new_page) ? -1 : 1);
+}
+
+/*
+ Delete key from the page
+*/
+int rtree_delete_key(MI_INFO *info, uchar *page_buf, uchar *key,
+ uint key_length, uint nod_flag)
+{
+ uint16 page_size = mi_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;
+
+ mi_putint(page_buf, page_size, nod_flag);
+ return 0;
+}
+
+
+/*
+ Calculate and store key MBR
+*/
+
+int rtree_set_key_mbr(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, my_off_t child_page)
+{
+ if (!_mi_fetch_keypage(info, keyinfo, child_page,
+ DFLT_INIT_HITS, info->buff, 0))
+ return -1;
+
+ return rtree_page_mbr(info, keyinfo->seg, info->buff, key, key_length);
+}
+
+#endif /*HAVE_RTREE_KEYS*/
diff --git a/storage/myisam/rt_key.h b/storage/myisam/rt_key.h
new file mode 100644
index 00000000000..df4f8aa03a2
--- /dev/null
+++ b/storage/myisam/rt_key.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2000 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 rtree_add_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, uchar *page_buf, my_off_t *new_page);
+int rtree_delete_key(MI_INFO *info, uchar *page, uchar *key,
+ uint key_length, uint nod_flag);
+int rtree_set_key_mbr(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
+ uint key_length, my_off_t child_page);
+
+#endif /*HAVE_RTREE_KEYS*/
+#endif /* _rt_key_h */
diff --git a/storage/myisam/rt_mbr.c b/storage/myisam/rt_mbr.c
new file mode 100644
index 00000000000..c43daec2f7c
--- /dev/null
+++ b/storage/myisam/rt_mbr.c
@@ -0,0 +1,801 @@
+/* Copyright (C) 2000 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 "myisamdef.h"
+
+#ifdef HAVE_RTREE_KEYS
+
+#include "rt_index.h"
+#include "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; \
+ }
+
+#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 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 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 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 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 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 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 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 rtree_page_mbr(MI_INFO *info, HA_KEYSEG *keyseg, uchar *page_buf,
+ uchar *c, uint key_length)
+{
+ uint inc = 0;
+ uint k_len = key_length;
+ uint nod_flag = mi_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*/
diff --git a/storage/myisam/rt_mbr.h b/storage/myisam/rt_mbr.h
new file mode 100644
index 00000000000..2153faad2b4
--- /dev/null
+++ b/storage/myisam/rt_mbr.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2000 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_mbr_h
+#define _rt_mbr_h
+
+#ifdef HAVE_RTREE_KEYS
+
+int rtree_key_cmp(HA_KEYSEG *keyseg, uchar *a, uchar *b, uint key_length,
+ uint nextflag);
+int rtree_combine_rect(HA_KEYSEG *keyseg,uchar *, uchar *, uchar*,
+ uint key_length);
+double rtree_rect_volume(HA_KEYSEG *keyseg, uchar*, uint key_length);
+int rtree_d_mbr(HA_KEYSEG *keyseg, uchar *a, uint key_length, double *res);
+double rtree_overlapping_area(HA_KEYSEG *keyseg, uchar *a, uchar *b,
+ uint key_length);
+double rtree_area_increase(HA_KEYSEG *keyseg, uchar *a, uchar *b,
+ uint key_length, double *ab_area);
+double rtree_perimeter_increase(HA_KEYSEG *keyseg, uchar* a, uchar* b,
+ uint key_length, double *ab_perim);
+int rtree_page_mbr(MI_INFO *info, HA_KEYSEG *keyseg, uchar *page_buf,
+ uchar* c, uint key_length);
+#endif /*HAVE_RTREE_KEYS*/
+#endif /* _rt_mbr_h */
diff --git a/storage/myisam/rt_split.c b/storage/myisam/rt_split.c
new file mode 100644
index 00000000000..005e86805bb
--- /dev/null
+++ b/storage/myisam/rt_split.c
@@ -0,0 +1,352 @@
+/* Copyright (C) 2000 MySQL AB & Alexey Botchkov & 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 "myisamdef.h"
+
+#ifdef HAVE_RTREE_KEYS
+
+#include "rt_index.h"
+#include "rt_key.h"
+#include "rt_mbr.h"
+
+typedef struct
+{
+ double square;
+ int n_node;
+ uchar *key;
+ double *coords;
+} SplitStruct;
+
+inline static double *reserve_coords(double **d_buffer, int n_dim)
+{
+ double *coords = *d_buffer;
+ (*d_buffer) += n_dim * 2;
+ return coords;
+}
+
+static void mbr_join(double *a, const double *b, int n_dim)
+{
+ double *end = a + n_dim * 2;
+ do
+ {
+ if (a[0] > b[0])
+ a[0] = b[0];
+
+ if (a[1] < b[1])
+ a[1] = b[1];
+
+ a += 2;
+ b += 2;
+ }while (a != end);
+}
+
+/*
+Counts the square of mbr which is a join of a and b
+*/
+static double mbr_join_square(const double *a, const double *b, int n_dim)
+{
+ const double *end = a + n_dim * 2;
+ double square = 1.0;
+ do
+ {
+ square *=
+ ((a[1] < b[1]) ? b[1] : a[1]) - ((a[0] > b[0]) ? b[0] : a[0]);
+
+ a += 2;
+ b += 2;
+ }while (a != end);
+
+ return square;
+}
+
+static double count_square(const double *a, int n_dim)
+{
+ const double *end = a + n_dim * 2;
+ double square = 1.0;
+ do
+ {
+ square *= a[1] - a[0];
+ a += 2;
+ }while (a != end);
+ return square;
+}
+
+inline static void copy_coords(double *dst, const double *src, int n_dim)
+{
+ memcpy(dst, src, sizeof(double) * (n_dim * 2));
+}
+
+/*
+Select two nodes to collect group upon
+*/
+static void pick_seeds(SplitStruct *node, int n_entries,
+ SplitStruct **seed_a, SplitStruct **seed_b, int n_dim)
+{
+ SplitStruct *cur1;
+ SplitStruct *lim1 = node + (n_entries - 1);
+ SplitStruct *cur2;
+ SplitStruct *lim2 = node + n_entries;
+
+ double max_d = -DBL_MAX;
+ double d;
+
+ for (cur1 = node; cur1 < lim1; ++cur1)
+ {
+ for (cur2=cur1 + 1; cur2 < lim2; ++cur2)
+ {
+
+ d = mbr_join_square(cur1->coords, cur2->coords, n_dim) - cur1->square -
+ cur2->square;
+ if (d > max_d)
+ {
+ max_d = d;
+ *seed_a = cur1;
+ *seed_b = cur2;
+ }
+ }
+ }
+}
+
+/*
+Select next node and group where to add
+*/
+static void pick_next(SplitStruct *node, int n_entries, double *g1, double *g2,
+ SplitStruct **choice, int *n_group, int n_dim)
+{
+ SplitStruct *cur = node;
+ SplitStruct *end = node + n_entries;
+
+ double max_diff = -DBL_MAX;
+
+ for (; cur<end; ++cur)
+ {
+ double diff;
+ double abs_diff;
+
+ if (cur->n_node)
+ {
+ continue;
+ }
+
+ diff = mbr_join_square(g1, cur->coords, n_dim) -
+ mbr_join_square(g2, cur->coords, n_dim);
+
+ abs_diff = fabs(diff);
+ if (abs_diff > max_diff)
+ {
+ max_diff = abs_diff;
+ *n_group = 1 + (diff > 0);
+ *choice = cur;
+ }
+ }
+}
+
+/*
+Mark not-in-group entries as n_group
+*/
+static void mark_all_entries(SplitStruct *node, int n_entries, int n_group)
+{
+ SplitStruct *cur = node;
+ SplitStruct *end = node + n_entries;
+ for (; cur<end; ++cur)
+ {
+ if (cur->n_node)
+ {
+ continue;
+ }
+ cur->n_node = n_group;
+ }
+}
+
+static int split_rtree_node(SplitStruct *node, int n_entries,
+ int all_size, /* Total key's size */
+ int key_size,
+ int min_size, /* Minimal group size */
+ int size1, int size2 /* initial group sizes */,
+ double **d_buffer, int n_dim)
+{
+ SplitStruct *cur;
+ SplitStruct *a;
+ SplitStruct *b;
+ double *g1 = reserve_coords(d_buffer, n_dim);
+ double *g2 = reserve_coords(d_buffer, n_dim);
+ SplitStruct *next;
+ int next_node;
+ int i;
+ SplitStruct *end = node + n_entries;
+
+ if (all_size < min_size * 2)
+ {
+ return 1;
+ }
+
+ cur = node;
+ for (; cur<end; ++cur)
+ {
+ cur->square = count_square(cur->coords, n_dim);
+ cur->n_node = 0;
+ }
+
+ pick_seeds(node, n_entries, &a, &b, n_dim);
+ a->n_node = 1;
+ b->n_node = 2;
+
+
+ copy_coords(g1, a->coords, n_dim);
+ size1 += key_size;
+ copy_coords(g2, b->coords, n_dim);
+ size2 += key_size;
+
+
+ for (i=n_entries - 2; i>0; --i)
+ {
+ if (all_size - (size2 + key_size) < min_size) /* Can't write into group 2 */
+ {
+ mark_all_entries(node, n_entries, 1);
+ break;
+ }
+
+ if (all_size - (size1 + key_size) < min_size) /* Can't write into group 1 */
+ {
+ mark_all_entries(node, n_entries, 2);
+ break;
+ }
+
+ pick_next(node, n_entries, g1, g2, &next, &next_node, n_dim);
+ if (next_node == 1)
+ {
+ size1 += key_size;
+ mbr_join(g1, next->coords, n_dim);
+ }
+ else
+ {
+ size2 += key_size;
+ mbr_join(g2, next->coords, n_dim);
+ }
+ next->n_node = next_node;
+ }
+
+ return 0;
+}
+
+int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key,
+ uint key_length, my_off_t *new_page_offs)
+{
+ int n1, n2; /* Number of items in groups */
+
+ SplitStruct *task;
+ SplitStruct *cur;
+ SplitStruct *stop;
+ double *coord_buf;
+ double *next_coord;
+ double *old_coord;
+ int n_dim;
+ uchar *source_cur, *cur1, *cur2;
+ uchar *new_page;
+ int err_code = 0;
+
+ uint nod_flag = mi_test_if_nod(page);
+ uint full_length = key_length + (nod_flag ? nod_flag :
+ info->s->base.rec_reflength);
+
+ int max_keys = (mi_getint(page)-2) / (full_length);
+
+ n_dim = keyinfo->keysegs / 2;
+
+ if (!(coord_buf= my_alloca(n_dim * 2 * sizeof(double) * (max_keys + 1 + 4) +
+ sizeof(SplitStruct) * (max_keys + 1))))
+ return -1;
+
+ task= (SplitStruct *)(coord_buf + n_dim * 2 * (max_keys + 1 + 4));
+
+ next_coord = coord_buf;
+
+ stop = task + max_keys;
+ source_cur = rt_PAGE_FIRST_KEY(page, nod_flag);
+
+ for (cur = task; cur < stop; ++cur, source_cur = rt_PAGE_NEXT_KEY(source_cur,
+ key_length, nod_flag))
+ {
+ cur->coords = reserve_coords(&next_coord, n_dim);
+ cur->key = source_cur;
+ rtree_d_mbr(keyinfo->seg, source_cur, key_length, cur->coords);
+ }
+
+ cur->coords = reserve_coords(&next_coord, n_dim);
+ rtree_d_mbr(keyinfo->seg, key, key_length, cur->coords);
+ cur->key = key;
+
+ old_coord = next_coord;
+
+ if (split_rtree_node(task, max_keys + 1,
+ mi_getint(page) + full_length + 2, full_length,
+ rt_PAGE_MIN_SIZE(keyinfo->block_length),
+ 2, 2, &next_coord, n_dim))
+ {
+ err_code = 1;
+ goto split_err;
+ }
+
+ if (!(new_page = (uchar*)my_alloca((uint)keyinfo->block_length)))
+ {
+ err_code= -1;
+ goto split_err;
+ }
+
+ stop = task + (max_keys + 1);
+ cur1 = rt_PAGE_FIRST_KEY(page, nod_flag);
+ cur2 = rt_PAGE_FIRST_KEY(new_page, nod_flag);
+
+ n1 = 0;
+ n2 = 0;
+ for (cur = task; cur < stop; ++cur)
+ {
+ uchar *to;
+ if (cur->n_node == 1)
+ {
+ to = cur1;
+ cur1 = rt_PAGE_NEXT_KEY(cur1, key_length, nod_flag);
+ ++n1;
+ }
+ else
+ {
+ to = cur2;
+ cur2 = rt_PAGE_NEXT_KEY(cur2, key_length, nod_flag);
+ ++n2;
+ }
+ if (to != cur->key)
+ memcpy(to - nod_flag, cur->key - nod_flag, full_length);
+ }
+
+ mi_putint(page, 2 + n1 * full_length, nod_flag);
+ mi_putint(new_page, 2 + n2 * full_length, nod_flag);
+
+ if ((*new_page_offs= _mi_new(info, keyinfo, DFLT_INIT_HITS)) ==
+ HA_OFFSET_ERROR)
+ err_code= -1;
+ else
+ err_code= _mi_write_keypage(info, keyinfo, *new_page_offs,
+ DFLT_INIT_HITS, new_page);
+
+ my_afree((byte*)new_page);
+
+split_err:
+ my_afree((byte*) coord_buf);
+ return err_code;
+}
+
+#endif /*HAVE_RTREE_KEYS*/
diff --git a/storage/myisam/rt_test.c b/storage/myisam/rt_test.c
new file mode 100644
index 00000000000..4f04aa11fce
--- /dev/null
+++ b/storage/myisam/rt_test.c
@@ -0,0 +1,471 @@
+/* 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 */
+
+/* Testing of the basic functions of a MyISAM rtree table */
+/* Written by Alex Barkov who has a shared copyright to this code */
+
+
+#include "myisam.h"
+
+#ifdef HAVE_RTREE_KEYS
+
+#include "rt_index.h"
+
+#define MAX_REC_LENGTH 1024
+#define ndims 2
+#define KEYALG HA_KEY_ALG_RTREE
+
+static int read_with_pos(MI_INFO * file, int silent);
+static void create_record(char *record,uint rownr);
+static void create_record1(char *record,uint rownr);
+static void print_record(char * record,my_off_t offs,const char * tail);
+static int run_test(const char *filename);
+
+static double rt_data[]=
+{
+ /*1*/ 0,10,0,10,
+ /*2*/ 5,15,0,10,
+ /*3*/ 0,10,5,15,
+ /*4*/ 10,20,10,20,
+ /*5*/ 0,10,0,10,
+ /*6*/ 5,15,0,10,
+ /*7*/ 0,10,5,15,
+ /*8*/ 10,20,10,20,
+ /*9*/ 0,10,0,10,
+ /*10*/ 5,15,0,10,
+ /*11*/ 0,10,5,15,
+ /*12*/ 10,20,10,20,
+ /*13*/ 0,10,0,10,
+ /*14*/ 5,15,0,10,
+ /*15*/ 0,10,5,15,
+ /*16*/ 10,20,10,20,
+ /*17*/ 5,15,0,10,
+ /*18*/ 0,10,5,15,
+ /*19*/ 10,20,10,20,
+ /*20*/ 0,10,0,10,
+
+ /*1*/ 100,110,0,10,
+ /*2*/ 105,115,0,10,
+ /*3*/ 100,110,5,15,
+ /*4*/ 110,120,10,20,
+ /*5*/ 100,110,0,10,
+ /*6*/ 105,115,0,10,
+ /*7*/ 100,110,5,15,
+ /*8*/ 110,120,10,20,
+ /*9*/ 100,110,0,10,
+ /*10*/ 105,115,0,10,
+ /*11*/ 100,110,5,15,
+ /*12*/ 110,120,10,20,
+ /*13*/ 100,110,0,10,
+ /*14*/ 105,115,0,10,
+ /*15*/ 100,110,5,15,
+ /*16*/ 110,120,10,20,
+ /*17*/ 105,115,0,10,
+ /*18*/ 100,110,5,15,
+ /*19*/ 110,120,10,20,
+ /*20*/ 100,110,0,10,
+ -1
+};
+
+int main(int argc __attribute__((unused)),char *argv[] __attribute__((unused)))
+{
+ MY_INIT(argv[0]);
+ exit(run_test("rt_test"));
+}
+
+
+static int run_test(const char *filename)
+{
+ MI_INFO *file;
+ MI_UNIQUEDEF uniquedef;
+ MI_CREATE_INFO create_info;
+ MI_COLUMNDEF recinfo[20];
+ MI_KEYDEF keyinfo[20];
+ HA_KEYSEG keyseg[20];
+ key_range range;
+
+ int silent=0;
+ int opt_unique=0;
+ int create_flag=0;
+ int key_type=HA_KEYTYPE_DOUBLE;
+ int key_length=8;
+ int null_fields=0;
+ int nrecords=sizeof(rt_data)/(sizeof(double)*4);/* 3000;*/
+ int rec_length=0;
+ int uniques=0;
+ int i;
+ int error;
+ int row_count=0;
+ char record[MAX_REC_LENGTH];
+ char read_record[MAX_REC_LENGTH];
+ int upd= 10;
+ ha_rows hrows;
+
+ /* Define a column for NULLs and DEL markers*/
+
+ recinfo[0].type=FIELD_NORMAL;
+ recinfo[0].length=1; /* For NULL bits */
+ rec_length=1;
+
+ /* Define 2*ndims columns for coordinates*/
+
+ for (i=1; i<=2*ndims ;i++){
+ recinfo[i].type=FIELD_NORMAL;
+ recinfo[i].length=key_length;
+ rec_length+=key_length;
+ }
+
+ /* Define a key with 2*ndims segments */
+
+ keyinfo[0].seg=keyseg;
+ keyinfo[0].keysegs=2*ndims;
+ keyinfo[0].flag=0;
+ keyinfo[0].key_alg=KEYALG;
+
+ for (i=0; i<2*ndims; i++){
+ keyinfo[0].seg[i].type= key_type;
+ keyinfo[0].seg[i].flag=0; /* Things like HA_REVERSE_SORT */
+ keyinfo[0].seg[i].start= (key_length*i)+1;
+ keyinfo[0].seg[i].length=key_length;
+ keyinfo[0].seg[i].null_bit= null_fields ? 2 : 0;
+ keyinfo[0].seg[i].null_pos=0;
+ keyinfo[0].seg[i].language=default_charset_info->number;
+ }
+
+ if (!silent)
+ printf("- Creating isam-file\n");
+
+ bzero((char*) &create_info,sizeof(create_info));
+ create_info.max_rows=10000000;
+
+ if (mi_create(filename,
+ 1, /* keys */
+ keyinfo,
+ 1+2*ndims+opt_unique, /* columns */
+ recinfo,uniques,&uniquedef,&create_info,create_flag))
+ goto err;
+
+ if (!silent)
+ printf("- Open isam-file\n");
+
+ if (!(file=mi_open(filename,2,HA_OPEN_ABORT_IF_LOCKED)))
+ goto err;
+
+ if (!silent)
+ printf("- Writing key:s\n");
+
+ for (i=0; i<nrecords; i++ )
+ {
+ create_record(record,i);
+ error=mi_write(file,record);
+ print_record(record,mi_position(file),"\n");
+ if (!error)
+ {
+ row_count++;
+ }
+ else
+ {
+ printf("mi_write: %d\n", error);
+ goto err;
+ }
+ }
+
+ if ((error=read_with_pos(file,silent)))
+ goto err;
+
+ if (!silent)
+ printf("- Reading rows with key\n");
+
+ for (i=0 ; i < nrecords ; i++)
+ {
+ my_errno=0;
+ create_record(record,i);
+
+ bzero((char*) read_record,MAX_REC_LENGTH);
+ error=mi_rkey(file,read_record,0,record+1,0,HA_READ_MBR_EQUAL);
+
+ if (error && error!=HA_ERR_KEY_NOT_FOUND)
+ {
+ printf(" mi_rkey: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ if (error == HA_ERR_KEY_NOT_FOUND)
+ {
+ print_record(record,mi_position(file)," NOT FOUND\n");
+ continue;
+ }
+ print_record(read_record,mi_position(file),"\n");
+ }
+
+ if (!silent)
+ printf("- Deleting rows\n");
+ for (i=0; i < nrecords/4; i++)
+ {
+ my_errno=0;
+ bzero((char*) read_record,MAX_REC_LENGTH);
+ error=mi_rrnd(file,read_record,i == 0 ? 0L : HA_OFFSET_ERROR);
+ if (error)
+ {
+ printf("pos: %2d mi_rrnd: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file),"\n");
+
+ error=mi_delete(file,read_record);
+ if (error)
+ {
+ printf("pos: %2d mi_delete: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ }
+
+ if (!silent)
+ printf("- Updating rows with position\n");
+ for (i=0; i < (nrecords - nrecords/4) ; i++)
+ {
+ my_errno=0;
+ bzero((char*) read_record,MAX_REC_LENGTH);
+ error=mi_rrnd(file,read_record,i == 0 ? 0L : HA_OFFSET_ERROR);
+ if (error)
+ {
+ if (error==HA_ERR_RECORD_DELETED)
+ continue;
+ printf("pos: %2d mi_rrnd: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file),"");
+ create_record(record,i+nrecords*upd);
+ printf("\t-> ");
+ print_record(record,mi_position(file),"\n");
+ error=mi_update(file,read_record,record);
+ if (error)
+ {
+ printf("pos: %2d mi_update: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ }
+
+ if ((error=read_with_pos(file,silent)))
+ goto err;
+
+ if (!silent)
+ printf("- Test mi_rkey then a sequence of mi_rnext_same\n");
+
+ create_record(record, nrecords*4/5);
+ print_record(record,0," search for\n");
+
+ if ((error=mi_rkey(file,read_record,0,record+1,0,HA_READ_MBR_INTERSECT)))
+ {
+ printf("mi_rkey: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file)," mi_rkey\n");
+ row_count=1;
+
+ for (;;)
+ {
+ if ((error=mi_rnext_same(file,read_record)))
+ {
+ if (error==HA_ERR_END_OF_FILE)
+ break;
+ printf("mi_next: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file)," mi_rnext_same\n");
+ row_count++;
+ }
+ printf(" %d rows\n",row_count);
+
+ if (!silent)
+ printf("- Test mi_rfirst then a sequence of mi_rnext\n");
+
+ error=mi_rfirst(file,read_record,0);
+ if (error)
+ {
+ printf("mi_rfirst: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ row_count=1;
+ print_record(read_record,mi_position(file)," mi_frirst\n");
+
+ for (i=0;i<nrecords;i++)
+ {
+ if ((error=mi_rnext(file,read_record,0)))
+ {
+ if (error==HA_ERR_END_OF_FILE)
+ break;
+ printf("mi_next: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file)," mi_rnext\n");
+ row_count++;
+ }
+ printf(" %d rows\n",row_count);
+
+ if (!silent)
+ printf("- Test mi_records_in_range()\n");
+
+ create_record1(record, nrecords*4/5);
+ print_record(record,0,"\n");
+
+ range.key= record+1;
+ range.length= 1000; /* Big enough */
+ range.flag= HA_READ_MBR_INTERSECT;
+ hrows= mi_records_in_range(file,0, &range, (key_range*) 0);
+ printf(" %ld rows\n", (long) hrows);
+
+ if (mi_close(file)) goto err;
+ my_end(MY_CHECK_ERROR);
+
+ return 0;
+
+err:
+ printf("got error: %3d when using myisam-database\n",my_errno);
+ return 1; /* skip warning */
+}
+
+
+
+static int read_with_pos (MI_INFO * file,int silent)
+{
+ int error;
+ int i;
+ char read_record[MAX_REC_LENGTH];
+
+ if (!silent)
+ printf("- Reading rows with position\n");
+ for (i=0;;i++)
+ {
+ my_errno=0;
+ bzero((char*) read_record,MAX_REC_LENGTH);
+ error=mi_rrnd(file,read_record,i == 0 ? 0L : HA_OFFSET_ERROR);
+ if (error)
+ {
+ if (error==HA_ERR_END_OF_FILE)
+ break;
+ if (error==HA_ERR_RECORD_DELETED)
+ continue;
+ printf("pos: %2d mi_rrnd: %3d errno: %3d\n",i,error,my_errno);
+ return error;
+ }
+ print_record(read_record,mi_position(file),"\n");
+ }
+ return 0;
+}
+
+
+#ifdef NOT_USED
+static void bprint_record(char * record,
+ my_off_t offs __attribute__((unused)),
+ const char * tail)
+{
+ int i;
+ char * pos;
+ i=(unsigned char)record[0];
+ printf("%02X ",i);
+
+ for( pos=record+1, i=0; i<32; i++,pos++){
+ int b=(unsigned char)*pos;
+ printf("%02X",b);
+ }
+ printf("%s",tail);
+}
+#endif
+
+
+static void print_record(char * record,
+ my_off_t offs __attribute__((unused)),
+ const char * tail)
+{
+ int i;
+ char * pos;
+ double c;
+
+ printf(" rec=(%d)",(unsigned char)record[0]);
+ for ( pos=record+1, i=0; i<2*ndims; i++)
+ {
+ memcpy(&c,pos,sizeof(c));
+ float8get(c,pos);
+ printf(" %.14g ",c);
+ pos+=sizeof(c);
+ }
+ printf("pos=%ld",(long int)offs);
+ printf("%s",tail);
+}
+
+
+
+static void create_record1(char *record,uint rownr)
+{
+ int i;
+ char * pos;
+ double c=rownr+10;
+
+ bzero((char*) record,MAX_REC_LENGTH);
+ record[0]=0x01; /* DEL marker */
+
+ for ( pos=record+1, i=0; i<2*ndims; i++)
+ {
+ memcpy(pos,&c,sizeof(c));
+ float8store(pos,c);
+ pos+=sizeof(c);
+ }
+}
+
+#ifdef NOT_USED
+
+static void create_record0(char *record,uint rownr)
+{
+ int i;
+ char * pos;
+ double c=rownr+10;
+ double c0=0;
+
+ bzero((char*) record,MAX_REC_LENGTH);
+ record[0]=0x01; /* DEL marker */
+
+ for ( pos=record+1, i=0; i<ndims; i++)
+ {
+ memcpy(pos,&c0,sizeof(c0));
+ float8store(pos,c0);
+ pos+=sizeof(c0);
+ memcpy(pos,&c,sizeof(c));
+ float8store(pos,c);
+ pos+=sizeof(c);
+ }
+}
+
+#endif
+
+static void create_record(char *record,uint rownr)
+{
+ int i;
+ char *pos;
+ double *data= rt_data+rownr*4;
+ record[0]=0x01; /* DEL marker */
+ for ( pos=record+1, i=0; i<ndims*2; i++)
+ {
+ float8store(pos,data[i]);
+ pos+=8;
+ }
+}
+
+#else
+int main(int argc __attribute__((unused)),char *argv[] __attribute__((unused)))
+{
+ exit(0);
+}
+#endif /*HAVE_RTREE_KEYS*/
diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c
new file mode 100644
index 00000000000..9d2af2e8c70
--- /dev/null
+++ b/storage/myisam/sort.c
@@ -0,0 +1,1017 @@
+/* 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 */
+
+/*
+ Creates a index for a database by reading keys, sorting them and outputing
+ them in sorted order through SORT_INFO functions.
+*/
+
+#include "fulltext.h"
+#if defined(MSDOS) || defined(__WIN__)
+#include <fcntl.h>
+#else
+#include <stddef.h>
+#endif
+#include <queues.h>
+
+/* static variables */
+
+#undef MIN_SORT_MEMORY
+#undef MYF_RW
+#undef DISK_BUFFER_SIZE
+
+#define MERGEBUFF 15
+#define MERGEBUFF2 31
+#define MIN_SORT_MEMORY (4096-MALLOC_OVERHEAD)
+#define MYF_RW MYF(MY_NABP | MY_WME | MY_WAIT_IF_FULL)
+#define DISK_BUFFER_SIZE (IO_SIZE*16)
+
+
+/*
+ Pointers of functions for store and read keys from temp file
+*/
+
+extern void print_error _VARARGS((const char *fmt,...));
+
+/* Functions defined in this file */
+
+static ha_rows NEAR_F find_all_keys(MI_SORT_PARAM *info,uint keys,
+ uchar **sort_keys,
+ DYNAMIC_ARRAY *buffpek,int *maxbuffer,
+ IO_CACHE *tempfile,
+ IO_CACHE *tempfile_for_exceptions);
+static int NEAR_F write_keys(MI_SORT_PARAM *info,uchar **sort_keys,
+ uint count, BUFFPEK *buffpek,IO_CACHE *tempfile);
+static int NEAR_F write_key(MI_SORT_PARAM *info, uchar *key,
+ IO_CACHE *tempfile);
+static int NEAR_F write_index(MI_SORT_PARAM *info,uchar * *sort_keys,
+ uint count);
+static int NEAR_F merge_many_buff(MI_SORT_PARAM *info,uint keys,
+ uchar * *sort_keys,
+ BUFFPEK *buffpek,int *maxbuffer,
+ IO_CACHE *t_file);
+static uint NEAR_F read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
+ uint sort_length);
+static int NEAR_F merge_buffers(MI_SORT_PARAM *info,uint keys,
+ IO_CACHE *from_file, IO_CACHE *to_file,
+ uchar * *sort_keys, BUFFPEK *lastbuff,
+ BUFFPEK *Fb, BUFFPEK *Tb);
+static int NEAR_F merge_index(MI_SORT_PARAM *,uint,uchar **,BUFFPEK *, int,
+ IO_CACHE *);
+static int flush_ft_buf(MI_SORT_PARAM *info);
+
+static int NEAR_F write_keys_varlen(MI_SORT_PARAM *info,uchar **sort_keys,
+ uint count, BUFFPEK *buffpek,
+ IO_CACHE *tempfile);
+static uint NEAR_F read_to_buffer_varlen(IO_CACHE *fromfile,BUFFPEK *buffpek,
+ uint sort_length);
+static int NEAR_F write_merge_key(MI_SORT_PARAM *info, IO_CACHE *to_file,
+ char *key, uint sort_length, uint count);
+static int NEAR_F write_merge_key_varlen(MI_SORT_PARAM *info,
+ IO_CACHE *to_file,
+ char* key, uint sort_length,
+ uint count);
+static inline int
+my_var_write(MI_SORT_PARAM *info, IO_CACHE *to_file, byte *bufs);
+
+/*
+ Creates a index of sorted keys
+
+ SYNOPSIS
+ _create_index_by_sort()
+ info Sort parameters
+ no_messages Set to 1 if no output
+ sortbuff_size Size if sortbuffer to allocate
+
+ RESULT
+ 0 ok
+ <> 0 Error
+*/
+
+int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages,
+ ulong sortbuff_size)
+{
+ int error,maxbuffer,skr;
+ uint memavl,old_memavl,keys,sort_length;
+ DYNAMIC_ARRAY buffpek;
+ ha_rows records;
+ uchar **sort_keys;
+ IO_CACHE tempfile, tempfile_for_exceptions;
+ DBUG_ENTER("_create_index_by_sort");
+ DBUG_PRINT("enter",("sort_length: %d", info->key_length));
+
+ if (info->keyinfo->flag & HA_VAR_LENGTH_KEY)
+ {
+ info->write_keys=write_keys_varlen;
+ info->read_to_buffer=read_to_buffer_varlen;
+ info->write_key=write_merge_key_varlen;
+ }
+ else
+ {
+ info->write_keys=write_keys;
+ info->read_to_buffer=read_to_buffer;
+ info->write_key=write_merge_key;
+ }
+
+ my_b_clear(&tempfile);
+ my_b_clear(&tempfile_for_exceptions);
+ bzero((char*) &buffpek,sizeof(buffpek));
+ sort_keys= (uchar **) NULL; error= 1;
+ maxbuffer=1;
+
+ memavl=max(sortbuff_size,MIN_SORT_MEMORY);
+ records= info->sort_info->max_records;
+ sort_length= info->key_length;
+ LINT_INIT(keys);
+
+ while (memavl >= MIN_SORT_MEMORY)
+ {
+ if ((my_off_t) (records+1)*(sort_length+sizeof(char*)) <=
+ (my_off_t) memavl)
+ keys= records+1;
+ else
+ do
+ {
+ skr=maxbuffer;
+ if (memavl < sizeof(BUFFPEK)*(uint) maxbuffer ||
+ (keys=(memavl-sizeof(BUFFPEK)*(uint) maxbuffer)/
+ (sort_length+sizeof(char*))) <= 1)
+ {
+ mi_check_print_error(info->sort_info->param,
+ "sort_buffer_size is to small");
+ goto err;
+ }
+ }
+ while ((maxbuffer= (int) (records/(keys-1)+1)) != skr);
+
+ if ((sort_keys=(uchar **)my_malloc(keys*(sort_length+sizeof(char*))+
+ HA_FT_MAXBYTELEN, MYF(0))))
+ {
+ if (my_init_dynamic_array(&buffpek, sizeof(BUFFPEK), maxbuffer,
+ maxbuffer/2))
+ {
+ my_free((gptr) sort_keys,MYF(0));
+ sort_keys= 0;
+ }
+ else
+ break;
+ }
+ old_memavl=memavl;
+ if ((memavl=memavl/4*3) < MIN_SORT_MEMORY && old_memavl > MIN_SORT_MEMORY)
+ memavl=MIN_SORT_MEMORY;
+ }
+ if (memavl < MIN_SORT_MEMORY)
+ {
+ mi_check_print_error(info->sort_info->param,"Sort buffer to small"); /* purecov: tested */
+ goto err; /* purecov: tested */
+ }
+ (*info->lock_in_memory)(info->sort_info->param);/* Everything is allocated */
+
+ if (!no_messages)
+ printf(" - Searching for keys, allocating buffer for %d keys\n",keys);
+
+ if ((records=find_all_keys(info,keys,sort_keys,&buffpek,&maxbuffer,
+ &tempfile,&tempfile_for_exceptions))
+ == HA_POS_ERROR)
+ goto err; /* purecov: tested */
+ if (maxbuffer == 0)
+ {
+ if (!no_messages)
+ printf(" - Dumping %lu keys\n", (ulong) records);
+ if (write_index(info,sort_keys, (uint) records))
+ goto err; /* purecov: inspected */
+ }
+ else
+ {
+ keys=(keys*(sort_length+sizeof(char*)))/sort_length;
+ if (maxbuffer >= MERGEBUFF2)
+ {
+ if (!no_messages)
+ printf(" - Merging %lu keys\n", (ulong) records); /* purecov: tested */
+ if (merge_many_buff(info,keys,sort_keys,
+ dynamic_element(&buffpek,0,BUFFPEK *),&maxbuffer,&tempfile))
+ goto err; /* purecov: inspected */
+ }
+ if (flush_io_cache(&tempfile) ||
+ reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
+ goto err; /* purecov: inspected */
+ if (!no_messages)
+ printf(" - Last merge and dumping keys\n"); /* purecov: tested */
+ if (merge_index(info,keys,sort_keys,dynamic_element(&buffpek,0,BUFFPEK *),
+ maxbuffer,&tempfile))
+ goto err; /* purecov: inspected */
+ }
+
+ if (flush_ft_buf(info) || flush_pending_blocks(info))
+ goto err;
+
+ if (my_b_inited(&tempfile_for_exceptions))
+ {
+ MI_INFO *index=info->sort_info->info;
+ uint keyno=info->key;
+ uint key_length, ref_length=index->s->rec_reflength;
+
+ if (!no_messages)
+ printf(" - Adding exceptions\n"); /* purecov: tested */
+ if (flush_io_cache(&tempfile_for_exceptions) ||
+ reinit_io_cache(&tempfile_for_exceptions,READ_CACHE,0L,0,0))
+ goto err;
+
+ while (!my_b_read(&tempfile_for_exceptions,(byte*)&key_length,
+ sizeof(key_length))
+ && !my_b_read(&tempfile_for_exceptions,(byte*)sort_keys,
+ (uint) key_length))
+ {
+ if (_mi_ck_write(index,keyno,(uchar*) sort_keys,key_length-ref_length))
+ goto err;
+ }
+ }
+
+ error =0;
+
+err:
+ if (sort_keys)
+ my_free((gptr) sort_keys,MYF(0));
+ delete_dynamic(&buffpek);
+ close_cached_file(&tempfile);
+ close_cached_file(&tempfile_for_exceptions);
+
+ DBUG_RETURN(error ? -1 : 0);
+} /* _create_index_by_sort */
+
+
+/* Search after all keys and place them in a temp. file */
+
+static ha_rows NEAR_F find_all_keys(MI_SORT_PARAM *info, uint keys,
+ uchar **sort_keys, DYNAMIC_ARRAY *buffpek,
+ int *maxbuffer, IO_CACHE *tempfile,
+ IO_CACHE *tempfile_for_exceptions)
+{
+ int error;
+ uint idx;
+ DBUG_ENTER("find_all_keys");
+
+ idx=error=0;
+ sort_keys[0]=(uchar*) (sort_keys+keys);
+
+ while (!(error=(*info->key_read)(info,sort_keys[idx])))
+ {
+ if (info->real_key_length > info->key_length)
+ {
+ if (write_key(info,sort_keys[idx],tempfile_for_exceptions))
+ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ continue;
+ }
+
+ if (++idx == keys)
+ {
+ if (info->write_keys(info,sort_keys,idx-1,(BUFFPEK *)alloc_dynamic(buffpek),
+ tempfile))
+ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+
+ sort_keys[0]=(uchar*) (sort_keys+keys);
+ memcpy(sort_keys[0],sort_keys[idx-1],(size_t) info->key_length);
+ idx=1;
+ }
+ sort_keys[idx]=sort_keys[idx-1]+info->key_length;
+ }
+ if (error > 0)
+ DBUG_RETURN(HA_POS_ERROR); /* Aborted by get_key */ /* purecov: inspected */
+ if (buffpek->elements)
+ {
+ if (info->write_keys(info,sort_keys,idx,(BUFFPEK *)alloc_dynamic(buffpek),
+ tempfile))
+ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ *maxbuffer=buffpek->elements-1;
+ }
+ else
+ *maxbuffer=0;
+
+ DBUG_RETURN((*maxbuffer)*(keys-1)+idx);
+} /* find_all_keys */
+
+
+#ifdef THREAD
+/* Search after all keys and place them in a temp. file */
+
+pthread_handler_decl(thr_find_all_keys,arg)
+{
+ MI_SORT_PARAM *info= (MI_SORT_PARAM*) arg;
+ int error;
+ uint memavl,old_memavl,keys,sort_length;
+ uint idx, maxbuffer;
+ uchar **sort_keys=0;
+
+ LINT_INIT(keys);
+
+ error=1;
+
+ if (my_thread_init())
+ goto err;
+ if (info->sort_info->got_error)
+ goto err;
+
+ if (info->keyinfo->flag && HA_VAR_LENGTH_KEY)
+ {
+ info->write_keys=write_keys_varlen;
+ info->read_to_buffer=read_to_buffer_varlen;
+ info->write_key=write_merge_key_varlen;
+ }
+ else
+ {
+ info->write_keys=write_keys;
+ info->read_to_buffer=read_to_buffer;
+ info->write_key=write_merge_key;
+ }
+
+ my_b_clear(&info->tempfile);
+ my_b_clear(&info->tempfile_for_exceptions);
+ bzero((char*) &info->buffpek,sizeof(info->buffpek));
+ bzero((char*) &info->unique, sizeof(info->unique));
+ sort_keys= (uchar **) NULL;
+
+ memavl=max(info->sortbuff_size, MIN_SORT_MEMORY);
+ idx= info->sort_info->max_records;
+ sort_length= info->key_length;
+ maxbuffer= 1;
+
+ while (memavl >= MIN_SORT_MEMORY)
+ {
+ if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <=
+ (my_off_t) memavl)
+ keys= idx+1;
+ else
+ {
+ uint skr;
+ do
+ {
+ skr=maxbuffer;
+ if (memavl < sizeof(BUFFPEK)*maxbuffer ||
+ (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/
+ (sort_length+sizeof(char*))) <= 1)
+ {
+ mi_check_print_error(info->sort_info->param,
+ "sort_buffer_size is to small");
+ goto err;
+ }
+ }
+ while ((maxbuffer= (int) (idx/(keys-1)+1)) != skr);
+ }
+ if ((sort_keys=(uchar **)my_malloc(keys*(sort_length+sizeof(char*))+
+ ((info->keyinfo->flag & HA_FULLTEXT) ?
+ HA_FT_MAXBYTELEN : 0), MYF(0))))
+ {
+ if (my_init_dynamic_array(&info->buffpek, sizeof(BUFFPEK),
+ maxbuffer, maxbuffer/2))
+ my_free((gptr) sort_keys,MYF(0));
+ else
+ break;
+ }
+ old_memavl=memavl;
+ if ((memavl=memavl/4*3) < MIN_SORT_MEMORY && old_memavl > MIN_SORT_MEMORY)
+ memavl=MIN_SORT_MEMORY;
+ }
+ if (memavl < MIN_SORT_MEMORY)
+ {
+ mi_check_print_error(info->sort_info->param,"Sort buffer to small"); /* purecov: tested */
+ goto err; /* purecov: tested */
+ }
+
+ if (info->sort_info->param->testflag & T_VERBOSE)
+ printf("Key %d - Allocating buffer for %d keys\n",info->key+1,keys);
+ info->sort_keys=sort_keys;
+
+ idx=error=0;
+ sort_keys[0]=(uchar*) (sort_keys+keys);
+
+ while (!(error=info->sort_info->got_error) &&
+ !(error=(*info->key_read)(info,sort_keys[idx])))
+ {
+ if (info->real_key_length > info->key_length)
+ {
+ if (write_key(info,sort_keys[idx], &info->tempfile_for_exceptions))
+ goto err;
+ continue;
+ }
+
+ if (++idx == keys)
+ {
+ if (info->write_keys(info,sort_keys,idx-1,
+ (BUFFPEK *)alloc_dynamic(&info->buffpek),
+ &info->tempfile))
+ goto err;
+ sort_keys[0]=(uchar*) (sort_keys+keys);
+ memcpy(sort_keys[0],sort_keys[idx-1],(size_t) info->key_length);
+ idx=1;
+ }
+ sort_keys[idx]=sort_keys[idx-1]+info->key_length;
+ }
+ if (error > 0)
+ goto err;
+ if (info->buffpek.elements)
+ {
+ if (info->write_keys(info,sort_keys, idx,
+ (BUFFPEK *) alloc_dynamic(&info->buffpek), &info->tempfile))
+ goto err;
+ info->keys=(info->buffpek.elements-1)*(keys-1)+idx;
+ }
+ else
+ info->keys=idx;
+
+ info->sort_keys_length=keys;
+ goto ok;
+
+err:
+ info->sort_info->got_error=1; /* no need to protect this with a mutex */
+ if (sort_keys)
+ my_free((gptr) sort_keys,MYF(0));
+ info->sort_keys=0;
+ delete_dynamic(& info->buffpek);
+ close_cached_file(&info->tempfile);
+ close_cached_file(&info->tempfile_for_exceptions);
+
+ok:
+ remove_io_thread(&info->read_cache);
+ pthread_mutex_lock(&info->sort_info->mutex);
+ info->sort_info->threads_running--;
+ pthread_cond_signal(&info->sort_info->cond);
+ pthread_mutex_unlock(&info->sort_info->mutex);
+ my_thread_end();
+ return NULL;
+}
+
+
+int thr_write_keys(MI_SORT_PARAM *sort_param)
+{
+ SORT_INFO *sort_info=sort_param->sort_info;
+ MI_CHECK *param=sort_info->param;
+ ulong length, keys;
+ ulong *rec_per_key_part=param->rec_per_key_part;
+ int got_error=sort_info->got_error;
+ uint i;
+ MI_INFO *info=sort_info->info;
+ MYISAM_SHARE *share=info->s;
+ MI_SORT_PARAM *sinfo;
+ byte *mergebuf=0;
+ LINT_INIT(length);
+
+ for (i= 0, sinfo= sort_param ;
+ i < sort_info->total_keys ;
+ i++, rec_per_key_part+=sinfo->keyinfo->keysegs, sinfo++)
+ {
+ if (!sinfo->sort_keys)
+ {
+ got_error=1;
+ continue;
+ }
+ if (!got_error)
+ {
+ share->state.key_map|=(ulonglong) 1 << sinfo->key;
+ if (param->testflag & T_STATISTICS)
+ update_key_parts(sinfo->keyinfo, rec_per_key_part,
+ sinfo->unique, (ulonglong) info->state->records);
+ if (!sinfo->buffpek.elements)
+ {
+ if (param->testflag & T_VERBOSE)
+ {
+ printf("Key %d - Dumping %u keys\n",sinfo->key+1, sinfo->keys);
+ fflush(stdout);
+ }
+ if (write_index(sinfo, sinfo->sort_keys, sinfo->keys) ||
+ flush_ft_buf(sinfo) || flush_pending_blocks(sinfo))
+ got_error=1;
+ }
+ }
+ my_free((gptr) sinfo->sort_keys,MYF(0));
+ my_free(mi_get_rec_buff_ptr(info, sinfo->rec_buff),
+ MYF(MY_ALLOW_ZERO_PTR));
+ sinfo->sort_keys=0;
+ }
+
+ for (i= 0, sinfo= sort_param ;
+ i < sort_info->total_keys ;
+ i++,
+ delete_dynamic(&sinfo->buffpek),
+ close_cached_file(&sinfo->tempfile),
+ close_cached_file(&sinfo->tempfile_for_exceptions),
+ sinfo++)
+ {
+ if (got_error)
+ continue;
+ if (sinfo->keyinfo->flag && HA_VAR_LENGTH_KEY)
+ {
+ sinfo->write_keys=write_keys_varlen;
+ sinfo->read_to_buffer=read_to_buffer_varlen;
+ sinfo->write_key=write_merge_key_varlen;
+ }
+ else
+ {
+ sinfo->write_keys=write_keys;
+ sinfo->read_to_buffer=read_to_buffer;
+ sinfo->write_key=write_merge_key;
+ }
+ if (sinfo->buffpek.elements)
+ {
+ uint maxbuffer=sinfo->buffpek.elements-1;
+ if (!mergebuf)
+ {
+ length=param->sort_buffer_length;
+ while (length >= MIN_SORT_MEMORY && !mergebuf)
+ {
+ mergebuf=my_malloc(length, MYF(0));
+ length=length*3/4;
+ }
+ if (!mergebuf)
+ {
+ got_error=1;
+ continue;
+ }
+ }
+ keys=length/sinfo->key_length;
+ if (maxbuffer >= MERGEBUFF2)
+ {
+ if (param->testflag & T_VERBOSE)
+ printf("Key %d - Merging %u keys\n",sinfo->key+1, sinfo->keys);
+ if (merge_many_buff(sinfo, keys, (uchar **)mergebuf,
+ dynamic_element(&sinfo->buffpek, 0, BUFFPEK *),
+ (int*) &maxbuffer, &sinfo->tempfile))
+ {
+ got_error=1;
+ continue;
+ }
+ }
+ if (flush_io_cache(&sinfo->tempfile) ||
+ reinit_io_cache(&sinfo->tempfile,READ_CACHE,0L,0,0))
+ {
+ got_error=1;
+ continue;
+ }
+ if (param->testflag & T_VERBOSE)
+ printf("Key %d - Last merge and dumping keys\n", sinfo->key+1);
+ if (merge_index(sinfo, keys, (uchar **)mergebuf,
+ dynamic_element(&sinfo->buffpek,0,BUFFPEK *),
+ maxbuffer,&sinfo->tempfile) ||
+ flush_ft_buf(sinfo) ||
+ flush_pending_blocks(sinfo))
+ {
+ got_error=1;
+ continue;
+ }
+ }
+ if (my_b_inited(&sinfo->tempfile_for_exceptions))
+ {
+ uint key_length;
+
+ if (param->testflag & T_VERBOSE)
+ printf("Key %d - Dumping 'long' keys\n", sinfo->key+1);
+
+ if (flush_io_cache(&sinfo->tempfile_for_exceptions) ||
+ reinit_io_cache(&sinfo->tempfile_for_exceptions,READ_CACHE,0L,0,0))
+ {
+ got_error=1;
+ continue;
+ }
+
+ while (!got_error &&
+ !my_b_read(&sinfo->tempfile_for_exceptions,(byte*)&key_length,
+ sizeof(key_length)) &&
+ !my_b_read(&sinfo->tempfile_for_exceptions,(byte*)mergebuf,
+ (uint) key_length))
+ {
+ if (_mi_ck_write(info,sinfo->key,(uchar*) mergebuf,
+ key_length - info->s->rec_reflength))
+ got_error=1;
+ }
+ }
+ }
+ my_free((gptr) mergebuf,MYF(MY_ALLOW_ZERO_PTR));
+ return got_error;
+}
+#endif /* THREAD */
+
+ /* Write all keys in memory to file for later merge */
+
+static int NEAR_F write_keys(MI_SORT_PARAM *info, register uchar **sort_keys,
+ uint count, BUFFPEK *buffpek, IO_CACHE *tempfile)
+{
+ uchar **end;
+ uint sort_length=info->key_length;
+ DBUG_ENTER("write_keys");
+
+ qsort2((byte*) sort_keys,count,sizeof(byte*),(qsort2_cmp) info->key_cmp,
+ info);
+ if (!my_b_inited(tempfile) &&
+ open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST",
+ DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
+ DBUG_RETURN(1); /* purecov: inspected */
+
+ buffpek->file_pos=my_b_tell(tempfile);
+ buffpek->count=count;
+
+ for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
+ {
+ if (my_b_write(tempfile,(byte*) *sort_keys,(uint) sort_length))
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ DBUG_RETURN(0);
+} /* write_keys */
+
+
+static inline int
+my_var_write(MI_SORT_PARAM *info, IO_CACHE *to_file, byte *bufs)
+{
+ int err;
+ uint16 len = _mi_keylength(info->keyinfo, (uchar*) bufs);
+
+ /* The following is safe as this is a local file */
+ if ((err= my_b_write(to_file, (byte*)&len, sizeof(len))))
+ return (err);
+ if ((err= my_b_write(to_file,bufs, (uint) len)))
+ return (err);
+ return (0);
+}
+
+
+static int NEAR_F write_keys_varlen(MI_SORT_PARAM *info,
+ register uchar **sort_keys,
+ uint count, BUFFPEK *buffpek,
+ IO_CACHE *tempfile)
+{
+ uchar **end;
+ int err;
+ DBUG_ENTER("write_keys_varlen");
+
+ qsort2((byte*) sort_keys,count,sizeof(byte*),(qsort2_cmp) info->key_cmp,
+ info);
+ if (!my_b_inited(tempfile) &&
+ open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST",
+ DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
+ DBUG_RETURN(1); /* purecov: inspected */
+
+ buffpek->file_pos=my_b_tell(tempfile);
+ buffpek->count=count;
+ for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
+ {
+ if ((err= my_var_write(info,tempfile, (byte*) *sort_keys)))
+ DBUG_RETURN(err);
+ }
+ DBUG_RETURN(0);
+} /* write_keys_varlen */
+
+
+static int NEAR_F write_key(MI_SORT_PARAM *info, uchar *key,
+ IO_CACHE *tempfile)
+{
+ uint key_length=info->real_key_length;
+ DBUG_ENTER("write_key");
+
+ if (!my_b_inited(tempfile) &&
+ open_cached_file(tempfile, my_tmpdir(info->tmpdir), "ST",
+ DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
+ DBUG_RETURN(1);
+
+ if (my_b_write(tempfile,(byte*)&key_length,sizeof(key_length)) ||
+ my_b_write(tempfile,(byte*)key,(uint) key_length))
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+} /* write_key */
+
+
+/* Write index */
+
+static int NEAR_F write_index(MI_SORT_PARAM *info, register uchar **sort_keys,
+ register uint count)
+{
+ DBUG_ENTER("write_index");
+
+ qsort2((gptr) sort_keys,(size_t) count,sizeof(byte*),
+ (qsort2_cmp) info->key_cmp,info);
+ while (count--)
+ {
+ if ((*info->key_write)(info,*sort_keys++))
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ DBUG_RETURN(0);
+} /* write_index */
+
+
+ /* Merge buffers to make < MERGEBUFF2 buffers */
+
+static int NEAR_F merge_many_buff(MI_SORT_PARAM *info, uint keys,
+ uchar **sort_keys, BUFFPEK *buffpek,
+ int *maxbuffer, IO_CACHE *t_file)
+{
+ register int i;
+ IO_CACHE t_file2, *from_file, *to_file, *temp;
+ BUFFPEK *lastbuff;
+ DBUG_ENTER("merge_many_buff");
+
+ if (*maxbuffer < MERGEBUFF2)
+ DBUG_RETURN(0); /* purecov: inspected */
+ if (flush_io_cache(t_file) ||
+ open_cached_file(&t_file2,my_tmpdir(info->tmpdir),"ST",
+ DISK_BUFFER_SIZE, info->sort_info->param->myf_rw))
+ DBUG_RETURN(1); /* purecov: inspected */
+
+ from_file= t_file ; to_file= &t_file2;
+ while (*maxbuffer >= MERGEBUFF2)
+ {
+ reinit_io_cache(from_file,READ_CACHE,0L,0,0);
+ reinit_io_cache(to_file,WRITE_CACHE,0L,0,0);
+ lastbuff=buffpek;
+ for (i=0 ; i <= *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
+ {
+ if (merge_buffers(info,keys,from_file,to_file,sort_keys,lastbuff++,
+ buffpek+i,buffpek+i+MERGEBUFF-1))
+ break; /* purecov: inspected */
+ }
+ if (merge_buffers(info,keys,from_file,to_file,sort_keys,lastbuff++,
+ buffpek+i,buffpek+ *maxbuffer))
+ break; /* purecov: inspected */
+ if (flush_io_cache(to_file))
+ break; /* purecov: inspected */
+ temp=from_file; from_file=to_file; to_file=temp;
+ *maxbuffer= (int) (lastbuff-buffpek)-1;
+ }
+ close_cached_file(to_file); /* This holds old result */
+ if (to_file == t_file)
+ *t_file=t_file2; /* Copy result file */
+
+ DBUG_RETURN(*maxbuffer >= MERGEBUFF2); /* Return 1 if interrupted */
+} /* merge_many_buff */
+
+
+/*
+ Read data to buffer
+
+ SYNOPSIS
+ read_to_buffer()
+ fromfile File to read from
+ buffpek Where to read from
+ sort_length max length to read
+ RESULT
+ > 0 Ammount of bytes read
+ -1 Error
+*/
+
+static uint NEAR_F read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
+ uint sort_length)
+{
+ register uint count;
+ uint length;
+
+ if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
+ {
+ if (my_pread(fromfile->file,(byte*) buffpek->base,
+ (length= sort_length*count),buffpek->file_pos,MYF_RW))
+ return((uint) -1); /* purecov: inspected */
+ buffpek->key=buffpek->base;
+ buffpek->file_pos+= length; /* New filepos */
+ buffpek->count-= count;
+ buffpek->mem_count= count;
+ }
+ return (count*sort_length);
+} /* read_to_buffer */
+
+static uint NEAR_F read_to_buffer_varlen(IO_CACHE *fromfile, BUFFPEK *buffpek,
+ uint sort_length)
+{
+ register uint count;
+ uint16 length_of_key = 0;
+ uint idx;
+ uchar *buffp;
+
+ if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
+ {
+ buffp = buffpek->base;
+
+ for (idx=1;idx<=count;idx++)
+ {
+ if (my_pread(fromfile->file,(byte*)&length_of_key,sizeof(length_of_key),
+ buffpek->file_pos,MYF_RW))
+ return((uint) -1);
+ buffpek->file_pos+=sizeof(length_of_key);
+ if (my_pread(fromfile->file,(byte*) buffp,length_of_key,
+ buffpek->file_pos,MYF_RW))
+ return((uint) -1);
+ buffpek->file_pos+=length_of_key;
+ buffp = buffp + sort_length;
+ }
+ buffpek->key=buffpek->base;
+ buffpek->count-= count;
+ buffpek->mem_count= count;
+ }
+ return (count*sort_length);
+} /* read_to_buffer_varlen */
+
+
+static int NEAR_F write_merge_key_varlen(MI_SORT_PARAM *info,
+ IO_CACHE *to_file,char* key,
+ uint sort_length, uint count)
+{
+ uint idx;
+
+ char *bufs = key;
+ for (idx=1;idx<=count;idx++)
+ {
+ int err;
+ if ((err= my_var_write(info,to_file, (byte*) bufs)))
+ return (err);
+ bufs=bufs+sort_length;
+ }
+ return(0);
+}
+
+
+static int NEAR_F write_merge_key(MI_SORT_PARAM *info __attribute__((unused)),
+ IO_CACHE *to_file, char* key,
+ uint sort_length, uint count)
+{
+ return my_b_write(to_file,(byte*) key,(uint) sort_length*count);
+}
+
+/*
+ Merge buffers to one buffer
+ If to_file == 0 then use info->key_write
+*/
+
+static int NEAR_F
+merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
+ IO_CACHE *to_file, uchar **sort_keys, BUFFPEK *lastbuff,
+ BUFFPEK *Fb, BUFFPEK *Tb)
+{
+ int error;
+ uint sort_length,maxcount;
+ ha_rows count;
+ my_off_t to_start_filepos;
+ uchar *strpos;
+ BUFFPEK *buffpek,**refpek;
+ QUEUE queue;
+ volatile int *killed= killed_ptr(info->sort_info->param);
+
+ DBUG_ENTER("merge_buffers");
+
+ count=error=0;
+ maxcount=keys/((uint) (Tb-Fb) +1);
+ LINT_INIT(to_start_filepos);
+ if (to_file)
+ to_start_filepos=my_b_tell(to_file);
+ strpos=(uchar*) sort_keys;
+ sort_length=info->key_length;
+
+ if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0,
+ (int (*)(void*, byte *,byte*)) info->key_cmp,
+ (void*) info))
+ DBUG_RETURN(1); /* purecov: inspected */
+
+ for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
+ {
+ count+= buffpek->count;
+ buffpek->base= strpos;
+ buffpek->max_keys=maxcount;
+ strpos+= (uint) (error=(int) info->read_to_buffer(from_file,buffpek,
+ sort_length));
+ if (error == -1)
+ goto err; /* purecov: inspected */
+ queue_insert(&queue,(char*) buffpek);
+ }
+
+ while (queue.elements > 1)
+ {
+ for (;;)
+ {
+ if (*killed)
+ {
+ error=1; goto err;
+ }
+ buffpek=(BUFFPEK*) queue_top(&queue);
+ if (to_file)
+ {
+ if (info->write_key(info,to_file,(byte*) buffpek->key,
+ (uint) sort_length,1))
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ }
+ else
+ {
+ if ((*info->key_write)(info,(void*) buffpek->key))
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ }
+ buffpek->key+=sort_length;
+ if (! --buffpek->mem_count)
+ {
+ if (!(error=(int) info->read_to_buffer(from_file,buffpek,sort_length)))
+ {
+ uchar *base=buffpek->base;
+ uint max_keys=buffpek->max_keys;
+
+ VOID(queue_remove(&queue,0));
+
+ /* Put room used by buffer to use in other buffer */
+ for (refpek= (BUFFPEK**) &queue_top(&queue);
+ refpek <= (BUFFPEK**) &queue_end(&queue);
+ refpek++)
+ {
+ buffpek= *refpek;
+ if (buffpek->base+buffpek->max_keys*sort_length == base)
+ {
+ buffpek->max_keys+=max_keys;
+ break;
+ }
+ else if (base+max_keys*sort_length == buffpek->base)
+ {
+ buffpek->base=base;
+ buffpek->max_keys+=max_keys;
+ break;
+ }
+ }
+ break; /* One buffer have been removed */
+ }
+ }
+ else if (error == -1)
+ goto err; /* purecov: inspected */
+ queue_replaced(&queue); /* Top element has been replaced */
+ }
+ }
+ buffpek=(BUFFPEK*) queue_top(&queue);
+ buffpek->base=(uchar *) sort_keys;
+ buffpek->max_keys=keys;
+ do
+ {
+ if (to_file)
+ {
+ if (info->write_key(info,to_file,(byte*) buffpek->key,
+ sort_length,buffpek->mem_count))
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ }
+ else
+ {
+ register uchar *end;
+ strpos= buffpek->key;
+ for (end=strpos+buffpek->mem_count*sort_length;
+ strpos != end ;
+ strpos+=sort_length)
+ {
+ if ((*info->key_write)(info,(void*) strpos))
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ }
+ }
+ }
+ while ((error=(int) info->read_to_buffer(from_file,buffpek,sort_length)) != -1 &&
+ error != 0);
+
+ lastbuff->count=count;
+ if (to_file)
+ lastbuff->file_pos=to_start_filepos;
+err:
+ delete_queue(&queue);
+ DBUG_RETURN(error);
+} /* merge_buffers */
+
+
+ /* Do a merge to output-file (save only positions) */
+
+static int NEAR_F
+merge_index(MI_SORT_PARAM *info, uint keys, uchar **sort_keys,
+ BUFFPEK *buffpek, int maxbuffer, IO_CACHE *tempfile)
+{
+ DBUG_ENTER("merge_index");
+ if (merge_buffers(info,keys,tempfile,(IO_CACHE*) 0,sort_keys,buffpek,buffpek,
+ buffpek+maxbuffer))
+ DBUG_RETURN(1); /* purecov: inspected */
+ DBUG_RETURN(0);
+} /* merge_index */
+
+static int
+flush_ft_buf(MI_SORT_PARAM *info)
+{
+ int err=0;
+ if (info->sort_info->ft_buf)
+ {
+ err=sort_ft_buf_flush(info);
+ my_free((gptr)info->sort_info->ft_buf, MYF(0));
+ info->sort_info->ft_buf=0;
+ }
+ return err;
+}
+
diff --git a/storage/myisam/sp_defs.h b/storage/myisam/sp_defs.h
new file mode 100644
index 00000000000..4cc2267a1bd
--- /dev/null
+++ b/storage/myisam/sp_defs.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2000 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 _SP_DEFS_H
+#define _SP_DEFS_H
+
+#define SPDIMS 2
+#define SPTYPE HA_KEYTYPE_DOUBLE
+#define SPLEN 8
+
+#ifdef HAVE_SPATIAL
+
+enum wkbType
+{
+ wkbPoint = 1,
+ wkbLineString = 2,
+ wkbPolygon = 3,
+ wkbMultiPoint = 4,
+ wkbMultiLineString = 5,
+ wkbMultiPolygon = 6,
+ wkbGeometryCollection = 7
+};
+
+enum wkbByteOrder
+{
+ wkbXDR = 0, /* Big Endian */
+ wkbNDR = 1 /* Little Endian */
+};
+
+uint sp_make_key(register MI_INFO *info, uint keynr, uchar *key,
+ const byte *record, my_off_t filepos);
+
+#endif /*HAVE_SPATIAL*/
+#endif /* _SP_DEFS_H */
diff --git a/storage/myisam/sp_key.c b/storage/myisam/sp_key.c
new file mode 100644
index 00000000000..b61e8094cde
--- /dev/null
+++ b/storage/myisam/sp_key.c
@@ -0,0 +1,296 @@
+/* Copyright (C) 2000 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 "myisamdef.h"
+
+#ifdef HAVE_SPATIAL
+
+#include "sp_defs.h"
+
+static int sp_add_point_to_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order, double *mbr);
+static int sp_get_point_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order, double *mbr);
+static int sp_get_linestring_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order, double *mbr);
+static int sp_get_polygon_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order, double *mbr);
+static int sp_get_geometry_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ double *mbr, int top);
+static int sp_mbr_from_wkb(uchar (*wkb), uint size, uint n_dims, double *mbr);
+
+
+uint sp_make_key(register MI_INFO *info, uint keynr, uchar *key,
+ const byte *record, my_off_t filepos)
+{
+ HA_KEYSEG *keyseg;
+ MI_KEYDEF *keyinfo = &info->s->keyinfo[keynr];
+ uint len = 0;
+ byte *pos;
+ uint dlen;
+ uchar *dptr;
+ double mbr[SPDIMS * 2];
+ uint i;
+
+ keyseg = &keyinfo->seg[-1];
+ pos = (byte*)record + keyseg->start;
+
+ dlen = _mi_calc_blob_length(keyseg->bit_start, pos);
+ memcpy_fixed(&dptr, pos + keyseg->bit_start, sizeof(char*));
+ if (!dptr)
+ {
+ my_errno= HA_ERR_NULL_IN_SPATIAL;
+ return 0;
+ }
+ sp_mbr_from_wkb(dptr + 4, dlen - 4, SPDIMS, mbr); /* SRID */
+
+ for (i = 0, keyseg = keyinfo->seg; keyseg->type; keyseg++, i++)
+ {
+ uint length = keyseg->length;
+
+ pos = ((byte*)mbr) + keyseg->start;
+ if (keyseg->flag & HA_SWAP_KEY)
+ {
+#ifdef HAVE_ISNAN
+ if (keyseg->type == HA_KEYTYPE_FLOAT)
+ {
+ float nr;
+ float4get(nr, pos);
+ if (isnan(nr))
+ {
+ /* Replace NAN with zero */
+ bzero(key, length);
+ key+= length;
+ continue;
+ }
+ }
+ else if (keyseg->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;
+ }
+ }
+ else
+ {
+ memcpy((byte*)key, pos, length);
+ key += keyseg->length;
+ }
+ len += keyseg->length;
+ }
+ _mi_dpointer(info, key, filepos);
+ return len;
+}
+
+/*
+Calculate minimal bounding rectangle (mbr) of the spatial object
+stored in "well-known binary representation" (wkb) format.
+*/
+static int sp_mbr_from_wkb(uchar *wkb, uint size, uint n_dims, double *mbr)
+{
+ uint i;
+
+ for (i=0; i < n_dims; ++i)
+ {
+ mbr[i * 2] = DBL_MAX;
+ mbr[i * 2 + 1] = -DBL_MAX;
+ }
+
+ return sp_get_geometry_mbr(&wkb, wkb + size, n_dims, mbr, 1);
+}
+
+/*
+ Add one point stored in wkb to mbr
+*/
+
+static int sp_add_point_to_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order __attribute__((unused)),
+ double *mbr)
+{
+ double ord;
+ double *mbr_end= mbr + n_dims * 2;
+
+ while (mbr < mbr_end)
+ {
+ if ((*wkb) > end - 8)
+ return -1;
+ float8get(ord, (*wkb));
+ (*wkb)+= 8;
+ if (ord < *mbr)
+ float8store((char*) mbr, ord);
+ mbr++;
+ if (ord > *mbr)
+ float8store((char*) mbr, ord);
+ mbr++;
+ }
+ return 0;
+}
+
+
+static int sp_get_point_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order, double *mbr)
+{
+ return sp_add_point_to_mbr(wkb, end, n_dims, byte_order, mbr);
+}
+
+
+static int sp_get_linestring_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order, double *mbr)
+{
+ uint n_points;
+
+ n_points = uint4korr(*wkb);
+ (*wkb) += 4;
+ for (; n_points > 0; --n_points)
+ {
+ /* Add next point to mbr */
+ if (sp_add_point_to_mbr(wkb, end, n_dims, byte_order, mbr))
+ return -1;
+ }
+ return 0;
+}
+
+
+static int sp_get_polygon_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ uchar byte_order, double *mbr)
+{
+ uint n_linear_rings;
+ uint n_points;
+
+ n_linear_rings = uint4korr((*wkb));
+ (*wkb) += 4;
+
+ for (; n_linear_rings > 0; --n_linear_rings)
+ {
+ n_points = uint4korr((*wkb));
+ (*wkb) += 4;
+ for (; n_points > 0; --n_points)
+ {
+ /* Add next point to mbr */
+ if (sp_add_point_to_mbr(wkb, end, n_dims, byte_order, mbr))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int sp_get_geometry_mbr(uchar *(*wkb), uchar *end, uint n_dims,
+ double *mbr, int top)
+{
+ int res;
+ uchar byte_order;
+ uint wkb_type;
+
+ byte_order = *(*wkb);
+ ++(*wkb);
+
+ wkb_type = uint4korr((*wkb));
+ (*wkb) += 4;
+
+ switch ((enum wkbType) wkb_type)
+ {
+ case wkbPoint:
+ res = sp_get_point_mbr(wkb, end, n_dims, byte_order, mbr);
+ break;
+ case wkbLineString:
+ res = sp_get_linestring_mbr(wkb, end, n_dims, byte_order, mbr);
+ break;
+ case wkbPolygon:
+ res = sp_get_polygon_mbr(wkb, end, n_dims, byte_order, mbr);
+ break;
+ case wkbMultiPoint:
+ {
+ uint n_items;
+ n_items = uint4korr((*wkb));
+ (*wkb) += 4;
+ for (; n_items > 0; --n_items)
+ {
+ byte_order = *(*wkb);
+ ++(*wkb);
+ (*wkb) += 4;
+ if (sp_get_point_mbr(wkb, end, n_dims, byte_order, mbr))
+ return -1;
+ }
+ res = 0;
+ break;
+ }
+ case wkbMultiLineString:
+ {
+ uint n_items;
+ n_items = uint4korr((*wkb));
+ (*wkb) += 4;
+ for (; n_items > 0; --n_items)
+ {
+ byte_order = *(*wkb);
+ ++(*wkb);
+ (*wkb) += 4;
+ if (sp_get_linestring_mbr(wkb, end, n_dims, byte_order, mbr))
+ return -1;
+ }
+ res = 0;
+ break;
+ }
+ case wkbMultiPolygon:
+ {
+ uint n_items;
+ n_items = uint4korr((*wkb));
+ (*wkb) += 4;
+ for (; n_items > 0; --n_items)
+ {
+ byte_order = *(*wkb);
+ ++(*wkb);
+ (*wkb) += 4;
+ if (sp_get_polygon_mbr(wkb, end, n_dims, byte_order, mbr))
+ return -1;
+ }
+ res = 0;
+ break;
+ }
+ case wkbGeometryCollection:
+ {
+ uint n_items;
+
+ if (!top)
+ return -1;
+
+ n_items = uint4korr((*wkb));
+ (*wkb) += 4;
+ for (; n_items > 0; --n_items)
+ {
+ if (sp_get_geometry_mbr(wkb, end, n_dims, mbr, 0))
+ return -1;
+ }
+ res = 0;
+ break;
+ }
+ default:
+ res = -1;
+ }
+ return res;
+}
+
+#endif /*HAVE_SPATIAL*/
diff --git a/storage/myisam/sp_test.c b/storage/myisam/sp_test.c
new file mode 100644
index 00000000000..f0b48dbd5d8
--- /dev/null
+++ b/storage/myisam/sp_test.c
@@ -0,0 +1,565 @@
+/* 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 */
+
+/* Testing of the basic functions of a MyISAM spatial table */
+/* Written by Alex Barkov, who has a shared copyright to this code */
+
+#include "myisam.h"
+
+#ifdef HAVE_SPATIAL
+#include "sp_defs.h"
+
+#define MAX_REC_LENGTH 1024
+#define KEYALG HA_KEY_ALG_RTREE
+
+static void create_linestring(char *record,uint rownr);
+static void print_record(char * record,my_off_t offs,const char * tail);
+
+static void create_key(char *key,uint rownr);
+static void print_key(const char *key,const char * tail);
+
+static int run_test(const char *filename);
+static int read_with_pos(MI_INFO * file, int silent);
+
+static int rtree_CreateLineStringWKB(double *ords, uint n_dims, uint n_points,
+ uchar *wkb);
+static void rtree_PrintWKB(uchar *wkb, uint n_dims);
+
+static char blob_key[MAX_REC_LENGTH];
+
+
+int main(int argc __attribute__((unused)),char *argv[])
+{
+ MY_INIT(argv[0]);
+ exit(run_test("sp_test"));
+}
+
+
+int run_test(const char *filename)
+{
+ MI_INFO *file;
+ MI_UNIQUEDEF uniquedef;
+ MI_CREATE_INFO create_info;
+ MI_COLUMNDEF recinfo[20];
+ MI_KEYDEF keyinfo[20];
+ HA_KEYSEG keyseg[20];
+ key_range min_range, max_range;
+ int silent=0;
+ int create_flag=0;
+ int null_fields=0;
+ int nrecords=30;
+ int uniques=0;
+ int i;
+ int error;
+ int row_count=0;
+ char record[MAX_REC_LENGTH];
+ char key[MAX_REC_LENGTH];
+ char read_record[MAX_REC_LENGTH];
+ int upd=10;
+ ha_rows hrows;
+
+ /* Define a column for NULLs and DEL markers*/
+
+ recinfo[0].type=FIELD_NORMAL;
+ recinfo[0].length=1; /* For NULL bits */
+
+
+ /* Define spatial column */
+
+ recinfo[1].type=FIELD_BLOB;
+ recinfo[1].length=4 + mi_portable_sizeof_char_ptr;
+
+
+
+ /* Define a key with 1 spatial segment */
+
+ keyinfo[0].seg=keyseg;
+ keyinfo[0].keysegs=1;
+ keyinfo[0].flag=HA_SPATIAL;
+ keyinfo[0].key_alg=KEYALG;
+
+ keyinfo[0].seg[0].type= HA_KEYTYPE_BINARY;
+ keyinfo[0].seg[0].flag=0;
+ keyinfo[0].seg[0].start= 1;
+ keyinfo[0].seg[0].length=1; /* Spatial ignores it anyway */
+ keyinfo[0].seg[0].null_bit= null_fields ? 2 : 0;
+ keyinfo[0].seg[0].null_pos=0;
+ keyinfo[0].seg[0].language=default_charset_info->number;
+ keyinfo[0].seg[0].bit_start=4; /* Long BLOB */
+
+
+ if (!silent)
+ printf("- Creating isam-file\n");
+
+ bzero((char*) &create_info,sizeof(create_info));
+ create_info.max_rows=10000000;
+
+ if (mi_create(filename,
+ 1, /* keys */
+ keyinfo,
+ 2, /* columns */
+ recinfo,uniques,&uniquedef,&create_info,create_flag))
+ goto err;
+
+ if (!silent)
+ printf("- Open isam-file\n");
+
+ if (!(file=mi_open(filename,2,HA_OPEN_ABORT_IF_LOCKED)))
+ goto err;
+
+ if (!silent)
+ printf("- Writing key:s\n");
+
+ for (i=0; i<nrecords; i++ )
+ {
+ create_linestring(record,i);
+ error=mi_write(file,record);
+ print_record(record,mi_position(file),"\n");
+ if (!error)
+ {
+ row_count++;
+ }
+ else
+ {
+ printf("mi_write: %d\n", error);
+ goto err;
+ }
+ }
+
+ if ((error=read_with_pos(file,silent)))
+ goto err;
+
+ if (!silent)
+ printf("- Deleting rows with position\n");
+ for (i=0; i < nrecords/4; i++)
+ {
+ my_errno=0;
+ bzero((char*) read_record,MAX_REC_LENGTH);
+ error=mi_rrnd(file,read_record,i == 0 ? 0L : HA_OFFSET_ERROR);
+ if (error)
+ {
+ printf("pos: %2d mi_rrnd: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file),"\n");
+ error=mi_delete(file,read_record);
+ if (error)
+ {
+ printf("pos: %2d mi_delete: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ }
+
+ if (!silent)
+ printf("- Updating rows with position\n");
+ for (i=0; i < nrecords/2 ; i++)
+ {
+ my_errno=0;
+ bzero((char*) read_record,MAX_REC_LENGTH);
+ error=mi_rrnd(file,read_record,i == 0 ? 0L : HA_OFFSET_ERROR);
+ if (error)
+ {
+ if (error==HA_ERR_RECORD_DELETED)
+ continue;
+ printf("pos: %2d mi_rrnd: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file),"");
+ create_linestring(record,i+nrecords*upd);
+ printf("\t-> ");
+ print_record(record,mi_position(file),"\n");
+ error=mi_update(file,read_record,record);
+ if (error)
+ {
+ printf("pos: %2d mi_update: %3d errno: %3d\n",i,error,my_errno);
+ goto err;
+ }
+ }
+
+ if ((error=read_with_pos(file,silent)))
+ goto err;
+
+ if (!silent)
+ printf("- Test mi_rkey then a sequence of mi_rnext_same\n");
+
+ create_key(key, nrecords*4/5);
+ print_key(key," search for INTERSECT\n");
+
+ if ((error=mi_rkey(file,read_record,0,key,0,HA_READ_MBR_INTERSECT)))
+ {
+ printf("mi_rkey: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file)," mi_rkey\n");
+ row_count=1;
+
+ for (;;)
+ {
+ if ((error=mi_rnext_same(file,read_record)))
+ {
+ if (error==HA_ERR_END_OF_FILE)
+ break;
+ printf("mi_next: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file)," mi_rnext_same\n");
+ row_count++;
+ }
+ printf(" %d rows\n",row_count);
+
+ if (!silent)
+ printf("- Test mi_rfirst then a sequence of mi_rnext\n");
+
+ error=mi_rfirst(file,read_record,0);
+ if (error)
+ {
+ printf("mi_rfirst: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ row_count=1;
+ print_record(read_record,mi_position(file)," mi_frirst\n");
+
+ for(i=0;i<nrecords;i++) {
+ if ((error=mi_rnext(file,read_record,0)))
+ {
+ if (error==HA_ERR_END_OF_FILE)
+ break;
+ printf("mi_next: %3d errno: %3d\n",error,my_errno);
+ goto err;
+ }
+ print_record(read_record,mi_position(file)," mi_rnext\n");
+ row_count++;
+ }
+ printf(" %d rows\n",row_count);
+
+ if (!silent)
+ printf("- Test mi_records_in_range()\n");
+
+ create_key(key, nrecords*upd);
+ print_key(key," INTERSECT\n");
+ min_range.key= key;
+ min_range.length= 1000; /* Big enough */
+ min_range.flag= HA_READ_MBR_INTERSECT;
+ max_range.key= record+1;
+ max_range.length= 1000; /* Big enough */
+ max_range.flag= HA_READ_KEY_EXACT;
+ hrows= mi_records_in_range(file,0, &min_range, &max_range);
+ printf(" %ld rows\n", (long) hrows);
+
+ if (mi_close(file)) goto err;
+ my_end(MY_CHECK_ERROR);
+ return 0;
+
+err:
+ printf("got error: %3d when using myisam-database\n",my_errno);
+ return 1; /* skip warning */
+}
+
+
+static int read_with_pos (MI_INFO * file,int silent)
+{
+ int error;
+ int i;
+ char read_record[MAX_REC_LENGTH];
+ int rows=0;
+
+ if (!silent)
+ printf("- Reading rows with position\n");
+ for (i=0;;i++)
+ {
+ my_errno=0;
+ bzero((char*) read_record,MAX_REC_LENGTH);
+ error=mi_rrnd(file,read_record,i == 0 ? 0L : HA_OFFSET_ERROR);
+ if (error)
+ {
+ if (error==HA_ERR_END_OF_FILE)
+ break;
+ if (error==HA_ERR_RECORD_DELETED)
+ continue;
+ printf("pos: %2d mi_rrnd: %3d errno: %3d\n",i,error,my_errno);
+ return error;
+ }
+ rows++;
+ print_record(read_record,mi_position(file),"\n");
+ }
+ printf(" %d rows\n",rows);
+ return 0;
+}
+
+
+#ifdef NOT_USED
+static void bprint_record(char * record,
+ my_off_t offs __attribute__((unused)),
+ const char * tail)
+{
+ int i;
+ char * pos;
+ i=(unsigned char)record[0];
+ printf("%02X ",i);
+
+ for( pos=record+1, i=0; i<32; i++,pos++)
+ {
+ int b=(unsigned char)*pos;
+ printf("%02X",b);
+ }
+ printf("%s",tail);
+}
+#endif
+
+
+static void print_record(char * record, my_off_t offs,const char * tail)
+{
+ char *pos;
+ char *ptr;
+ uint len;
+
+ printf(" rec=(%d)",(unsigned char)record[0]);
+ pos=record+1;
+ len=sint4korr(pos);
+ pos+=4;
+ printf(" len=%d ",len);
+ memcpy_fixed(&ptr,pos,sizeof(char*));
+ if (ptr)
+ rtree_PrintWKB((uchar*) ptr,SPDIMS);
+ else
+ printf("<NULL> ");
+ printf(" offs=%ld ",(long int)offs);
+ printf("%s",tail);
+}
+
+
+#ifdef NOT_USED
+static void create_point(char *record,uint rownr)
+{
+ uint tmp;
+ char *ptr;
+ char *pos=record;
+ double x[200];
+ int i;
+
+ for(i=0;i<SPDIMS;i++)
+ x[i]=rownr;
+
+ bzero((char*) record,MAX_REC_LENGTH);
+ *pos=0x01; /* DEL marker */
+ pos++;
+
+ memset(blob_key,0,sizeof(blob_key));
+ tmp=rtree_CreatePointWKB(x,SPDIMS,blob_key);
+
+ int4store(pos,tmp);
+ pos+=4;
+
+ ptr=blob_key;
+ memcpy_fixed(pos,&ptr,sizeof(char*));
+}
+#endif
+
+
+static void create_linestring(char *record,uint rownr)
+{
+ uint tmp;
+ char *ptr;
+ char *pos=record;
+ double x[200];
+ int i,j;
+ int npoints=2;
+
+ for(j=0;j<npoints;j++)
+ for(i=0;i<SPDIMS;i++)
+ x[i+j*SPDIMS]=rownr*j;
+
+ bzero((char*) record,MAX_REC_LENGTH);
+ *pos=0x01; /* DEL marker */
+ pos++;
+
+ memset(blob_key,0,sizeof(blob_key));
+ tmp=rtree_CreateLineStringWKB(x,SPDIMS,npoints, (uchar*) blob_key);
+
+ int4store(pos,tmp);
+ pos+=4;
+
+ ptr=blob_key;
+ memcpy_fixed(pos,&ptr,sizeof(char*));
+}
+
+
+static void create_key(char *key,uint rownr)
+{
+ double c=rownr;
+ char *pos;
+ uint i;
+
+ bzero(key,MAX_REC_LENGTH);
+ for ( pos=key, i=0; i<2*SPDIMS; i++)
+ {
+ float8store(pos,c);
+ pos+=sizeof(c);
+ }
+}
+
+static void print_key(const char *key,const char * tail)
+{
+ double c;
+ uint i;
+
+ printf(" key=");
+ for (i=0; i<2*SPDIMS; i++)
+ {
+ float8get(c,key);
+ key+=sizeof(c);
+ printf("%.14g ",c);
+ }
+ printf("%s",tail);
+}
+
+
+#ifdef NOT_USED
+
+static int rtree_CreatePointWKB(double *ords, uint n_dims, uchar *wkb)
+{
+ uint i;
+
+ *wkb = wkbXDR;
+ ++wkb;
+ int4store(wkb, wkbPoint);
+ wkb += 4;
+
+ for (i=0; i < n_dims; ++i)
+ {
+ float8store(wkb, ords[i]);
+ wkb += 8;
+ }
+ return 5 + n_dims * 8;
+}
+#endif
+
+
+static int rtree_CreateLineStringWKB(double *ords, uint n_dims, uint n_points,
+ uchar *wkb)
+{
+ uint i;
+ uint n_ords = n_dims * n_points;
+
+ *wkb = wkbXDR;
+ ++wkb;
+ int4store(wkb, wkbLineString);
+ wkb += 4;
+ int4store(wkb, n_points);
+ wkb += 4;
+ for (i=0; i < n_ords; ++i)
+ {
+ float8store(wkb, ords[i]);
+ wkb += 8;
+ }
+ return 9 + n_points * n_dims * 8;
+}
+
+
+static void rtree_PrintWKB(uchar *wkb, uint n_dims)
+{
+ uint wkb_type;
+
+ ++wkb;
+ wkb_type = uint4korr(wkb);
+ wkb += 4;
+
+ switch ((enum wkbType)wkb_type)
+ {
+ case wkbPoint:
+ {
+ uint i;
+ double ord;
+
+ printf("POINT(");
+ for (i=0; i < n_dims; ++i)
+ {
+ float8get(ord, wkb);
+ wkb += 8;
+ printf("%.14g", ord);
+ if (i < n_dims - 1)
+ printf(" ");
+ else
+ printf(")");
+ }
+ break;
+ }
+ case wkbLineString:
+ {
+ uint p, i;
+ uint n_points;
+ double ord;
+
+ printf("LineString(");
+ n_points = uint4korr(wkb);
+ wkb += 4;
+ for (p=0; p < n_points; ++p)
+ {
+ for (i=0; i < n_dims; ++i)
+ {
+ float8get(ord, wkb);
+ wkb += 8;
+ printf("%.14g", ord);
+ if (i < n_dims - 1)
+ printf(" ");
+ }
+ if (p < n_points - 1)
+ printf(", ");
+ else
+ printf(")");
+ }
+ break;
+ }
+ case wkbPolygon:
+ {
+ printf("POLYGON(...)");
+ break;
+ }
+ case wkbMultiPoint:
+ {
+ printf("MULTIPOINT(...)");
+ break;
+ }
+ case wkbMultiLineString:
+ {
+ printf("MULTILINESTRING(...)");
+ break;
+ }
+ case wkbMultiPolygon:
+ {
+ printf("MULTIPOLYGON(...)");
+ break;
+ }
+ case wkbGeometryCollection:
+ {
+ printf("GEOMETRYCOLLECTION(...)");
+ break;
+ }
+ default:
+ {
+ printf("UNKNOWN GEOMETRY TYPE");
+ break;
+ }
+ }
+}
+
+#else
+int main(int argc __attribute__((unused)),char *argv[] __attribute__((unused)))
+{
+ exit(0);
+}
+#endif /*HAVE_SPATIAL*/
+
diff --git a/storage/myisam/test_pack b/storage/myisam/test_pack
new file mode 100755
index 00000000000..0cbeb57ba70
--- /dev/null
+++ b/storage/myisam/test_pack
@@ -0,0 +1,9 @@
+silent="-s"
+suffix=$MACH
+mi_test1$MACH -s ; myisampack$MACH --force -s test1 ; myisamchk$MACH -es test1 ; myisamchk$MACH -rqs test1 ; myisamchk$MACH -es test1 ; myisamchk$MACH -us test1 ; myisamchk$MACH -es test1
+mi_test1$MACH -s -S ; myisampack$MACH --force -s test1 ; myisamchk$MACH -es test1 ; myisamchk$MACH -rqs test1 ; myisamchk$MACH -es test1 ;myisamchk$MACH -us test1 ; myisamchk$MACH -es test1
+mi_test1$MACH -s -b ; myisampack$MACH --force -s test1 ; myisamchk$MACH -es test1 ; myisamchk$MACH -rqs test1 ; myisamchk$MACH -es test1
+mi_test1$MACH -s -w ; myisampack$MACH --force -s test1 ; myisamchk$MACH -es test1 ; myisamchk$MACH -ros test1 ; myisamchk$MACH -es test1
+
+mi_test2$MACH -s -t4 ; myisampack$MACH --force -s test2 ; myisamchk$MACH -es test2 ; myisamchk$MACH -ros test2 ; myisamchk$MACH -es test2 ; myisamchk$MACH -s -u test2 ; myisamchk$MACH -sm test2
+mi_test2$MACH -s -t4 -b -N ; myisampack$MACH --force -s test2 ; myisamchk$MACH -es test2 ; myisamchk$MACH -ros test2 ; myisamchk$MACH -es test2 ; myisamchk$MACH -s -u test2 ; myisamchk$MACH -sm test2